我是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个?
答案 0 :(得分:29)
没有。这意味着您已经使用一个块调用了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<5
6次,以及递增i
5次。在第二种情况下,它只涉及归档b
数组内容(如果稍后使用int i=5;
,则可能加上i
。循环展开的另一个好处是增强了指令级并行(ILP)。在展开版本中,处理器可能会有更多操作进入处理管道而不必担心每次迭代中的for
循环条件。
像this这样的帖子解释说,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错误检查(解释here)。