1

我已经读过很多次关于 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;
}

(我已经评论了所有不重要的行,只是让行调用函数转置)

我已经了解函数 main 中的所有行,除了调用函数的行tranpose。当我说:当我们调用函数时transpose<<<gDim, bDim>>>(Md, Bd, WIDTH),CUDA 会自动将数组的每个元素分配到一个线程(和块)中,当我们调用“一次性”转置时,CUDA 将在线程上运行gDim * bDim时间转置。gDim * bDim

这一点让我感到非常沮丧,因为它不喜欢java中的多线程,当我使用:(请告诉我。

谢谢 :)

4

1 回答 1

5

你的理解本质上是正确的。

transpose不是一个函数,而是一个 CUDA 内核。当您调用常规函数时,它只运行一次。但是当你单次启动内核时,CUDA 会自动多次运行内核中的代码。CUDA 通过启动许多线程来做到这一点。每个线程运行一次内核中的代码。三方括号 ( <<< >>>) 内的数字称为内核执行配置。它决定了 CUDA 将启动多少线程并指定线程之间的一些关系。

将启动的线程数是通过将网格中的所有值和三重括号内的块尺寸相乘来计算的。例如,在您的示例中,线程数将为 1,048,576 (16 * 16 * 64 * 64)。

每个线程都可以读取一些变量来找出它是哪个线程。这些是内核顶部的blockIdxand结构。threadIdx这些值反映了内核执行配置中的值。因此,如果您使用 16 x 16 的网格配置运行内核(dim3三方括号中的第一个,您将获得线程,当它们每个读取结构中的xy值时blockIdx,将获得0xy15.

因此,如您所见,CUDA 对数组元素或任何其他特定于您的内核的数据结构一无所知。它只处理线程、线程索引和块索引。然后,您使用这些索引来确定给定线程应该做什么(特别是,它应该处理应用程序特定数据中的哪些值)。

于 2012-12-12T04:02:13.777 回答