【问题标题】:How can I implement a custom atomic function involving several variables?如何实现涉及多个变量的自定义原子函数?
【发布时间】:2013-06-28 23:43:42
【问题描述】:

我想在 CUDA 中实现这个原子函数:

__device__ float lowest;   // global var
__device__ int  lowIdx;    // global var
float realNum;   // thread reg var
int index;       // thread reg var

if(realNum < lowest) {
 lowest= realNum;  // the new lowest
 lowIdx= index;    // update the 'low' index
}

我不相信我可以用任何原子函数来做到这一点。我需要为几个指令锁定几个全局内存位置。 我可以用 PTXAS(汇编)代码来实现吗?

【问题讨论】:

  • 我认为没有办法(PTX 或其他方式)使用任何特定的 GPU 硬件一次以原子方式更新多个位置。其他人可能有一个聪明的主意。通常我认为这种类型的问题可以使用“临界区”方法来解决,您可能想使用右上角的搜索框搜索“cuda 临界区”并查看其中一些问题中描述的内容.看来您可能希望在每个线程的基础上运行它,并且每个线程的关键部分管理可能非常危险/困难。
  • 实际上,对于这种有限的情况,您只有两个要管理的 32 位数量,可以创建一个自定义原子函数,可能围绕 atomicCAS 构建,使用64 位数量(通过巧妙地组合两个 32 位数量),可能与文档中给出的 arbitrary atomic example 的行一致。

标签: cuda atomic gpu-atomics ptxas


【解决方案1】:

正如我在上面的第二条评论中所说,可以将您的两个 32 位数量组合成一个 64 位原子管理数量,并以这种方式处理问题。然后,我们使用arbitrary atomic example 作为粗略指南以原子方式管理 64 位数量。显然,您不能将此想法扩展到两个 32 位数量之外。这是一个例子:

#include <stdio.h>
#define DSIZE 5000
#define nTPB 256

#define cudaCheckErrors(msg) \
    do { \
        cudaError_t __err = cudaGetLastError(); \
        if (__err != cudaSuccess) { \
            fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                msg, cudaGetErrorString(__err), \
                __FILE__, __LINE__); \
            fprintf(stderr, "*** FAILED - ABORTING\n"); \
            exit(1); \
        } \
    } while (0)

typedef union  {
  float floats[2];                 // floats[0] = lowest
  int ints[2];                     // ints[1] = lowIdx
  unsigned long long int ulong;    // for atomic update
} my_atomics;

__device__ my_atomics test;

__device__ unsigned long long int my_atomicMin(unsigned long long int* address, float val1, int val2)
{
    my_atomics loc, loctest;
    loc.floats[0] = val1;
    loc.ints[1] = val2;
    loctest.ulong = *address;
    while (loctest.floats[0] >  val1) 
      loctest.ulong = atomicCAS(address, loctest.ulong,  loc.ulong);
    return loctest.ulong;
}


__global__ void min_test(const float* data)
{

    int idx = (blockDim.x * blockIdx.x) + threadIdx.x;
    if (idx < DSIZE)
      my_atomicMin(&(test.ulong), data[idx],idx);
}

int main() {

  float *d_data, *h_data;
  my_atomics my_init;
  my_init.floats[0] = 10.0f;
  my_init.ints[1] = DSIZE;

  h_data = (float *)malloc(DSIZE * sizeof(float));
  if (h_data == 0) {printf("malloc fail\n"); return 1;}
  cudaMalloc((void **)&d_data, DSIZE * sizeof(float));
  cudaCheckErrors("cm1 fail");
  // create random floats between 0 and 1
  for (int i = 0; i < DSIZE; i++) h_data[i] = rand()/(float)RAND_MAX;
  cudaMemcpy(d_data, h_data, DSIZE*sizeof(float), cudaMemcpyHostToDevice);
  cudaCheckErrors("cmcp1 fail");
  cudaMemcpyToSymbol(test, &(my_init.ulong), sizeof(unsigned long long int));
  cudaCheckErrors("cmcp2 fail");
  min_test<<<(DSIZE+nTPB-1)/nTPB, nTPB>>>(d_data);
  cudaDeviceSynchronize();
  cudaCheckErrors("kernel fail");

  cudaMemcpyFromSymbol(&(my_init.ulong), test, sizeof(unsigned long long int));
  cudaCheckErrors("cmcp3 fail");

  printf("device min result = %f\n", my_init.floats[0]);
  printf("device idx result = %d\n", my_init.ints[1]);

  float host_val = 10.0f;
  int host_idx = DSIZE;
  for (int i=0; i<DSIZE; i++)
    if (h_data[i] < host_val){
      host_val = h_data[i];
      host_idx = i;
      }

  printf("host min result = %f\n", host_val);
  printf("host idx result = %d\n", host_idx);
  return 0;
}

Here 是一个类似的例子,它对 2 个float 数量进行原子更新。

【讨论】:

    【解决方案2】:

    @Robert Crovella:好主意,但我认为功能应该稍微修改如下:

    __device__ unsigned long long int my_atomicMin(unsigned long long int* address, float val1, int val2)
    {
        my_atomics loc, loctest, old;
        loc.floats[0] = val1;
        loc.ints[1] = val2;
        loctest.ulong = *address;
        old.ulong = loctest.ulong;
        while (loctest.floats[0] > val1){
            old.ulong = loctest.ulong;
            loctest.ulong = atomicCAS(address, loctest.ulong,  loc.ulong);
        }
        return old.ulong;
    }
    

    【讨论】:

    • 我不知道为什么。似乎我们只是在函数的返回值上存在分歧。在您的情况下,返回值模式 不匹配 example given in the documentation 建立的模式,该模式返回从atomicCAS 函数返回的 最新值(假设 while循环进入)。你的品种不这样做。
    猜你喜欢
    • 2015-03-18
    • 2021-11-18
    • 1970-01-01
    • 2020-04-14
    • 2019-03-08
    • 2021-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多