ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[spring 并行5]GPU

2019-12-15 15:52:34  阅读:202  来源: 互联网

标签:spring memory 并行 线程 GPU gpu cpu pycuda


GPU篇 1

准备

  1. 需要有支持CUDA的Nvidia显卡
    linux查看显卡信息:lspci | grep -i vga
    使用nvidia显卡可以这样查看:lspci | grep -i nvidia
    上一个命令可以得到类似"03.00.0"的显卡代号,查看详细信息:lspic -v -s 03.00.0
    查看显卡使用情况(nvidia专用):nvidia-smi
    持续周期性输出使用情况(1秒1次):watch -n 1 nvidia-smi
  2. 需要安装pycuda(linux安装:apt install python3-pycuda)

基本使用

1.底层操作

  1. 准备工作: sudo apt install python3-pycuda
  2. 在cpu使用的内存中创建矩阵
  3. 将矩阵从cpu使用的内存移动到gpu的显存中
  4. 编辑c语言代码,让gpu计算
  5. 将矩阵从gpu显存移动到cpu使用的内存中

内核与线程层级
CUDA程序的一个最重要元素就是内核(kernel),它代表可以并行执行的代码
每个内核的执行均有叫做线程(thread)的计算单元完成,与cpu的线程不同,gpu线程更加轻量,上下文切换不会影响性能
为了确定运行一个内核所需的线程数机器逻辑组织形式,CUDA定义了一个二层结构。在最高一层,定义了所谓的区块网格(grid of blocks),这个网格代表了线程区块所在的二维结构,而这些线程区块则是三维的(简单来说,一个cuda结构包含多个blocks,每个blocks包含多个thread)(在下面还会对每个线程区块细分操作)

一个线程区块会被指派给一个流式多处理器(SM),然后这些线程被进一步划分为被称为warp的线程组,其大小有GPU的架构决定
为了充分发挥SM本身的并发性,同一组内的线程必须执行相同的指令,否则会出现线程分歧(divergence of thread)

示例:
用gpu将矩阵每个元素x2

import pycuda.driver as cuda
import pycuda.autoinit  # init GPU
from pycuda.compiler import SourceModule

import numpy as np

# 1.cpu create matrix
a = np.random.randn(5, 5)   # matrix:m*n
a = a.astype(np.float32)    # nvidia only support float calculate

# 2.move to gpu from cpu
a_gpu = cuda.mem_alloc(a.nbytes)    # alloc memory of gpu, this is 1 dim
cuda.memcpy_htod(a_gpu, a)      # copy cpu memory to gpu memory

# 3.gpu calculate
# create module of gpu calculate by c
mod = SourceModule('''
    __global__ void doubleMatrix(float *a)
    {
        int idx = threadIdx.x + threadIdx.y * 5;    // (x,y,z), gpu -> sm -> warp
        a[idx] *= 2;
    }
''')

func = mod.get_function('doubleMatrix') # get function from module
func(a_gpu, block = (5, 5, 1))      # set var and thread number from (x,y,z) orient to function

# 4.move to cpu from gpu
a_doubled = np.empty_like(a)        # create memory of cpu
cuda.memcpy_dtoh(a_doubled, a_gpu)  # copy gpu memory to cpu memory

print('original matrix')
print(a)
print('double matrix')
print(a_doubled)

注1import pycuda.autoinit 语句自动根据GPU可用性和数量选择要使用的GPU,这将创建一个接下来的代码运行中所需的GPU上下文(只需导入即可完成)

: astype(numpy.float32):将矩阵中的项转换为单精度模式,因为许多Nvidia显卡只支持单精度

注3:在调用gpu的c函数时,通过block参数设定,分配线程的方式,(5, 5, 1)是对应这gpu的(x, y, x)的分配
在c函数中,threadIdx是一个结构体,它有三个字段xyz,每个线程中的这个变量都不同(结合线程层级理解),故用此索引数组,由于动态分配的gpu内存是一维数组,所以需要在c函数内,使用threadIdx.y乘以矩阵每行的元素个数转换
更多详情:自行搜索cuda的线程区块划分

注4:gpu执行的c函数中 __global__ 关键字表示该函数是一个内核函数,必须从主机上调用才能在gpu设备上生成线程层级
涉及到pycuda内存,为了最大限度地利用可用资源,在支持CUDA的GPU显卡中,有4类内存:
寄存器(registers):每个线程将被分配一个寄存器,每个线程只能访问自身的寄存器,即使同属于一个线程区块
共享存储器(shared memory):在共享存储器中,每个线程区块都有一个其内部线程共享的内存,这部分内存速度极快
常数存储器(constant memory):一个网格中的所有线程一直都可以访问这部分内存,但只能在读取时访问。常数存储器中的数据在应用持续期间一直存在
全局存储器(global memory),:所有网格中的线程(也包含所有内核)都可访问全局存储器

更多详情:自行搜索PyCUDA内存模型

2.python封装控制

用gpuarray调用内核,它可以直接将数据保存在计算设备(gpu)中,并在该设备中进行计算

示例:
用gpu将矩阵每个元素x2

import pycuda.autoinit
import pycuda.gpuarray as gpuarray

import numpy as np

x = np.random.randn(5, 5)
x_gpu = gpuarray.to_gpu(x)

x_doubled = (2 * x_gpu).get()

print('x:')
print(x)
print('x_doubled:')
print(x_doubled)

其它

还有其它的库可以方便对cuda编程,如NumbaPro是一个Python编译器,提供了基于CUDA的API编程接口,可以编写CUDA持续,它专门设计用来执行与数组相关的计算任务,和广泛使用的numpy库类似
NumbaPro: 对GPU编程的库,提供许多的数值计算库,GPU加速库


1.参考书籍:《Python并行编程手册》

标签:spring,memory,并行,线程,GPU,gpu,cpu,pycuda
来源: https://www.cnblogs.com/maplesnow/p/12044372.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有