pycuda使用简介
开始使用环境 win10, visual studio 2019, pycuda 2019.02,在你使用PyCuda之前,要先用import命令来导入并初始化一下。import pycuda.driver as cudaimport pycuda.autoinitfrom pycuda.compiler import SourceModule这里要注意,你并不是必须使用 pycud...
开始使用
环境 win10, visual studio 2019, pycuda 2019.02,
在你使用PyCuda之前,要先用import命令来导入并初始化一下。
import pycuda.driver as cuda
import pycuda.autoinit
from pycuda.compiler import SourceModule
这里要注意,你并不是必须使用 pycuda.autoinit,如果你愿意的话,初始化、内容的创建和清理也都可以手动实现。
传输数据
接下来就是要把数据传输到设备(device)上了。一般情况下,在使用PyCuda的时候,你主要是传输主机host上的Numpy数组。(不过实际上,只要符合Python缓冲区接口的数据类型就都可以使用的,甚至连字符串类型str都可以。)下面这行示例代码创建了一个随机数组成的4*4大小的数组a:
import numpy
a = numpy.random.randn(4,4)
不过要先暂停一下—咱们刚刚创建的这个数组a包含的是双精度浮点数,但大多数常用的NVIDIA显卡只支持单精度浮点数,所以需要转换一下类型:
a = a.astype(numpy.float32)
接下来,要把已有的数据转移过去,还要设定一个目的地,所以我们要在显卡中分配一段显存:
a_gpu = cuda.mem_alloc(a.nbytes)
最后,咱们把刚刚生成的数组a转移到GPU里面吧:
cuda.memcpy_htod(a_gpu, a)
运行一个内核函数(kernel)
咱们这篇简介争取说的都是最简单的内容:咱们写一个代码来把a_gpu这段显存中存储的数组的每一个值都乘以2. 为了实现这个效果,我们就要写一段CUDA C代码,然后把这段代码提交给一个构造函数,这里用到了pycuda.compiler.SourceModule:
mod = SourceModule("""
__global__ void doublify(float *a)
{
int idx = threadIdx.x + threadIdx.y*4;
a[idx] *= 2;
}
""")
这一步如果没有出错,就说明这段代码已经编译成功,并且加载到显卡中。然后咱们可以使用pycuda.driver.Function
,然后调用此引用,把显存中的数组a_gpu作为参数传过去,同时设定块大小为4x4:
func = mod.get_function("doublify")
func(a_gpu, block=(4,4,1))
最后,咱们就把经过运算处理过的数据从GPU取回,并且将它和原始数组a一同显示出来对比一下:
a_doubled = numpy.empty_like(a)
cuda.memcpy_dtoh(a_doubled, a_gpu)
print (a_doubled)
print (a)
输出的效果大概就是如下所示:
[[ 0.51360393 1.40589952 2.25009012 3.02563429]
[-0.75841576 -1.18757617 2.72269917 3.12156057]
[ 0.28826082 -2.92448163 1.21624792 2.86353827]
[ 1.57651746 0.63500965 2.21570683 -0.44537592]]
[[ 0.25680196 0.70294976 1.12504506 1.51281714]
[-0.37920788 -0.59378809 1.36134958 1.56078029]
[ 0.14413041 -1.46224082 0.60812396 1.43176913]
[ 0.78825873 0.31750482 1.10785341 -0.22268796]]
出现上面这样输出就说明成功了!整个攻略就完成了。另外很值得庆幸的是,运行输出之后PyCuda就会把所有清理和内存回收工作做好了,咱们的简介也就完毕了。不过你可以再看一下接下来的内容,里面有一些有意思的东西。
简化内存拷贝
PyCuda提供了pycuda.driver.In, pycuda.driver.Out, 以及pycuda.driver.InOut 这三个参数处理器(argument handlers),能用来简化内存和显存之间的数据拷贝。例如,咱们可以不去创建一个a_gpu,而是直接把a移动过去,下面的代码就可以实现:
func(cuda.InOut(a), block=(4, 4, 1))
有准备地调用函数
使用内置的pycuda.driver.Function.call() 方法来进行的函数调用,会增加类型识别的资源开销(参考显卡接口)。 要实现跟上面代码同样的效果,又不造成这种开销,这个函数就需要设定好参数类型(如Python的标准库中的结构体模块struct所示),然后再去调用该函数。这样也就不用需要再使用numpy.number类去制定参数的规模了:
grid = (1, 1)
block = (4, 4, 1)
func.prepare("P")
func.prepared_call(grid, block, a_gpu)
馈赠:抽象以降低复杂度
使用 pycuda.gpuarray.GPUArray,同样效果的代码实现起来就更加精简了:
import pycuda.gpuarray as gpuarray
import pycuda.driver as cuda
import pycuda.autoinit
import numpy
a_gpu = gpuarray.to_gpu(numpy.random.randn(4,4).astype(numpy.float32))
a_doubled = (2*a_gpu).get()
print a_doubled
print a_gpu
参考链接:https://www.jianshu.com/p/85b536311f9f

欢迎来到由智源人工智能研究院发起的Triton中文社区,这里是一个汇聚了AI开发者、数据科学家、机器学习爱好者以及业界专家的活力平台。我们致力于成为业内领先的Triton技术交流与应用分享的殿堂,为推动人工智能技术的普及与深化应用贡献力量。
更多推荐
所有评论(0)