我在CUDA样本中使用了改编自threadFenceReduction的缩减代码,这也在this presentation (PDF)中进行了描述。
在做一些调试时,我发现只是插入一个额外的__syncthreads()调用,减少不再给出正确的总和:
typedef int64_t SumType;
template <int blockSize>
static __device__ void
reduceBlock(
SumType mySum,
const unsigned int tid
)
{
// Each thread puts its local sum into shared memory
extern __shared__ SumType sdata[];
sdata[tid] = mySum;
__syncthreads();
// Sum values at an offset of 128 and 64
if( blockSize >= 256 ) { if (tid < 128) { sdata[tid] = mySum = mySum + (sdata[tid + 128]); } __syncthreads(); }
if( blockSize >= 128 ) { if (tid < 64) { sdata[tid] = mySum = mySum + (sdata[tid + 64]); } __syncthreads(); }
if( tid < 32 )
{
__syncthreads(); // <=== Extra __syncthreads(), breaks reduction!
// Synchronize within warp using volatile type
volatile SumType *smem = sdata;
if( blockSize >= 64 ) { smem[tid] = mySum = mySum + (smem[tid + 32]); }
if( blockSize >= 32 ) { smem[tid] = mySum = mySum + (smem[tid + 16]); }
if( blockSize >= 16 ) { smem[tid] = mySum = mySum + (smem[tid + 8]); }
if( blockSize >= 8 ) { smem[tid] = mySum = mySum + (smem[tid + 4]); }
if( blockSize >= 4 ) { smem[tid] = mySum = mySum + (smem[tid + 2]); }
if( blockSize >= 2 ) { smem[tid] = mySum = mySum + (smem[tid + 1]); }
}
}
为什么插入额外的__syncthreads()导致此代码不再起作用?
请参阅下面的答案,了解自包含的代码示例。
编辑:将__syncthreads()移动到示例中的if()语句中,以反映实际触发错误的代码。
答案 0 :(得分:2)
问题与只为块中的某些线程调用__syncthreads()有关。最终结果是一些非常奇怪的行为。 来自CUDA C Programming Guide,B.6节:
__ syncthreads()在条件代码中是允许的,但仅当条件在整个线程块中进行相同的求值时, 否则代码执行可能会挂起或产生意外 副作用。
我将其归结为以下简单示例。共享内存中的标志s_onlyOneBlock由每个块中的一个线程设置;在块0中,它是真的,而在其他块中,它是错误的。可以预期块0中的所有线程获得s_onlyOneBlock = true;但是,因为__syncthreads()只调用线程0到31,所以行为是意外的:只有线程0到31才能获得s_onlyOneBlock = true:
#include <stdio.h>
static __global__ void
kernel()
{
__shared__ bool s_onlyOneBlock;
const unsigned int tid = threadIdx.x;
// Call __syncthreads() for only some threads (don't do this!)
if( tid < 32 )
__syncthreads();
// Thread 0 sets s_onlyOneBlock
if( tid == 0 )
s_onlyOneBlock = ( blockIdx.x == 0 );
__syncthreads();
if( s_onlyOneBlock )
{
// Only block 0 should reach this point
if( tid==0 || tid==31 || tid==32 || tid==128 )
printf("s_onlyOneBlock is TRUE: block=%d thread=%d\n", blockIdx.x, threadIdx.x);
}
else
{
if( tid==0 || tid==31 || tid==32 || tid==128 )
printf("s_onlyOneBlock is false: block=%d thread=%d\n", blockIdx.x, threadIdx.x);
}
}
int main()
{
kernel<<<2, 256>>>();
cudaDeviceSynchronize();
}
结果:
nvcc syncproblem.cu -o syncproblem
./syncproblem
s_onlyOneBlock is false: block=0 thread=128 <--- should be true!
s_onlyOneBlock is false: block=1 thread=128
s_onlyOneBlock is false: block=0 thread=32 <--- should be true!
s_onlyOneBlock is false: block=1 thread=32
s_onlyOneBlock is TRUE: block=0 thread=0
s_onlyOneBlock is TRUE: block=0 thread=31
s_onlyOneBlock is false: block=1 thread=0
s_onlyOneBlock is false: block=1 thread=31