在python上玩CUDA - Numba 与 Pycuda 的比较

1 篇文章 0 订阅
1 篇文章 0 订阅

python上的CUDA已经广泛应用在TensorFlow,PyTorch等库中,但当我们想用GPU计算资源实现其他的算法时,不得不自己调用CUDA的python接口完成编程,以下是我在python上,利用GPU完成高斯过程计算的经验。

 

【文首劝退】如果是想用CUDA完成较复杂的功能和算法,还是用C++实现吧。。。python的话我感觉很多已定义好的库无法正常调用,虽然numpy的很多属性和功能仍被支持,但如scikit-learn等库无法正常调用,而且坑比较多(错误提示不明显且有关错误和document网络上资料较少),对新手不友好。本人已有一定的C++开发CUDA的经验,但还是费了不少劲在python+CUDA上。

 

实现平台:

window10

python3.7

CUDA10.0(一开始我笔记本安装的是CUDA8.0,因为在尝试pycuda时找不到8.0对应的pycuda版本,后面卸载并安装为10.0。其实后面找到简单的下载pycuda旧版本的方法,8.0还是可以用的)

Pycuda 2018.1.1

一、概述

基于大家都安装好CUDA的前提基础上,在python上使用cuda编程有两种途径:基于Numba 和 基于pycuda(及skcuda)。总体而言,A. Numba编程较容易,且有较详细的document (https://numba.pydata.org/numba-doc/dev/cuda/overview.html),入门,实现一些简单功能比较容易。但是编程过程中我发现了一些问题。1)不知是不是版本原因,document上的例子没法在我的平台上跑的通(如无法识别python的cmath库,但还是可以识别math库) 2)有些内存管理的api没法正常运行如和local memory相关的(可能只是我菜,没搞明白怎样用)3)可能因为没有显式的分配GPU内存,只有显式地传输,当传入的数组过大时,在memory copy from device to host时有报错的可能性  B. Pycuda编程比Numba复杂,且pycuda 和 skcuda并不是anaconda自带的库,需要额外安装平台对应的版本。但pycuda有gpu相应的库函数,能用gpu完成简单线性代数,基本数学运算,且能结合C代码完成复杂功能,在数据量大的时候加速性能比Numba要好,因此当所需gpu计算编程比较复杂时,建议使用pycuda。pycuda的初步教程可以参考https://zhuanlan.zhihu.com/p/32062796

 

二、Numba的坑

1. 计算过程需要用到的数组需要传入gpu中, 但float,int可以直接传进gpu,而无需api。

第一个红框就是把X1等numpy数组传入gpu,第二个红框指定gpu计算过程的block,thread个数,最后一个红框用于把结果传回来cpu。

2.Numba装饰的代码不能使用numpy的方法或属性

3.Numba装饰的代码不能使用numpy的向量化计算,必须把数组分开成一个个元素这样来计算

@cuda.jit
def gaussian_kernel(x1, x2, m, n, dim, dist_matrix, result, l=1.0, sigma_f=1.0):
    tidx = cuda.threadIdx.x
    bidx = cuda.blockIdx.x

    for i in range(bidx, m, cuda.gridDim.x):
        for j in range(n):
            for k in range(tidx, dim, cuda.blockDim.x):
                dist_matrix[i][j] += (x1[i][k] - x2[j][k]) ** 2
            result[i][j] = sigma_f ** 2 * math.exp(-0.5 * dist_matrix[i][j] / l ** 2)

4. Numba装饰的代码不允许递归调用方法本身。(这对很多算法而言不是太友好)

5. 使用Numba时可能会遇到关于nvvm的报错,这里提供一个解决方法,把下面两个环境变量添加到pycharm的环境变量中(如图)(注意文件路径要根据自己电脑的路径进行修改,且要提前安装好CUDA(上Nvidia官网下载CUDA及显卡对应的驱动版本即可))

NUMBAPRO_NVVM=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\nvvm\bin\nvvm64_33_0.dll;

NUMBAPRO_LIBDEVICE=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\nvvm\libdevice

总的来看,Numba的使用还是比较简单的,打好一个方法,用Numba装饰器修饰它,然后在其他地方传好数组到gpu中就可以调用了。

 

三、Pycuda的坑

1.使用pycuda和skcuda的第一步就是安装这两个库(毕竟不是anaconda自带的)。但是网上搜pycuda的官网或document,有好几个网站都没有提供不同版本的安装包,直到找到这个网址:https://pypi.org/project/pycuda/#history。这样你就可以找版本对应的来安装。其实还有一个简单的方法就是,在pycharm的terminal窗口,用

pip install pycuda == 2018.1.1

也是可以安装的,但要先更新pip到较新版本。

scikit-cuda的安装及document可以参考它的官网:https://scikit-cuda.readthedocs.io/en/latest/install.html。 这里我没遇到什么坑。

库的版本,CUDA版本,显卡驱动版本这几样如果没对应好,就会报“ ... is not supported on this platform" 错误。

2.下面就是把相关的库import到代码中啦,以下几个库基本是必须要用到的,最后一个skcuda库是线性代数的,里面有编写好的GPU矩阵求逆的方法。高斯过程正好要求逆矩阵。

import pycuda.autoinit
import pycuda.gpuarray as gpuarray
import pycuda.driver as dry
from pycuda.compiler import SourceModule
import skcuda.linalg as sklin

若试运行时有很多warning,还可以用

import warnings
warnings.filterwarnings('ignore')

这个来屏蔽那些warning。。。瞬间世界干净了。

3.值得注意的是,pycuda和Numba不能同时用,因为pycuda需要autoinit来初始化gpu context,但这会导致numba认为gpu is non-primary context(就是被人占用了),所以报错。

4.按照网上已有的教程(譬如:https://zhuanlan.zhihu.com/p/32062796)编写好pycuda的代码。同样有以下几步:

a) 先对gpu分配内存

b) 把numpy数组传入gpu

