pycuda使用简介

开始使用

环境 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

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值