了解CUDA shfl指令

时间:2016-09-06 08:37:40

标签: c++ cuda

我已阅读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()函数究竟发生了什么。

2 个答案:

答案 0 :(得分:8)

来自int __shfl_xor(int var, int laneMask, int width=warpSize);的说明:

  

__ shfl_xor()通过使用laneMask执行调用者的通道ID的按位异或来计算源行ID:返回由结果通道ID保存的var的值。 (...)

通道ID是warp中线程的索引,从0到31.因此硬件为每个线程执行按位XOR:sourceLaneId XOR laneMask => destinationLaneId

例如,使用线程0和:

__shfl_xor(val, 16)
  

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运算符。在步骤:

  1. 32个线程将它们的值累积到较低的16个线程中。
  2. 16个线程累积到较低的8个线程中。 (其他线程对于实际算法无关紧要)
  3. 8个线程累积到较低的4个线程中。
  4. 4个线程累积到下面2个线程......
  5. PD:请注意这两个代码并不完全相同。这个'32'的偏移告诉我们你的共享内存数组是2 * WARP长。 (您将2 * WARP值减少为1)

    srt[tid] = min( srt[tid], srt[tid+32] );
    

    随机播放将WARP值降低为1。

答案 1 :(得分:1)

这些幻灯片第27页有一个直观的图表:

http://on-demand.gputechconf.com/gtc/2017/presentation/s7622-Kyrylo-perelygin-robust-and-scalable-cuda.pdf

xor 1首先交换奇数/偶数单输入

xor 2然后交换奇数/偶数对先前交换的输入

xor 4然后交换奇数/偶数四元组

另一种方法是计算一个完整的xor掩码和xor原始输入值。使用不同的warp shuffle指令可以通过多种方式实现此目的。这种xor模式也用于FFT / DFT变换,也称为“蝶形图”。

https://en.wikipedia.org/wiki/Butterfly_diagram