36

我是 CUDA 的新手,我无法理解循环展开。我写了一段代码来理解这项技术

__global__ void kernel(float *b, int size)
{
    int tid = blockDim.x * blockIdx.x + threadIdx.x;
 #pragma unroll
    for(int i=0;i<size;i++)
        b[i]=i;
}

以上是我的核函数。在main我这样称呼它

int main()
{
    float * a; //host array
    float * b; //device array
    int size=100;

    a=(float*)malloc(size*sizeof(float));
    cudaMalloc((float**)&b,size);
    cudaMemcpy(b, a, size, cudaMemcpyHostToDevice);

    kernel<<<1,size>>>(b,size); //size=100

    cudaMemcpy(a, b, size, cudaMemcpyDeviceToHost);

    for(int i=0;i<size;i++)
        cout<<a[i]<<"\t";

    _getch();

    return 0;
}

这是否意味着我有size* size=10000 个线程正在运行来执行程序?展开循环时是否创建了 100 个?

4

1 回答 1

50

不,这意味着你调用了一个带有一个块的 CUDA 内核,而那个块有 100 个活动线程。您将 size 作为第二个函数参数传递给内核。在您的内核中,这 100 个线程中的每一个都执行 for 循环 100 次。

#pragma unroll是一种编译器优化,例如,可以替换一段代码,例如

for ( int i = 0; i < 5; i++ )
    b[i] = i;

b[0] = 0;
b[1] = 1;
b[2] = 2;
b[3] = 3;
b[4] = 4;

#pragma unroll通过在循环之前放置指令。展开版本的好处是它涉及较少的处理器处理负载。在for循环版本的情况下,除了将每个分配给 之外,处理还i包括b[i]初始化i、评估i<56 次和递增i5 次。而在第二种情况下,它只涉及归档b数组内容(可能加上int i=5;ifi稍后使用)。循环展开的另一个好处是增强了指令级并行性 (ILP)。在展开的版本中,处理器可能会有更多操作推入处理管道,而不必担心for每次迭代中的循环条件。

像这样的帖子解释了 CUDA 不会发生运行时循环展开。在您的情况下,CUDA 编译器没有任何线索size将是 100,因此不会发生编译时循环展开,因此如果您强制展开,最终可能会损害性能。

如果您确定size所有执行的值为 100,则可以展开循环,如下所示:

#pragma unroll
for(int i=0;i<SIZE;i++)  //or simply for(int i=0;i<100;i++)
    b[i]=i;

其中SIZE在编译时与#define SIZE 100.

我还建议您在代码中进行适当的 CUDA 错误检查(在此处解释)。

于 2014-03-09T06:59:35.193 回答