【发布时间】:2013-07-24 09:46:06
【问题描述】:
我最近使用 CUDA 测试了减少算法(例如,您可以在 http://www.cuvilib.com/Reduction.pdf,第 16 页找到该算法)。但最后,我遇到了不使用原子性的麻烦。所以基本上我做每个块的总和并将其存储到共享数组中。然后我把它取回全局数组x(tdx是threadIndex.x,i是全局索引)。
if(i==0){
*sum = 0.; // Initialize to 0
}
__syncthreads();
if (tdx == 0){
x[blockIdx.x] = s_x[tdx]; //get the shared sums in global memory
}
__syncthreads();
然后我想对前 x 个元素求和(与我的块一样多)。 使用原子性时它工作正常(与 cpu 的结果相同),但是当我使用下面的注释行时它不起作用并且经常产生“nan”:
if(i == 0){
for(int k = 0; k < gridDim.x; k++){
atomicAdd(sum, x[k]); //Works good
//sum[0] += x[k]; //or *sum += x[k]; //Does not work, often results in nan
}
}
现在实际上我直接使用 atomicadd 来对共享和求和,但我想了解为什么这不起作用。当将操作限制为单个线程时,原子添加是非常无意义的。简单的总和应该可以正常工作!
【问题讨论】:
-
__syncthreads()只同步同一块中的线程,而不是跨不同块。我认为不正确的结果是由于同步问题。通过atomicAdd,您正在执行__syncthreads()缺少的不同块之间的同步。 -
确实,当我在 for 循环中添加一个 __syncthreads() 时,简单的求和就起作用了!但我不明白。我在全局数组上只使用一个线程进行求和,那么我为什么要关心在 for 循环中同步呢?
-
好的,我想我明白了!进入循环时不一定会写入全局数组,因为所有块都不会同步。那么“全局”同步线程的命令是什么?
-
操作数
x[k]是不同块的计算结果:x[0]是块0的结果,x[1]是块1的结果,等等。怀疑线程0可能在某些块真正完成计算之前开始将它们相加。试试下面的。将第二个代码 sn -p 放在不同的内核中,以便强制同步,然后尝试sum[0] += x[k];行是否有效。 -
关于你的新问题,CUDA 没有跨区块的安全同步机制。