我知道“每个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
答案 0 :(得分:7)
您应该将共享内存变量声明为volatile:
__shared__ volatile int data [BLOCK_SIZE];
您看到的问题是Fermi架构和编译器优化的工件。 Fermi架构缺乏直接在共享存储器上运行的指令(它们存在于G80 / 90 / GT200系列中)。所以一切都被加载到注册,操作和存储回共享内存。但是编译器可以自由地推断,如果在寄存器中暂存一系列操作,则可以使代码更快,而不需要从共享内存中间加载和存储。当你依赖于同一个warp操作共享内存中的线程的隐式同步时,这是非常好的除了,就像在这种缩减代码中一样。
通过将共享内存缓冲区声明为volatile,强制编译器在减少的每个阶段之后强制执行共享内存写入,并且还原warp中线程之间的隐式数据同步。
Fermi的编程说明中讨论了这个问题,该编程说明随CUDA工具包一起发布(或可能随附)。