我正在尝试为双精度数组实现经典的点积内核,并对各个块的最终总和进行原子计算.我使用atomicAdd进行双精度编程指南的第116页中所述.可能我做错了.每个块中线程的部分和都是正确计算的,但是之后原子操作似乎没有正常工作因为每次我使用相同的数据运行我的内核,我会收到不同的结果.如果有人能发现错误或提供替代解决方案,我将不胜感激!这是我的内核:
__global__ void cuda_dot_kernel(int *n,double *a, double *b, double *dot_res)
{
__shared__ double cache[threadsPerBlock]; //thread shared memory
int global_tid=threadIdx.x + blockIdx.x * blockDim.x;
int i=0,cacheIndex=0;
double temp = 0;
cacheIndex = threadIdx.x;
while (global_tid < (*n)) {
temp += a[global_tid] * b[global_tid];
global_tid += blockDim.x * gridDim.x;
}
cache[cacheIndex] = temp;
__syncthreads();
for (i=blockDim.x/2; i>0; i>>=1) {
if (threadIdx.x < i) {
cache[threadIdx.x] += cache[threadIdx.x + i];
}
__syncthreads();
}
__syncthreads();
if (cacheIndex==0) {
*dot_res=cuda_atomicAdd(dot_res,cache[0]);
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的设备函数atomicAdd:
__device__ double cuda_atomicAdd(double *address, double val)
{
double assumed,old=*address;
do {
assumed=old;
old= __longlong_as_double(atomicCAS((unsigned long long int*)address,
__double_as_longlong(assumed),
__double_as_longlong(val+assumed)));
}while (assumed!=old);
return old;
}
Run Code Online (Sandbox Code Playgroud)
使用临时CUDA代码获得正确的减少可能很棘手,因此这里是使用CUDA工具包中包含的Thrust算法的替代解决方案:
#include <thrust/inner_product.h>
#include <thrust/device_ptr.h>
double do_dot_product(int n, double *a, double *b)
{
// wrap raw pointers to device memory with device_ptr
thrust::device_ptr<double> d_a(a), d_b(b);
// inner_product implements a mathematical dot product
return thrust::inner_product(d_a, d_a + n, d_b, 0.0);
}
Run Code Online (Sandbox Code Playgroud)
您使用该cuda_atomicAdd
功能的方式不正确。内核的这一部分:
if (cacheIndex==0) {
*dot_res=cuda_atomicAdd(dot_res,cache[0]);
}
Run Code Online (Sandbox Code Playgroud)
是罪魁祸首。在这里,您自动添加到dot_res
. 然后用它返回的结果进行非原子设置。dot_res
该函数的返回结果是被原子更新的位置的先前值,并且它仅提供给调用者的“信息”或本地使用。您没有将其分配给原子更新的内容,这完全违背了使用原子内存访问的目的。做这样的事情:
if (cacheIndex==0) {
double result=cuda_atomicAdd(dot_res,cache[0]);
}
Run Code Online (Sandbox Code Playgroud)