PyCUDAプログラミング学習にはCUDA Cプログラミング学習が必須のようだ。GPUプログラミングは敷居が高そうだが、面白そうではある。
スポンサーリンク
昨日のコードのおさらい¶
from pycuda.tools import make_default_context
c = make_default_context()
d = c.get_device()
d.name()
import pycuda.driver as cuda
import pycuda.autoinit
from pycuda.compiler import SourceModule
import numpy
a = numpy.random.randn(4,4) # 4x4の乱数アレイを作る
a = a.astype(numpy.float32) # 倍精度数を単精度数に変換
a_gpu = cuda.mem_alloc(a.nbytes) # 転送先GPUにメモリを割り当てる
cuda.memcpy_htod(a_gpu, a) # データをGPUメモリに転送する
a.dtype # aのデータタイプを確認する。
a_gpu
# 入力データを2倍にするカーネル関数をCUDA Cで作成
mod = SourceModule("""
__global__ void doublify(float *a)
{
int idx = threadIdx.x + threadIdx.y*4;
a[idx] *= 2;
}
""")
func = mod.get_function("doublify")
func(a_gpu, block=(4,4,1))
a_doubled = numpy.empty_like(a)
cuda.memcpy_dtoh(a_doubled, a_gpu)
print (a) # 単精度(float32)乱数の入力データ
print (a_doubled) # input dataを2倍にした値
スポンサーリンク
より簡潔なPyCUDAコーディング¶
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)
こんなに簡潔なコードに書き換えられることにびっくりした。こっちの方がHello world!プログラムにふさわしいように思う。
スポンサーリンク
Advanced Topics(アドバンスド トピックス)¶
Structures (ストラクチャーズ)¶
mod = SourceModule("""
struct DoubleOperation {
int datalen, __padding; // so 64-bit ptrs can be aligned
float *ptr;
};
__global__ void double_array(DoubleOperation *a) {
a = &a[blockIdx.x];
for (int idx = threadIdx.x; idx < a->datalen; idx += blockDim.x) {
a->ptr[idx] *= 2;
}
}
""")
class DoubleOpStruct:
mem_size = 8 + numpy.intp(0).nbytes
def __init__(self, array, struct_arr_ptr):
self.data = cuda.to_device(array)
self.shape, self.dtype = array.shape, array.dtype
cuda.memcpy_htod(int(struct_arr_ptr), numpy.getbuffer(numpy.int32(array.size)))
cuda.memcpy_htod(int(struct_arr_ptr) + 8, numpy.getbuffer(numpy.intp(int(self.data))))
def __str__(self):
return str(cuda.from_device(self.data, self.shape, self.dtype))
struct_arr = cuda.mem_alloc(2 * DoubleOpStruct.mem_size)
do2_ptr = int(struct_arr) + DoubleOpStruct.mem_size
array1 = DoubleOpStruct(numpy.array([1, 2, 3], dtype=numpy.float32), struct_arr)
array2 = DoubleOpStruct(numpy.array([0, 4], dtype=numpy.float32), do2_ptr)
print("original arrays", array1, array2)
numpy.getbufferはpython3でmemoryviewに取って代わられたらしい。
class DoubleOpStruct:
mem_size = 8 + numpy.intp(0).nbytes
def __init__(self, array, struct_arr_ptr):
self.data = cuda.to_device(array)
self.shape, self.dtype = array.shape, array.dtype
cuda.memcpy_htod(int(struct_arr_ptr), memoryview(numpy.int32(array.size)))
cuda.memcpy_htod(int(struct_arr_ptr) + 8, memoryview(numpy.intp(int(self.data))))
def __str__(self):
return str(cuda.from_device(self.data, self.shape, self.dtype))
struct_arr = cuda.mem_alloc(2 * DoubleOpStruct.mem_size)
do2_ptr = int(struct_arr) + DoubleOpStruct.mem_size
array1 = DoubleOpStruct(numpy.array([1, 2, 3], dtype=numpy.float32), struct_arr)
array2 = DoubleOpStruct(numpy.array([0, 4], dtype=numpy.float32), do2_ptr)
print("original arrays", array1, array2)
func = mod.get_function("double_array")
func(struct_arr, block = (32, 1, 1), grid=(2, 1))
print("doubled arrays", array1, array2)
func(numpy.intp(do2_ptr), block = (32, 1, 1), grid=(1, 1))
print("doubled second only", array1, array2, "\n")
チュートリアルはここで終わってしまっている。後は、pycuda githubのサンプルコードやテストコードを見て学習するように推奨している。
スポンサーリンク
スポンサーリンク