我有以下代码,我试图在cuda中实现,但我在cuda中将3D数组展平为1D的问题
C ++代码
for(int i=0; i<w; i++)
for(int j=0; j<h; j++)
for(int k=0; k<d; k++)
arr[h*w*i+ w*j+ k] = (h*w*i+ w*j+ k)*2;
这就是我目前在Cuda的作品
int w = h = d;
int N = 64;
__global__ void getIndex(float* A)
{
int i = blockIdx.x;
int j = blockIdx.y;
int k = blockIdx.z;
A[h*w*i+ w*j+ k] = h*w*i+ w*j+ k;
}
int main(int argc, char **argv)
{
float *d_A;
cudaMalloc((void **)&d_A, w * h * d * sizeof(float) );
getIndex <<<N,1>>> (d_A);
}
但是我没有得到我期待的结果,我不知道如何获得正确的i,j
和k
指数
答案 0 :(得分:2)
考虑尺寸为w
x h
x d
的3D问题。 (这可能是一个简单的数组,必须像您的问题或任何其他易于并行化的3D问题一样进行设置。)我将使用您的简单set-task进行演示。
使用CUDA内核处理此问题的最简单方法是为每个数组条目启动一个线程,即w*h*d
个线程。 This answer讨论了为什么每个元素的一个线程可能并不总是最佳解决方案。
现在让我们看看下面的代码行
dim3 numThreads(w,h,d);
getIndex <<<1, numThreads>>> (d_A, w, h, d);
这里我们发布一个总共有w*h*d
个线程的内核。
内核可以实现为
__global__ void getIndex(float* A, int w, int h, int d) // we actually do not need w
{
int i = threadIdx.x;
int j = threadIdx.y;
int k = threadIdx.z;
A[h*d*i+ d*j+ k] = h*d*i+ d*j+ k;
}
但是这个内核和内核调用存在一个问题:每个线程块的线程数是有限的(“特定方向的线程数”也是有界的= z方向通常是最有界的)。由于我们只调用一个线程块,因此我们的问题大小不能超过这些特定限制(例如w*h*d <= 1024
)。
这就是线程块的用途。您几乎可以根据需要启动具有多个线程的内核。 (事实并非如此,但最大线程数量的限制可能不会用尽。)
以这种方式调用内核:
dim3 numBlocks(w/8,h/8,d/8);
dim3 numThreads(8,8,8);
getIndex <<<numBlocks, numThreads>>> (d_A, w, h, d);
将为w/8 * h/8 * d/8
线程块启动内核,而每个块包含8*8*8
个线程。因此,将调用总共w*h*d
个线程。
现在我们必须相应地调整我们的内核:
__global__ void getIndex(float* A, int w, int h, int d) // we actually do not need w
{
int bx = blockIdx.x;
int by = blockIdx.y;
int bz = blockIdx.z;
int tx = threadIdx.x;
int ty = threadIdx.y;
int tz = threadIdx.z;
A[h*d*(8*bx + tx)+ d*(8*by + ty)+ (8*bz + tz)] = h*d*(8*bx + tx)+ d*(8*by + ty)+ (8*bz + tz);
}
注意:
blockDim.x
而不是固定大小8
和gridDim.x
来编写更通用的内核,以通过w
计算gridDim.x*blockDim.x
。其他两个维度也同样处理。w
,h
和d
必须是8的倍数。您还可以将内核概括为允许每个维度。 (然后你必须解析内核的所有三个维度,以检查计算的位置是否仍在问题的范围内。)