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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章