多block的数据求和问题

昨天在该版询问了cuda改写cpu版本计算程序的问题,十分感谢ice、横扫千军两位斑竹和lianuosun站友的热情及时高水准的回复,帮我解决了久思不得其解的问题。
今天又遇到一个问题难以解决:

问题详情:
和之前问题一样,程序的物理意义是建立64512256的三维空间格点,我将数据分为444块,每块包含1612864个格点,一个物理上的格点的数据由一个thread计算。
现在希望实现空间所有格点的数据求和以及寻找最大值。

以数据求和为例:

核函数中参数的含义和值:
三个维度的格点数目 NScale_R 64,512,256
三个维度的块数目 Block_num_R 4,4,4
每一块三个维度的SM数目 Block_Width_R 16,128,64
总格电数目 Nsize = 64512256
x_g,y_g,z_g表示了物理上该格点的坐标,pos_g表示了格点数据在所有数据组成的数组中的位置。

数据求和
核函数功能:建立一个64512256的double共享数组tempD。每个thread保存一个tempD值,求所有tempD的和并存入EpsilonBar_cal;

函数调用:

EpsilonBar_cal=0.0;

dim3 dimGrid(Block_num_R[0],Block_num_R[1],Block_num_R[2]);
dim3 dimBlock(Block_Width_R[0],Block_Width_R[1],Block_Width_R[2]);
ComputeEpsilon_1_GPU<<<dimGrid, dimBlock>>>(… EpsilonBar_cal, Block_Width_R, NScale_R, Nsize);

核函数内容
global void ComputeEpsilon_1_GPU(… double *t_EpsilonBar_cal, int *Block_Width_g, int *t_NScale, int t_Nsize)
{
…//一些与求和无关的参数设定

int pos_g, pos_g1;
extern shared double tempD;

int x_g=blockIdx.xBlock_Width_g[0]+threadIdx.x;
int y_g=blockIdx.y
Block_Width_g[1]+threadIdx.y;
int z_g=blockIdx.z*Block_Width_g[2]+threadIdx.z;

//第一步:根据输入的数据计算每个格点的tempD值
if ((x_g<t_NScale[0])&&(y_g<t_NScale[1])&&(z_g<t_NScale[2]))
{
pos_g=(x_g*t_NScale[1]+y_g)*t_NScale[2]+z_g;

tempD[pos_g]=。。。。。。;
}

//第二步:单个block内的tempD求和
//总体思路:
第0步:将y_g=y0和z_g=z0的点的值求和,存入点(0,y0,z0),这样只需求和平面x_g=0上的点;
第1步:将平面x_g=0上,z_g=z0的点的值求和,存入点(0,0,z0),这样只需要求和直线x_g=0,y_g=0上的点;
第2步:将直线x_g=0,y_g=0上点的值求和,存入点(0,0,0);
这样就只需要对直线上的点求和,采取的方案是反复两两求和得到结果。

unsigned int t_stride[3];
int b_x[3]={x_g,y_g,z_g};
int b_Idx[3]={blockIdx.x,blockIdx.y,blockIdx.z};
int t_Idx[3]={threadIdx.x,threadIdx.y,threadIdx.z};

int flag=0;

for (int i=0;i<3;i++)
{
flag=0;
for (t_stride[i]=1;t_stride[i]<Block_Width_g[i];t_stride[i]=2)
{
__syncthreads();
flag=1;
if (t_Idx[i]%(2
t_stride[i])!=0) flag=0;
if ((x_g>=t_NScale[0])&&(y_g>=t_NScale[1])&&(z_g>=t_NScale[2])) flag=0;
if ((i==1)&&(t_Idx[0]!=0)) flag=0;
if ((i==2)&&((t_Idx[0]!=0)||(t_Idx[1]!=0))) flag=0;
int tempI=b_x[i]+t_stride[i];
if (tempI<((b_Idx[i]+1)*Block_Width_g[i])) flag=0;
if (tempI<t_NScale[i]) flag=0;

if (flag==1)
{
pos_g=(b_x[0]*t_NScale[1]+b_x[1])t_NScale[2]+b_x[2];
if (i==0) pos_g1=(tempI
t_NScale[1]+b_x[1])*t_NScale[2]+b_x[2];
if (i==1) pos_g1=(b_x[0]*t_NScale[1]+tempI)*t_NScale[2]+b_x[2];
if (i==2) pos_g1=(b_x[0]*t_NScale[1]+b_x[1])*t_NScale[2]+tempI;
tempD[pos_g]+=tempD[pos_g1];
}
}
__syncthreads();
}
__syncthreads();

//第三步:将每个block中点(0,0,0)的数据加在t_EpsilonBar_cal[0]上
if ((threadIdx.x==0)&&(threadIdx.y==0)&&(threadIdx.z==0))
{
pos_g=(x_g*t_NScale[1]+y_g)*t_NScale[2]+z_g;
t_EpsilonBar_cal[0]+=tempD[pos_g]/t_Nsize;
}
}

在与CPU版本的对照中,我确认这个求和结果是存在问题的,那么,问题出在什么地方?

笔误:
程序中:
if (tempI<((b_Idx[i]+1)*Block_Width_g[i])) flag=0;
if (tempI<t_NScale[i]) flag=0;
改为:
if (tempI>=((b_Idx[i]+1)*Block_Width_g[i])) flag=0;
if (tempI>=t_NScale[i]) flag=0;

又笔误: if ((x_g>=t_NScale[0])&&(y_g>=t_NScale[1])&&(z_g>=t_NScale[2]))
改为 if ((x_g>=t_NScale[0])||(y_g>=t_NScale[1])||(z_g>=t_NScale[2]))

LZ您好,手机上的,简要说一下。

您可以每个block先算出自己的结果,然后用原子操作累计一个全局的结果。

或者每个block保存自己的结果,然后再启动一个kernel扫尾得到最后的结果。

手机上网,看代码不便,回头再聊。

祝您好运!

啊,谢谢!我发现可能在前面的核函数就存在问题,是我的问题,不好意思。

嗯嗯,待您修复BUG后,欢迎继续讨论~