Cuda原子和条件分支

时间:2017-08-30 00:04:02

标签: c++ algorithm cuda atomic compare-and-swap

我正在尝试编写CUDA版本的serial代码,作为在分子动力学算法中实现周期性边界条件的一部分。我们的想法是,一小部分具有开箱即用位置的粒子需要使用两个ways中的一个来重新使用,并限制我使用第一种方式的次数。

基本上,它归结为以下MWE。我有一个数组x[N] N很大,以及以下serial代码。

#include <cstdlib>

int main()
{
  int N =30000;
  double x[30000];
  int Nmax = 10, count = 0;

  for(int i = 0; i < N; i++)
    x[i] = 1.0*(rand()%3);

  for(int i = 0; i < N; i++)
   {
      if(x[i] > 2.9)
        {
          if(count < Nmax)
            {
              x[i] += 0.1; //first way
              count++;
            }
          else
            x[i] -= 0.2; //second way
        }
    }
}

请假设x[i] > 2.9仅适用于x[i]的30000个元素中的一小部分(约12-15个)。

请注意i的序列并不重要,即没有必要让10最低i使用x[i] += 0.1,这使得算法可能具有可并行性。我想到了MWE的以下CUDA版本,该版本使用nvcc -arch sm_35 main.cu进行编译,其中main.cu读取为

#include <cstdlib>

__global__ void PeriodicCondition(double *x, int *N, int *Nmax, int *count)
{
  int i = threadIdx.x+blockIdx.x*blockDim.x;
  if(i < N[0])
    {
      if(x[i] > 2.9)
        {
           if(count[0] < Nmax[0]) //===============(line a)
             {
               x[i] += 0.1; //first way
               atomicAdd(&count[0],1); //========(line b)
             }
           else
             x[i] -= 0.2; //second way
        }
    }
}

int main()
{
  int N = 30000;
  double x[30000];
  int Nmax = 10, count = 0;

  srand(128512);
  for(int i = 0; i < N; i++)
    x[i] = 1.0*(rand()%3);

  double *xD;
  cudaMalloc( (void**) &xD, N*sizeof(double) );
  cudaMemcpy( xD, &x, N*sizeof(double),cudaMemcpyHostToDevice );

  int *countD;
  cudaMalloc( (void**) &countD, sizeof(int) );
  cudaMemcpy( countD, &count, sizeof(int),cudaMemcpyHostToDevice );

  int *ND;
  cudaMalloc( (void**) &ND, sizeof(int) );
  cudaMemcpy( ND, &N, sizeof(int),cudaMemcpyHostToDevice );

  int *NmaxD;
  cudaMalloc( (void**) &NmaxD, sizeof(int) );
  cudaMemcpy( NmaxD, &Nmax, sizeof(int),cudaMemcpyHostToDevice );

  PeriodicCondition<<<938,32>>>(xD, ND, NmaxD, countD);

  cudaFree(NmaxD);
  cudaFree(ND);
  cudaFree(countD);
  cudaFree(xD);

}

当然,这是不正确的,因为if上的(line a)条件使用了(line b)中更新的变量,该变量可能不是最新的。这有点类似于Cuda atomics change flag,但是,我不确定使用关键部分是否以及如何使用会有所帮助。

当每个线程检查count[0]上的if条件时,是否有办法确保(line a)是最新的,而不会使代码过于串行?

1 个答案:

答案 0 :(得分:4)

每次只增加原子计数器,并在测试中使用return value

...
  if(x[i] > 2.9)
    {
       int oldCount = atomicAdd(&count[0],1);
       if(oldCount < Nmax[0])
         x[i] += 0.1; //first way
       else
         x[i] -= 0.2; //second way
    }
...

如果如你所说,大约15个项目超过2.9且Nmax大约为10,那么将会有少量的&#34;额外的&#34;原子操作,其开销可能很小(我无法看到如何更有效地完成它,这并不是说它不可能......)。