为此,您需要向 CUDA 内核描述内存中数组的布局,并且您需要使用主机端提供的步幅在内核中进行正确的索引计算。一个简单的方法是在 CUDA 中定义一个小的帮助类,它隐藏了大部分索引并提供了一个简单的索引语法。例如:
from pycuda import driver, gpuarray
from pycuda.compiler import SourceModule
import pycuda.autoinit
import numpy as np
mod=SourceModule("""
struct stride3D
{
float* p;
int s0, s1;
__device__
stride3D(float* _p, int _s0, int _s1) : p(_p), s0(_s0), s1(_s1) {};
__device__
float operator () (int x, int y, int z) const { return p[x*s0 + y*s1 + z]; };
__device__
float& operator () (int x, int y, int z) { return p[x*s0 + y*s1 + z]; };
};
__global__ void mat_ops(float *A, int sA0, int sA1, float *B, int sB0, int sB1)
{
stride3D A3D(A, sA0, sA1);
stride3D B3D(B, sB0, sB1);
int xidx = blockIdx.x;
int yidx = threadIdx.x;
int zidx = threadIdx.y;
B3D(xidx, yidx, zidx) = A3D(xidx, yidx, zidx);
}
""")
A = 1 + np.arange(0, 4*4*3, dtype=np.float32).reshape(4,4,3)
B = np.zeros((5,5,5), dtype=np.float32)
A_k = gpuarray.to_gpu(A)
B_k = gpuarray.to_gpu(B)
astrides = np.array(A.strides, dtype=np.int32) // A.itemsize
bstrides = np.array(B.strides, dtype=np.int32) // B.itemsize
func = mod.get_function("mat_ops")
func(A_k, astrides[0], astrides[1], B_k, bstrides[0], bstrides[1], grid=(4,1,1),block=(4,3,1))
print(B_k[:4,:4,:3])
这里我选择使源数组和目标数组大小不同,只是为了表明代码是通用的,只要块大小足够,它就可以适用于任何大小的数组。请注意,这里没有在设备代码方面检查数组边界,您需要为非平凡的示例添加它。
还请注意,这对于 fortran 和 C 有序 numpy 数组都应该正确工作,因为它直接使用 numpy 步幅值。但是,由于内存合并问题,CUDA 方面的性能会受到影响。
注意:如果不扩展帮助程序类以在所有维度上跨步并更改内核以接受输入和输出数组的所有维度的跨步,这将不适用于 fortran 和 C 排序。从性能的角度来看,最好为 fortran 和 C 有序数组编写单独的辅助类。