代码之家  ›  专栏  ›  技术社区  ›  Magzhan Abdibayev

#pragma unroll具体做什么?它会影响线程数吗?

  •  44
  • Magzhan Abdibayev  · 技术社区  · 11 年前

    我是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 * 大小 =10000个线程运行以执行程序?当循环展开时,是否创建了其中的100个?

    1 回复  |  直到 7 年前
        1
  •  57
  •   Community CDub    7 年前

    不,这意味着你调用了一个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展开 指令就在循环之前。展开版本的好处在于它减少了处理器的处理负载。万一 for 循环版本,除了分配每个 i b[i] ,涉及 初始化,评估 i<5 6次,并递增 5次。而在第二种情况下,它只涉及备案 b 阵列内容(可能是加号 int i=5; 如果 稍后使用)。循环展开的另一个好处是增强了指令级并行性(ILP)。在展开的版本中,可能会有更多的操作供处理器推入处理管道,而不必担心 对于 循环条件。

    类似的帖子 this 解释CUDA无法进行运行时循环展开。在你的情况下,CUDA编译器没有任何线索 size 将为100,因此编译时循环展开不会发生,因此如果强制展开,可能会损害性能。

    如果您确定 大小 对于所有执行都是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错误检查(解释 here ).