我正在尝试为双精度数组实现经典的点积内核,并对各个块的最终和进行原子计算。在programming guide.Probably的第116页中,我使用atomicAdd实现了双精度。我正在做一些事情,每个块中的线程的部分和都可以正确计算,但之后原子操作似乎不能正常工作,因为每次我使用相同的数据运行内核时,我都会收到不同的结果。如果有人能发现错误或提供替代解决方案,我将不胜感激!下面是我的内核:
__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]);
}
}下面是我的设备函数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;
}发布于 2012-02-26 16:10:54
您错误地使用了cuda_atomicAdd函数。内核的这一部分:
if (cacheIndex==0) {
*dot_res=cuda_atomicAdd(dot_res,cache[0]);
}才是罪魁祸首。在这里,您可以原子地添加到dot_res。然后用它返回的结果非原子地设置dot_res。此函数的返回结果是被原子更新的位置的前一个值,它仅供“信息”或调用者的本地使用。你没有把它分配给你原子更新的东西,这完全违背了使用原子内存访问的初衷。改为这样做:
if (cacheIndex==0) {
double result=cuda_atomicAdd(dot_res,cache[0]);
}发布于 2012-02-26 10:04:35
使用即席CUDA代码获得正确的缩减可能很棘手,因此这里有一个使用推力算法的替代解决方案,该算法包含在CUDA工具包中:
#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);
}发布于 2012-02-26 12:08:30
我没有检查你的代码的深度,但这里有一些建议。
我只建议使用推力,如果你只使用你的gpu来完成这些通用的任务,因为如果一个复杂的问题会出现,人们没有想法在GPU上高效地编程并行。
由于数据已经在设备上,因此启动新内核不会降低性能。
未经测试,我只是把它写在记事本上:
/*
* @param inCount_s unsigned long long int Length of both input arrays
* @param inValues1_g double* First value array
* @param inValues2_g double* Second value array
* @param outDots_g double* Output dots of each block, length equals the number of blocks
*/
__global__ void dotProduct(const unsigned long long int inCount_s,
const double* inValuesA_g,
const double* inValuesB_g,
double* outDots_g)
{
//get unique block index in a possible 3D Grid
const unsigned long long int blockId = blockIdx.x //1D
+ blockIdx.y * gridDim.x //2D
+ gridDim.x * gridDim.y * blockIdx.z; //3D
//block dimension uses only x-coordinate
const unsigned long long int tId = blockId * blockDim.x + threadIdx.x;
/*
* shared value pair products array, where BLOCK_SIZE power of 2
*
* To improve performance increase its size by multiple of BLOCK_SIZE, so that each threads loads more then 1 element!
* (outDots_g length decreases by same factor, and you need to range check and initialize memory)
* -> see harris gpu optimisations / parallel reduction slides for more informations.
*/
__shared__ double dots_s[BLOCK_SIZE];
/*
* initialize shared memory array and calculate dot product of two values,
* shared memory always needs to be initialized, its never 0 by default, else garbage is read later!
*/
if(tId < inCount_s)
dots_s[threadIdx.x] = inValuesA_g[tId] * inValuesB_g[tId];
else
dots_s[threadIdx.x] = 0;
__syncthreads();
//do parallel reduction on shared memory array to sum up values
reductionAdd(dots_s, dots_s[0]) //see my thesis link
//output value
if(threadIdx.x == 0)
outDots_g[0] = dots_s[0];
//start new parallel reduction kernel to sum up outDots_g!
}编辑:删除不必要的点。
https://stackoverflow.com/questions/9449541
复制相似问题