从数组CUDA计算值

时间:2016-05-17 09:04:15

标签: c++ cuda

我有一个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。怎么样?

2 个答案:

答案 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来简化您的生活。