多个块和线程可以写入相同的输出吗?

时间:2013-01-21 17:19:46

标签: cuda

我有以下CUDA内核代码,用于计算两个数组的总平方误差。

__global__ void kSquaredError(double* data, double* recon, double* error, 
                               unsigned int num_elements)
{
    const unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;

    for (unsigned int i = idx; i < num_elements; i += blockDim.x * gridDim.x) {
        *error += pow(data[i] - recon[i], 2);
    }
}

我需要一个标量输出(错误)。在这种情况下,似乎所有线程都同时写入错误。我需要某种方式来同步它吗?

目前我的结果不好,所以我猜是有问题。

1 个答案:

答案 0 :(得分:1)

由于所有线程都试图同时更新相同的全局内存地址,因此您现在正在执行的实现受竞争条件的限制。您可以轻松地放置atomicAdd函数而不是*error += pow...,但由于在每次更新时序列化,因此会遇到性能问题。

相反,你应该尝试使用共享内存进行减少,如下所示:

_global__ void kSquaredError(double* data, double* recon, double* error, unsigned int num_elements) {
    const unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;
    const unsigned int tid = threadIdx.x;
    __shared__ double serror[blockDim.x];//temporary storage of each threads error

    for (unsigned int i = idx; i < num_elements; i += blockDim.x * gridDim.x) {
        serror[tid] += pow(data[i] - recon[i], 2);//put each threads value in shared memory
    }

    __syncthreads();

    int i = blockDim.x >> 1; //halve the threads
    for(;i>0;i>>=1) {//reduction in shared memory
            if(tid<i) {
                serror[tid] += serror[tid+i];
                __syncthreads();//make shure all threads are at the same state and have written to shared memory
            }
    }

    if(tid == 0) {//thread 0 updates the value in global memory
        atomicAdd(error,serror[tid]);// same as *error += serror[tid]; but atomic
    }
}

它按照以下原则工作,每个线程都有自己的临时变量,它计算所有输入的错误总和,当它完成所有线程收敛于__syncthreads指令以确保所有数据已经完成了。

现在,块中所有线程中的一半将从相应的另一半中获取一个值,将其添加到自己的一半,再次将一半的线程再次添加,直到剩下一个线程(线程0)为止总和。

现在,线程0将使用atomicAdd函数更新全局内存,以避免与其他块有竞争条件(如果有)。

如果我们只使用第一个示例并在每个赋值中使用atomicAdd。你会有gridDim.x*blockDim.x*num_elements个序列化的原子函数,现在我们只有gridDim.x个原子函数,而且要少得多。

请参阅Optimizing Parallel Reduction in CUDA,了解有关如何使用cuda进行减少的详细信息。

修改

在reduce for循环中添加if语句,忘记了。