我有一个代码来计算我使用 OpenMP 并行化的素数:
#pragma omp parallel for private(i,j) reduction(+:pcount) schedule(dynamic)
for (i = sqrt_limit+1; i < limit; i++)
{
check = 1;
for (j = 2; j <= sqrt_limit; j++)
{
if ( !(j&1) && (i&(j-1)) == 0 )
{
check = 0;
break;
}
if ( j&1 && i%j == 0 )
{
check = 0;
break;
}
}
if (check)
pcount++;
}
我正在尝试将它移植到 GPU,并且我想像上面的 OpenMP 示例一样减少计数。以下是我的代码,除了给出不正确的结果外,它的速度也较慢:
__global__ void sieve ( int *flags, int *o_flags, long int sqrootN, long int N)
{
long int gid = blockIdx.x*blockDim.x+threadIdx.x, tid = threadIdx.x, j;
__shared__ int s_flags[NTHREADS];
if (gid > sqrootN && gid < N)
s_flags[tid] = flags[gid];
else
return;
__syncthreads();
s_flags[tid] = 1;
for (j = 2; j <= sqrootN; j++)
{
if ( gid%j == 0 )
{
s_flags[tid] = 0;
break;
}
}
//reduce
for(unsigned int s=1; s < blockDim.x; s*=2)
{
if( tid % (2*s) == 0 )
{
s_flags[tid] += s_flags[tid + s];
}
__syncthreads();
}
//write results of this block to the global memory
if (tid == 0)
o_flags[blockIdx.x] = s_flags[0];
}
首先,我如何让这个内核快,我认为瓶颈是for循环,我不知道如何替换它。接下来,我的计数不正确。我确实更改了 '%' 运算符并注意到了一些好处。
在flags
数组中,我标记了从 2 到 sqroot(N) 的素数,在这个内核中我正在计算从 sqroot(N) 到 N 的素数,但我需要检查 {sqroot(N),N} 中的每个数字是否可被 {2,sqroot(N)} 中的素数整除。该o_flags
数组存储每个块的部分和。
编辑:按照建议,我修改了我的代码(我现在更好地理解了关于同步线程的评论);我意识到我不需要标志数组,只需要全局索引就可以了。在这一点上,我担心的是代码的缓慢(不仅仅是正确性),这可能归因于 for 循环。此外,在特定数据大小(100000)之后,内核对后续数据大小产生了不正确的结果。即使对于小于 100000 的数据大小,GPU 缩减结果也不正确(NVidia 论坛的一位成员指出,这可能是因为我的数据大小不是 2 的幂)。所以还有三个(可能是相关的)问题——
我怎样才能让这个内核更快?在我必须遍历每个 tid 的情况下使用共享内存是个好主意吗?
为什么它只对某些数据大小产生正确的结果?
我怎样才能修改减少?
__global__ void sieve ( int *o_flags, long int sqrootN, long int N ) { unsigned int gid = blockIdx.x*blockDim.x+threadIdx.x, tid = threadIdx.x; volatile __shared__ int s_flags[NTHREADS]; s_flags[tid] = 1; for (unsigned int j=2; j<=sqrootN; j++) { if ( gid % j == 0 ) s_flags[tid] = 0; } __syncthreads(); //reduce reduce(s_flags, tid, o_flags); }