CUDA中计算素数的并行归约

CUDA中计算素数的并行归约,cuda,primes,reduction,Cuda,Primes,Reduction,我有一个计算素数的代码,我已经用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++) {

我有一个计算素数的代码,我已经用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版本中存在大量的正确性问题,无论您正在实现的算法是否正确,这些问题都会阻止它正常工作:

  • \uuu syncthreads()
    调用必须是无条件的。如果分支分歧会使同一扭曲中的某些线程无法执行
    \uu syncthreads()
    调用,则编写代码是不正确的。基本的PTX是
    bar.sync
    ,PTX指南中说: 屏障是在每一个扭曲的基础上执行的,就好像一个扭曲中的所有线程一样 “扭曲”处于活动状态。因此,如果扭曲中的任何线程执行一个条 指令,就好像扭曲中的所有线程都执行了 酒吧指导。经纱中的所有线都会暂停,直到障碍物 完成,并且屏障的到达计数将按 扭曲大小(不是扭曲中活动线程的数量)。在里面 有条件执行的代码,仅当 众所周知,所有线程对条件的评估都是相同的( 翘曲不发散)。因为屏障是在每一个扭曲上执行的 在此基础上,可选线程数必须是扭曲大小的倍数
  • 在有条件地从全局内存加载一些值后,代码无条件地将
    s\u标志设置为1。这肯定不是守则的目的吗
  • 该代码在筛选代码和缩减之间缺少同步屏障,这可能导致共享内存争用和缩减结果不正确
  • 如果您计划在费米类卡上运行此代码,则应声明共享内存阵列为volatile,以防止编译器优化可能破坏共享内存减少
  • 如果您解决了这些问题,代码可能会工作。性能是一个完全不同的问题。当然,在旧硬件上,整数模运算非常非常慢,不推荐使用。我记得读过一些材料,其中指出这是一种在GPU上快速生成素代的有用方法


    我认为您提出了错误的问题,并且以错误的顺序提问-首先是“我如何才能使此代码正确工作?”,然后是“我如何才能在CUDA中有效地完成此任务?”。第二个问题的答案可能与第一个问题的答案毫无关系。是的,我确实意识到情况就是这样,这个问题是在没有深思熟虑和匆忙的情况下提出的。
    __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];
    
    }
    
    __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);
    }