我已多次阅读有关CUDA线程/块和数组的内容,但仍然不明白点:CUDA如何以及何时开始为内核函数运行多线程。当主机调用内核函数或内核函数时。
例如我有这个例子,它只是简单地转置一个数组。 (所以,它只是将值从这个数组复制到另一个数组)。
__global__
void transpose(float* in, float* out, uint width) {
uint tx = blockIdx.x * blockDim.x + threadIdx.x;
uint ty = blockIdx.y * blockDim.y + threadIdx.y;
out[tx * width + ty] = in[ty * width + tx];
}
int main(int args, char** vargs) {
/*const int HEIGHT = 1024;
const int WIDTH = 1024;
const int SIZE = WIDTH * HEIGHT * sizeof(float);
dim3 bDim(16, 16);
dim3 gDim(WIDTH / bDim.x, HEIGHT / bDim.y);
float* M = (float*)malloc(SIZE);
for (int i = 0; i < HEIGHT * WIDTH; i++) { M[i] = i; }
float* Md = NULL;
cudaMalloc((void**)&Md, SIZE);
cudaMemcpy(Md,M, SIZE, cudaMemcpyHostToDevice);
float* Bd = NULL;
cudaMalloc((void**)&Bd, SIZE); */
transpose<<<gDim, bDim>>>(Md, Bd, WIDTH); // CALLING FUNCTION TRANSPOSE
cudaMemcpy(M,Bd, SIZE, cudaMemcpyDeviceToHost);
return 0;
}
(我已经评论了所有不重要的行,只是让行调用函数转置)
除了行调用函数tranpose
之外,我已经理解了函数main中的所有行。当我说:当我们调用函数transpose<<<gDim, bDim>>>(Md, Bd, WIDTH)
时,CUDA会自动将数组的每个元素分配到一个线程(和块)中,当我们调用“一次”转换时,CUDA将运行{{1} }次转发gDim * bDim
个线程。
这一点让我感到非常沮丧,因为它在java中不喜欢多线程,当我使用:(请告诉我。
谢谢:)
答案 0 :(得分:5)
你的理解本质上是正确的。
transpose
不是函数,而是CUDA内核。当您调用常规函数时,它只运行一次。但是当你一次启动内核时,CUDA会自动多次在内核中运行代码。 CUDA通过启动许多线程来做到这一点。每个线程一次运行内核中的代码。 tripple括号内的数字(<<< >>>
)称为内核执行配置。它确定CUDA将启动多少个线程,并指定线程之间的某些关系。
将通过将网格中的所有值与三个括号内的块尺寸相乘来计算将要启动的线程数。例如,在您的示例中,线程数将为1,048,576(16 * 16 * 64 * 64)。
每个线程都可以读取一些变量来找出它是哪个线程。这些是内核顶部的blockIdx
和threadIdx
结构。值反映了内核执行配置中的值。因此,如果您使用16 x 16的网格配置(三个括号中的第一个dim3
)运行内核,那么当它们分别读取x
和y
时,您将获得线程blockIdx
结构中的值将获得0到15之间x
和y
的所有可能组合。
因此,如您所见,CUDA对数组元素或任何其他特定于您的内核的数据结构一无所知。它只处理线程,线程索引和块索引。然后,您可以使用这些索引来确定给定线程应该执行的操作(特别是应该处理的应用程序特定数据中的哪些值)。