我有以下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);
}
}
我需要一个标量输出(错误)。在这种情况下,似乎所有线程都同时写入错误。我需要某种方式来同步它吗?
目前我的结果不好,所以我猜是有问题。
答案 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语句,忘记了。