在CUDA内核中按顺序递增计数器?

时间:2016-02-18 10:14:44

标签: cuda counter

我无法弄清楚这个逻辑,所以我有一个计数器SOLUTIONS,每次我为每个线程提供一个新解决方案时,它会以原子方式递增,但在某些情况下,单个线程可以找到多个解决方案,但是在这种情况下,我希望它再次增加当前值。这个逻辑似乎很简单,由atomicAdd实现。但问题是,我需要将该计数器用作数组的索引。

示例:

SOME_ARRAY[tid] = STUFF; // puts stuff in individual indexes because of thread id.
atomicAdd(SOLUTIONS, 1); // increments the max solutions found.

但现在单个线程找到了两个解决方案,我希望它在当前MAX解决方案之后递增。

SOME_ARRAY[SOLUTIONS+1] = STUFF; 

但据我了解,如果两个或多个线程试图这样做,它将无法正常工作?我需要第二个线程在SOLUTIONS + 2而不是写相同的位置。

我怎样才能做到这一点?任何帮助表示赞赏。

编辑:我尝试实现,这仍然不起作用,因为当我printf偏移时,两个线程打印相同的数字。当我访问d_PROGRESS数组2时,元素始终为85,这是一个我从未放在那里的随机数。

 __device__
 bool isSafe (int row, int col, int cmp_row, int cmp_col) {
    if ((col - cmp_col) == 0) {return 0;}
    if (abs(row - cmp_row) == abs(col - cmp_col)) {return 0;}
    return 1;
 }

 __global__
  void nqueensKernel(int row, int n, bool one, bool all, int pitch,
               Solution * d_solution,
               Solution * d_PROGRESS,
               Solution *d_PROGRESS_OUT,
               unsigned long long * NUM_THREADS,
               unsigned long long * NUM_SOLUTIONS) {

int index = threadIdx.x + blockIdx.x * blockDim.x;
int isAlone = 0;
if (index == 0) {*NUM_THREADS = 0;}
__syncthreads();
if (row == 0) {
  d_PROGRESS_OUT[index * n + row] = index;
  atomicAdd(NUM_THREADS, 1);
}
else {
  int moresolutions = 0;
  for (int col = 0; col < n; col++) {
    for (int k = 0; k < row; k++) {
      int checkcol = d_PROGRESS[index * n + k];
      isAlone = isSafe(row, col, k, checkcol);
      if (!isAlone) { /*printf("Is Alone? %d\n", isAlone);*/ break; }
    }
    __syncthreads();
    if ( isAlone ) {
      moresolutions++;
      if (moresolutions == 1) {
          d_PROGRESS_OUT[(index * n) + row] = col;
          atomicAdd(NUM_THREADS, 1);
          for (int o = 0; o < row; o++) {
            d_PROGRESS_OUT[(index * n) + o] = d_PROGRESS[(index * n) + o];
          }
      } else if (moresolutions > 1) {
          int offset = atomicAdd(NUM_THREADS, 1);
          d_PROGRESS_OUT[((offset+1) * n) + row] = col;
          for (int m = 0; m < row; m++) { d_PROGRESS_OUT[((offset+1) * n) + m] = d_PROGRESS[(index * n) + m]; }
      }
      if (row == n-1) { atomicAdd(NUM_SOLUTIONS, 1); }
    }
  }
}

1 个答案:

答案 0 :(得分:1)

您希望使用SOME_ARRAY返回的值保留atomicAdd中的存储空间,这是之前存储在变量中的添加,并通过要保留的插槽数递增原子变量。例如:

int offset = atomicAdd(SOLUTIONS, number_of_solutions_found_by_this_thread);
SOME_ARRAY[offset] = stuff;
SOME_ARRAY[offset + 1] = more stuff;
...
SOME_ARRAY[offset + number_of_solutions_found_by_this_thread - 1] = also more stuff;

假设SOLUTIONS最初为0。

  1. 某个帖子(我们深情地称之为#27)找到4个解决方案,然后执行atomicAdd。结果为0,SOLUTIONS现为4。
  2. 在#27到达下一行之前,线程#88获取原子添加并将SOLUTIONS增加9.结果返回4SOLUTIONS现在为13
  3. #27继续将其解决方案从索引0存储到3,不干扰#88。
  4. 这是许多并行算法中的常见模式,需要跨线程聚合可变长度结果。