CUDA。如何展开前32个线程,以便它们并行执行?

时间:2012-12-21 16:57:12

标签: c++ cuda gpu

我知道“每个warp包含连续增加的线程ID的线程,第一个warp包含线程0”,因此前32个线程应该在第一个warp中。另外我知道一个warp中的所有线程都在任何可用的Streaming Multiprocessor上同时执行。

据我所知,因为如果只执行一个warp,就不需要线程同步。但是如果我在倒数第二个__syncthreads()块中移除if中的任何一个,则下面的代码会产生错误的答案。我试图找到原因,但最终没有任何结果。我真的希望得到你的帮助,所以你可以告诉我这段代码有什么问题?为什么我不能只留下__syncthreads()并得到正确答案?

#define BLOCK_SIZE 128

__global__ void reduce ( int * inData, int * outData )
{
 __shared__ int data [BLOCK_SIZE]; 
 int tid = threadIdx.x; 
 int i   = blockIdx.x * blockDim.x + threadIdx.x; 

 data [tid] = inData [i] + inData [i + blockDim.x / 2 ];
 __syncthreads ();

 for ( int s = blockDim.x / 4; s > 32; s >>= 1 ) 
 {
  if ( tid < s ) 
   data [tid] += data [tid + s]; 
  __syncthreads (); 
 } 

 if ( tid < 32 )
 { 
  data [tid] += data [tid + 32];
  __syncthreads (); 
  data [tid] += data [tid + 16];
  __syncthreads (); 
  data [tid] += data [tid + 8];
  __syncthreads (); 
  data [tid] += data [tid + 4];
  __syncthreads (); 
  data [tid] += data [tid + 2];
  __syncthreads (); 
  data [tid] += data [tid + 1];
  __syncthreads (); 
 }
 if ( tid == 0 )
  outData [blockIdx.x] = data [0];
}

void main()
{
...
 reduce<<<dim3(128), dim3(128)>>>(dev_data, dev_res);
...
}

P.S。我正在使用GT560Ti

1 个答案:

答案 0 :(得分:7)

您应该将共享内存变量声明为volatile:

__shared__ volatile int data [BLOCK_SIZE]; 

您看到的问题是Fermi架构和编译器优化的工件。 Fermi架构缺乏直接在共享存储器上运行的指令(它们存在于G80 / 90 / GT200系列中)。所以一切都被加载到注册,操作和存储回共享内存。但是编译器可以自由地推断,如果在寄存器中暂存一系列操作,则可以使代码更快,而不需要从共享内存中间加载和存储。当你依赖于同一个warp操作共享内存中的线程的隐式同步时,这是非常好的除了,就像在这种缩减代码中一样。

通过将共享内存缓冲区声明为volatile,强制编译器在减少的每个阶段之后强制执行共享内存写入,并且还原warp中线程之间的隐式数据同步。

Fermi的编程说明中讨论了这个问题,该编程说明随CUDA工具包一起发布(或可能随附)。