nvidia kepler洗牌是“破坏性的”吗?

时间:2014-03-10 14:13:57

标签: cuda nvidia kepler

我正在使用新的kepler的shuffle指令在CUDA上实现并行缩减,类似于: http://devblogs.nvidia.com/parallelforall/faster-parallel-reductions-kepler/

我在给定矩阵中搜索行的最小值,在内核的末尾我有以下代码:

my_register = min(my_register, __shfl_down(my_register,8,16));
my_register = min(my_register, __shfl_down(my_register,4,16));
my_register = min(my_register, __shfl_down(my_register,2,16));
my_register = min(my_register, __shfl_down(my_register,1,16));

我的块是16 * 16,所以一切正常,使用该代码我在同一个内核的两个子行中获得最小值。

现在我还需要返回矩阵每行中最小元素的索引,所以我要用“if”语句替换“min”并以类似的方式处理这些索引,我陷入困境这段代码:

if (my_reg > __shfl_down(my_reg,8,16)){my_reg = __shfl_down(my_reg,8,16);};
if (my_reg > __shfl_down(my_reg,4,16)){my_reg = __shfl_down(my_reg,4,16);};
if (my_reg > __shfl_down(my_reg,2,16)){my_reg = __shfl_down(my_reg,2,16);};
if (my_reg > __shfl_down(my_reg,1,16)){my_reg = __shfl_down(my_reg,1,16);};

没有cudaErrors,但内核现在返回垃圾。不过我已经解决了这个问题:

myreg_tmp = __shfl_down(myreg,8,16);
if (myreg > myreg_tmp){myreg = myreg_tmp;};
myreg_tmp = __shfl_down(myreg,4,16);
if (myreg > myreg_tmp){myreg = myreg_tmp;};
myreg_tmp = __shfl_down(myreg,2,16);
if (myreg > myreg_tmp){myreg = myreg_tmp;};
myreg_tmp = __shfl_down(myreg,1,16);
if (myreg > myreg_tmp){myreg = myreg_tmp;};

因此,分配新的tmp变量以潜入相邻寄存器可以为我保存所有内容。 现在的问题是:kepler shuffle指令是否具有破坏性?在某种意义上,两次调用相同的指令不会发出相同的结果。我还没有为那些寄存器说“my_reg> __shfl_down(my_reg,8,16)” - 这就是我的困惑。任何人都可以解释一下两次调用shuffle的问题是什么?我几乎是CUDA的新手,欢迎详细解释傻瓜

1 个答案:

答案 0 :(得分:5)

经纱洗牌不具有破坏性。如果在完全相同的条件下重复 ,则每次都会返回相同的结果。 warp shuffle函数本身不会修改var值(示例中为myreg)。

您遇到的问题是由于第一种方法中第二次调用__shfl_down()的参与线程数与其他调用不同这两种方法

首先,让我们提醒自己documentation中的一个关键点:

  

线程只能从另一个主动参与__shfl()命令的线程中读取数据。如果目标线程处于非活动状态,则检索到的值未定义。

现在让我们来看看你的第一个"破坏"方法:

if (my_reg > __shfl_down(my_reg,8,16)){my_reg = __shfl_down(my_reg,8,16);};

第一次调用__shfl_down()以上(在if子句中),所有线程正在参与。因此,__shfl_down()返回的所有值都将是您所期望的。但是,一旦if子句完成,只有满足if子句的线程才会参与if语句的主体。因此,在if语句正文中第二次调用__shfl_down()时,只有其my_reg值大于它们上面8个通道的my_reg值的线程才会参与。这意味着其中一些赋值语句可能不会返回您期望的值,因为另一个线程可能没有参与。 (上面的8个线程的参与将取决于该线程进行的if比较的结果,这可能是也可能不是。)

您建议的第二种方法没有这样的问题,并根据您的陈述正确运作。所有线程都参与__shfl_down()的每次调用。