c) 编写kernel方法(就是mod = SourceModule (""" .... """) 这块)

d) 把gpu数组作为参数,调用kernel方法

e) 从device中把结果拿回来

下面就是踩坑填坑时间

5.首先你得确保kernel方法中的c代码语法等正确无误,因为在SourceModule方法中,它以字符串的形式存在,所以debug主要靠经验(当代码量大的时候。。。笑而不语)。基本上,很多报错都是kernel方法的c代码没写好(很多时候的错误报告都会提到

pycuda.driver.CompileError: nvcc preprocessing failed)。有趣的是,错误报告中提及的.cu文件,原来是python自己在一个文件夹中根据c代码生成了一个临时文件,我的电脑的位置是在C:\Users\HP\AppData\Local\Temp里面,大家有兴趣可以看看。(毕竟在VS里面看代码要比字符串要看得舒服

网上有些资料说,这样的processing错误是由于缺少cl.exe文件的系统变量导致,可以把如

C:\Program Files (x86)\VC\bin;

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\lib;

的路径添加到系统的PATH变量中(前提是你要安装好VS的某个版本),然后重启pycharm,并把

这个勾给勾上。

6.当kernel方法没有错误后,还要规范好传入参数的数据类型。如下面三条指令

X1.astype(np.float32)
l = np.float32(self.params['l'])
m32 = np.int32(m)

其中X1是numpy数组(可以搭配driver.In(X1), 变成GPU数组,这一点在很多教程中都有提及),self.params['l']是float, m是int (得到的 l 和 m32 才是我们传入kernel方法的变量,因为很多教程都没提float, int 变量怎样直接传入kernel, 我这里列出来以作补充)

 

这样基本上就没什么问题啦~(只是我没有碰到更多的问题而已啦)

想看高斯过程  gpu CUDA简单实现的同学可以访问我的github,Zhixing1020。

 

 

 

  • 7
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值