我想在项目中清理CUDA内核的参数。
现在,一个内核需要3个uint32_t
数组,这会导致非常难看的代码:(id表示全局线程id和valX是一些任意值)
__global__ void some_kernel(uint32_t * arr1, uint32_t * arr2, uint32_t * arr3){arr1[id] = val1; arr2[id] = val2; arr3[id] = val3;}
我想用结构来覆盖所有这些数组:
typedef struct S{uint_32_t arr1, uint_32_t arr2, uint_32_t arr3, uint32_t size} S;
其中size表示结构中每个arrX的长度。
我想拥有的是:
__global__ void some_kernel(S * s){s->arr1[id] = val1; s->arr2[id] = val2; s->arr3[id] = val3;}
对于像这样的结构,相应的cudaMalloc和cudaMemcpy会是什么样子? 这有什么性能缺点,我还没有看到吗?
提前致谢!
答案 0 :(得分:4)
您至少有两个选择。一个很好的选择是talonmies already given,但我会向你介绍“学习艰难的方法”。
首先,你的结构定义:
typedef struct S {
uint32_t *arr1;
uint32_t *arr2;
uint32_t *arr3;
uint32_t size;
} S;
...和内核定义(带有一些全局变量,但你不需要遵循该模式):
const int size = 10000;
__global__ void some_kernel(S *s)
{
int id = blockIdx.x * blockDim.x + threadIdx.x;
if (id < size)
{
s->arr1[id] = 1; // val1
s->arr2[id] = 2; // val2
s->arr3[id] = 3; // val3
}
}
请注意if
可以保护您免受超出限制。
接下来,我们来一些准备数据,执行内核并打印一些结果的函数。第一部分是数据分配:
uint32_t *host_arr1, *host_arr2, *host_arr3;
uint32_t *dev_arr1, *dev_arr2, *dev_arr3;
// Allocate and fill host data
host_arr1 = new uint32_t[size]();
host_arr2 = new uint32_t[size]();
host_arr3 = new uint32_t[size]();
// Allocate device data
cudaMalloc((void **) &dev_arr1, size * sizeof(*dev_arr1));
cudaMalloc((void **) &dev_arr2, size * sizeof(*dev_arr2));
cudaMalloc((void **) &dev_arr3, size * sizeof(*dev_arr3));
// Allocate helper struct on the device
S *dev_s;
cudaMalloc((void **) &dev_s, sizeof(*dev_s));
没什么特别的,你只需要分配三个数组和结构。更有趣的是如何处理将此类数据复制到设备中:
// Copy data from host to device
cudaMemcpy(dev_arr1, host_arr1, size * sizeof(*dev_arr1), cudaMemcpyHostToDevice);
cudaMemcpy(dev_arr2, host_arr2, size * sizeof(*dev_arr2), cudaMemcpyHostToDevice);
cudaMemcpy(dev_arr3, host_arr3, size * sizeof(*dev_arr3), cudaMemcpyHostToDevice);
// NOTE: Binding pointers with dev_s
cudaMemcpy(&(dev_s->arr1), &dev_arr1, sizeof(dev_s->arr1), cudaMemcpyHostToDevice);
cudaMemcpy(&(dev_s->arr2), &dev_arr2, sizeof(dev_s->arr2), cudaMemcpyHostToDevice);
cudaMemcpy(&(dev_s->arr3), &dev_arr3, sizeof(dev_s->arr3), cudaMemcpyHostToDevice);
除了您注意到的普通数组副本之外,还需要将它们与结构“绑定”在一起。为此,您需要传递指针的地址。结果,只复制了这些指针。
下一次内核调用,将数据再次复制回主机并打印结果:
// Call kernel
some_kernel<<<10000/256 + 1, 256>>>(dev_s); // block size need to be a multiply of 256
// Copy result to host:
cudaMemcpy(host_arr1, dev_arr1, size * sizeof(*host_arr1), cudaMemcpyDeviceToHost);
cudaMemcpy(host_arr2, dev_arr2, size * sizeof(*host_arr2), cudaMemcpyDeviceToHost);
cudaMemcpy(host_arr3, dev_arr3, size * sizeof(*host_arr3), cudaMemcpyDeviceToHost);
// Print some result
std::cout << host_arr1[size-1] << std::endl;
std::cout << host_arr2[size-1] << std::endl;
std::cout << host_arr3[size-1] << std::endl;
请记住,在任何严肃的代码中,您都应该始终检查来自CUDA API调用的错误。