我正在编写用于计算前缀和的代码。这是我的内核
__global__ void prescan(int *indata,int *outdata,int n,long int *sums)
{
extern __shared__ int temp[];
int tid=threadIdx.x;
int offset=1,start_id,end_id;
int *global_sum=&temp[n+2];
if(tid==0)
{
temp[n]=blockDim.x*blockIdx.x;
temp[n+1]=blockDim.x*(blockIdx.x+1)-1;
start_id=temp[n];
end_id=temp[n+1];
//cuPrintf("Value of start %d and end %d\n",start_id,end_id);
}
__syncthreads();
start_id=temp[n];
end_id=temp[n+1];
temp[tid]=indata[start_id+tid];
temp[tid+1]=indata[start_id+tid+1];
for(int d=n>>1;d>0;d>>=1)
{
__syncthreads();
if(tid<d)
{
int ai=offset*(2*tid+1)-1;
int bi=offset*(2*tid+2)-1;
temp[bi]+=temp[ai];
}
offset*=2;
}
if(tid==0)
{
sums[blockIdx.x]=temp[n-1];
temp[n-1]=0;
cuPrintf("sums %d\n",sums[blockIdx.x]);
}
for(int d=1;d<n;d*=2)
{
offset>>=1;
__syncthreads();
if(tid<d)
{
int ai=offset*(2*tid+1)-1;
int bi=offset*(2*tid+2)-1;
int t=temp[ai];
temp[ai]=temp[bi];
temp[bi]+=t;
}
}
__syncthreads();
if(tid==0)
{
outdata[start_id]=0;
}
__threadfence_block();
__syncthreads();
outdata[start_id+tid]=temp[tid];
outdata[start_id+tid+1]=temp[tid+1];
__syncthreads();
if(tid==0)
{
temp[0]=0;
outdata[start_id]=0;
}
__threadfence_block();
__syncthreads();
if(blockIdx.x==0 && threadIdx.x==0)
{
for(int i=1;i<gridDim.x;i++)
{
sums[i]=sums[i]+sums[i-1];
}
}
__syncthreads();
__threadfence();
if(blockIdx.x==0 && threadIdx.x==0)
{
for(int i=0;i<gridDim.x;i++)
{
cuPrintf("****sums[%d]=%d ",i,sums[i]);
}
}
__syncthreads();
__threadfence();
if(blockIdx.x!=gridDim.x-1)
{
int tid=(blockIdx.x+1)*blockDim.x+threadIdx.x;
if(threadIdx.x==0)
cuPrintf("Adding %d \n",sums[blockIdx.x]);
outdata[tid]+=sums[blockIdx.x];
}
__syncthreads();
}
在上面的内核中,sums数组将累加每个块的前缀和,然后第一个线程将计算该sum数组的前缀和。现在如果我从设备端打印这个sum数组,它将在
中显示正确的结果cuPrintf(“添加%d \ n”,sums [blockIdx.x]);
这一行显示它正在取旧值。可能是什么原因?
答案 0 :(得分:2)
您的代码不是多块前缀和的有效实现。您尝试使用块0的单个线程来计算部分块总和的前缀和,然后保证这些部分块总和已写入内存。 __syncthreads()
仅在一个块中同步线程,而不是跨块。所以在这段代码中:
__threadfence_block();
__syncthreads();
if(blockIdx.x==0 && threadIdx.x==0)
{
for(int i=1;i<gridDim.x;i++)
{
sums[i]=sums[i]+sums[i-1];
}
}
在块0执行此代码之前,不保证所有块都计算其总和[blockIdx.x]。实际上,如果您启动的块数多于可以同时在设备上运行的块,则无法保证在您访问此代码时,所有块都已启动。
要使此代码正确,您需要在此代码之前结束内核,并启动一个新内核来计算块前缀和结果,另一个用于将结果添加到每个线程块。