我已经阅读了Shuffle Tips and Tricks论文,但是我不确定如何将其应用于继承的一些易懂的代码:
extern __shared__ unsigned int lpSharedMem[];
int tid = threadIdx.x;
lpSharedMem[tid] = startValue;
volatile unsigned int *srt = lpSharedMem;
// ...various stuff
srt[tid] = min( srt[tid], srt[tid+32] );
srt[tid] = min( srt[tid], srt[tid+16] );
srt[tid] = min( srt[tid], srt[tid+8] );
srt[tid] = min( srt[tid], srt[tid+4] );
srt[tid] = min( srt[tid], srt[tid+2] );
srt[tid] = min( srt[tid], srt[tid+1] );
__syncthreads();
即使没有CUDA,此代码也相当狡猾,但查看this implementation,我会看到:
__device__ inline int min_warp(int val) {
val = min(val, __shfl_xor(val, 16));
val = min(val, __shfl_xor(val, 8));
val = min(val, __shfl_xor(val, 4));
val = min(val, __shfl_xor(val, 2));
val = min(val, __shfl_xor(val, 1));
return __shfl(val, 0);
}
此代码可通过以下方式调用:
int minVal = min_warp(startValue);
因此,我可以用上面的代码替换我比较狡猾的
volatile
。但是,我无法真正理解发生了什么。有人可以解释我是否正确,以及min_warp()
函数中到底发生了什么。 最佳答案
根据int __shfl_xor(int var, int laneMask, int width=warpSize);
的描述:
通道ID是线程在扭曲中的索引,范围是0到31。因此,硬件会对每个线程执行按位XOR:sourceLaneId XOR laneMask => destinationLaneId
例如,使用线程0和:
__shfl_xor(val, 16)
然后线程0获得线程16的值。
现在使用线程4:
因此,线程4获得了线程20的值。
如果我们回到实际的算法,我们会看到这是应用
min
运算符的并行约简。在步骤:PD:请注意,两个代码并不完全相同。此偏移量“32”告诉我们您的共享内存阵列的长度为2 * WARP。 (您正在将2 * WARP值减少为1)
srt[tid] = min( srt[tid], srt[tid+32] );
洗牌一将WARP值减小为1。