我有一个float值数组,即life,我想在CUDA中计算值大于0的条目数。
在CPU上,代码如下所示:
int numParticles = 0;
for(int i = 0; i < MAX_PARTICLES; i++){
if(life[i]>0){
numParticles++;
}
}
现在在CUDA,我尝试过这样的事情:
__global__ void update(float* life, int* numParticles){
int idx = threadIdx.x + blockIdx.x * blockDim.x;
if (life[idx]>0){
(*numParticles)++;
}
}
//life is a filled device pointer
int launchCount(float* life)
{
int numParticles = 0;
int* numParticles_d = 0;
cudaMalloc((void**)&numParticles_d, sizeof(int));
update<<<MAX_PARTICLES/THREADS_PER_BLOCK,THREADS_PER_BLOCK>>>(life, numParticles_d);
cudaMemcpy(&numParticles, numParticles_d, sizeof(int), cudaMemcpyDeviceToHost);
std::cout << "numParticles: " << numParticles << std::endl;
}
但由于某种原因,CUDA尝试总是为numParticles返回0。怎么样?
答案 0 :(得分:3)
此:
if (life[idx]>0){
(*numParticles)++;
}
是一种后读写入危险。多个线程将同时尝试从numParticles
进行读写。 CUDA执行模型不保证同时交易的顺序。
您可以使用原子内存事务来完成此工作,例如:
if (life[idx]>0){
atomicAdd(numParticles, 1);
}
这将序列化内存事务并使计算正确。它也会对性能产生很大的负面影响。
您可能希望调查每个块使用缩减类型计算计算局部和,然后以原子方式或在主机上或在第二个内核中对块本地求和求和。
答案 1 :(得分:1)
您的代码实际上是启动MAX_PARTICLES
个线程,并且多个线程块同时执行(*numParticles)++;
。这是一场竞争。所以你得到的结果是0,或者你运气好的话,有时会比0大一点。
当您尝试总结所有life[i]>0 ? 1 : 0
的{{1}}时,您可以按CUDA parallel reduction来实施内核,或使用Thrust reduction来简化您的生活。