我最近使用CUDA(您可以在http://www.cuvilib.com/Reduction.pdf找到的那个,第16页)测试了减少算法。但最后,我遇到了不使用原子性的麻烦。所以基本上我做每个块的总和并将其存储到共享数组中。然后我把它恢复到全局数组x(tdx是threadIndex.x,我是全局索引)。
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来总结共享的总和,但我想理解为什么这不起作用。将操作限制为单个线程时,原子添加完全是无意义的。简单的总和应该可以正常工作!
答案 0 :(得分:3)
__syncthreads()
只同步同一个块中的线程,而不是跨越不同的块,CUDA没有跨块的安全同步机制。
错误的结果是由同步问题引起的。操作数x[k]
是来自不同块的计算结果:x[0]
是块0
的结果,x[1]
是块1
的结果,等等线程0
可以在某些块真正完成计算之前开始添加它们。
您应该将第二个代码段放在不同的内核中,以便强制执行同步,并且行sum[0] += x[k];
现在可以正常工作。
答案 1 :(得分:0)
正如已经指出的那样,您的问题是由于第一次传递后缺少同步,因为您无法在块之间进行同步。工具包提供的walkthrough减少了sample codes。
话虽如此,我强烈建议人们不要编写缩减内核(或其他原语,如扫描),这些原语存在于库代码中。将您的努力投入其他地方并在可用的情况下重用现有的优化代码会更好。如果您这样做是为了学习当然,这不适用!