我已经阅读了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();
Run Code Online (Sandbox Code Playgroud)
即使没有CUDA,这段代码也很狡猾,但看看这个实现,我看到:
__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);
}
Run Code Online (Sandbox Code Playgroud)
此代码可以通过以下方式调用:
int minVal = min_warp(startValue);
Run Code Online (Sandbox Code Playgroud)
因此,我可以用volatile
上面的代码替换我的狡猾.但是,我真的不明白发生了什么; 有人可以解释我是否正确,以及该min_warp()
功能究竟发生了什么.
从描述int __shfl_xor(int var, int laneMask, int width=warpSize);
:
__shfl_xor()通过使用laneMask执行调用者的通道ID的按位异或来计算源行ID:返回由结果通道ID保存的var的值.(......)
通道ID是warp中线程的索引,从0到31.因此硬件为每个线程执行按位异或:sourceLaneId XOR laneMask => destinationLaneId
例如,使用线程0和:
__shfl_xor(val, 16)
Run Code Online (Sandbox Code Playgroud)
laneMask = 0b00000000000000000000000000010000 = 16(十进制)
srclaneID = 0b00000000000000000000000000000000 = 0(十进制)
XOR ------------------------------------------------- ---------
dstLaneID = 0b00000000000000000000000000010000 = 16(十进制)
然后线程0获取线程16的值.
现在用线程4:
laneMask = 0b00000000000000000000000000010000 = 16(十进制)
srclaneID = 0b00000000000000000000000000000100 = 4(十进制)
XOR ------------------------------------------------- ---------
dstLaneID = 0b00000000000000000000000000010100 = 20(十进制)
所以线程4获取线程20的值.等等...
如果我们回到实际算法,我们会发现它min
是应用运算符的并行减少.在步骤:
PD:请注意这两个代码并不完全相同.这个'32'的偏移告诉我们你的共享内存数组是2*WARP长.(您将2*WARP值减少为1)
srt[tid] = min( srt[tid], srt[tid+32] );
Run Code Online (Sandbox Code Playgroud)
随机播放将WARP值降低为1.