Cuda 英伟达开普勒洗牌是“破坏性的”吗?

Cuda 英伟达开普勒洗牌是“破坏性的”吗?,cuda,nvidia,kepler,Cuda,Nvidia,Kepler,我使用新的开普勒洗牌指令在CUDA上实现并行缩减,类似于: 我在给定矩阵中搜索行的最小值,在内核的最后,我得到了以下代码: 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))

我使用新的开普勒洗牌指令在CUDA上实现并行缩减,类似于:

我在给定矩阵中搜索行的最小值,在内核的最后,我得到了以下代码:

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);};
没有任何错误,但内核现在返回垃圾。尽管如此,我还是有办法:

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变量以潜入相邻寄存器为我节省了一切。
现在的问题是:开普勒洗牌指令是否具有破坏性?从某种意义上说,两次调用同一条指令不会产生相同的结果。我没有给那些寄存器分配任何内容,比如my_reg>\u shfl\u downmy_reg,8,16-这增加了我的困惑。有人能解释一下调用两次shuffle有什么问题吗?我几乎是CUDA的新手,所以欢迎对假人进行详细解释

扭曲混洗并没有破坏性。如果在完全相同的条件下重复该操作,每次将返回相同的结果。示例中的var值myreg不会被warp shuffle函数本身修改

您遇到的问题是由于在第一个方法中第二次调用_shfl_down时参与线程的数量不同于两种方法中的其他调用

首先,让我们提醒自己以下方面的一个关键点:

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

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

if (my_reg > __shfl_down(my_reg,8,16)){my_reg = __shfl_down(my_reg,8,16);};
第一次在if子句中调用上面的_shfl_时,所有线程都在参与。因此_shfl_down返回的所有值都是您期望的值。但是,一旦if子句完成,只有满足if子句的线程才会参与if语句体。因此,在if语句体中第二次调用u shfl_down时,只有其my_reg值大于其上方线程8通道的my_reg值的线程才会参与。这意味着其中一些赋值语句可能不会返回您期望的值,因为另一个线程可能没有参与。上述线程8通道的参与取决于该线程进行的if比较的结果,该结果可能为真,也可能为假


你提出的第二种方法没有这样的问题,并且根据你的陈述是正确的。所有线程都参与每次调用“shfl\u down”。

扭曲洗牌不会造成破坏。如果在完全相同的条件下重复该操作,每次将返回相同的结果。示例中的var值myreg不会被warp shuffle函数本身修改

您遇到的问题是由于在第一个方法中第二次调用_shfl_down时参与线程的数量不同于两种方法中的其他调用

首先,让我们提醒自己以下方面的一个关键点:

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

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

if (my_reg > __shfl_down(my_reg,8,16)){my_reg = __shfl_down(my_reg,8,16);};
第一次在if子句中调用上面的_shfl_时,所有线程都在参与。因此_shfl_down返回的所有值都是您期望的值。但是,一旦if子句完成,只有满足if子句的线程才会参与if语句体。因此,在if语句体中第二次调用u shfl_down时,只有其my_reg值大于其上方线程8通道的my_reg值的线程才会参与。这意味着其中一些赋值语句可能不会返回您期望的值,因为另一个线程可能没有参与。上述线程8通道的参与取决于该线程进行的if比较的结果,该结果可能为真,也可能为假


你提出的第二种方法没有这样的问题,并且根据你的陈述是正确的。所有线程都会参与每次调用u_shfl_down。

现在已经非常清楚了,谢谢!当我第一次尝试实现warp shuffle时,我遇到了一个类似的问题:我使用了代码的第一个变体,但所有内容都包含在if子句中:ifthreadIdx.x它也不起作用,结果中返回了一些垃圾。就我所知
如果某些线程处于非活动状态,倒立扭曲洗牌行为是不可预测的。现在非常清楚了,谢谢!当我第一次尝试实现warp shuffle时,我遇到了一个类似的问题:我使用了代码的第一个变体,但所有内容都包含在if子句中:ifthreadIdx.x它也不起作用,结果中返回了一些垃圾。据我所知,在某些线程处于非活动状态的情况下,扭曲混洗行为是不可预测的。