如何在CUDA上应用结构的原子操作?

如何在CUDA上应用结构的原子操作?,cuda,Cuda,让结构定义如下: typedef struct S { float x; float y; } T; __device__ T struct_add(T a1, T a2) { T result; result.x = a1.x + a2.x; result.y = a1.y + a2.y; } 操作struct\u add定义如下: typedef struct S { float x; float y; } T; __devi

让结构定义如下:

typedef struct S { 
    float x;
    float y;
} T;
__device__ T struct_add(T a1, T a2) {
    T result;
    result.x = a1.x + a2.x;
    result.y = a1.y + a2.y;
}
操作
struct\u add
定义如下:

typedef struct S { 
    float x;
    float y;
} T;
__device__ T struct_add(T a1, T a2) {
    T result;
    result.x = a1.x + a2.x;
    result.y = a1.y + a2.y;
}
如果我想以原子方式应用
struct\u add
,如何在CUDA中实现这一点?例如,
a
b
c
需要使用
struct\u add
进行汇总,结果需要存储在
d
中。(其中
a
b
c
d
的类型为T)


我听说不建议通过while循环进行“锁定和访问控制”。有没有合适的方法来实现这一点?

CUDA没有提供涵盖任意结构原子更新的通用原子方法。一些可能性:

  • 因为您特别想要更新两个相邻的32位项目,所以可以使用通用的64位原子操作,这是所描述的操作的变体

  • 另一个选择是您已经提到的,基本上实现了一个

  • 最后,另一种可能的方法可能是,尽管这与原子用法并不完全类似

  • 按照上面建议1的思路,下面是对代码的一个修改,它可能指示如何使用64位原子:

    $ cat t56.cu
    #include <stdio.h>
    #define DSIZE 512
    #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];
      unsigned long long int ulong;    // for atomic update
    } my_atomics;
    
    __device__ my_atomics test;
    
    __device__ unsigned long long int my_atomicAdd_2floats(unsigned long long int* address, float val0, float val1)
    {
        my_atomics loctest;
        unsigned long long old = *address;
        do {
          loctest.ulong = old;
          my_atomics loc;
          loc.floats[0] = val0 + loctest.floats[0];
          loc.floats[1] = val1 + loctest.floats[1];
          old = atomicCAS(address, loctest.ulong,  loc.ulong);}
        while (old != loctest.ulong);
        return old;
    }
    
    
    __global__ void min_test(const float* data)
    {
    
        int idx = (blockDim.x * blockIdx.x) + threadIdx.x;
        if (idx < DSIZE)
          my_atomicAdd_2floats(&(test.ulong), data[idx], (float)idx);
    }
    
    int main() {
    
      float *d_data, *h_data;
      my_atomics my_init;
      my_init.floats[0] = 0.0f;
      my_init.floats[1] = 0.0f;
    
      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");
      for (int i = 0; i < DSIZE; i++) h_data[i] = 1.0f;
      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 float0 result = %f\n", my_init.floats[0]);
      printf("device float1 result = %f\n", my_init.floats[1]);
    
      float host_val0 = 0.0f;
      float host_val1 = 0.0f;
      for (int i=0; i<DSIZE; i++) {
              host_val0 += h_data[i];
              host_val1 += (float)(i);}
      printf("host float0 result = %f\n", host_val0);
      printf("host float1 result = %f\n", host_val1);
      return 0;
    }
    $ nvcc -arch=sm_35 -o t56 t56.cu -Wno-deprecated-gpu-targets
    $ cuda-memcheck ./t56
    ========= CUDA-MEMCHECK
    device float0 result = 512.000000
    device float1 result = 130816.000000
    host float0 result = 512.000000
    host float1 result = 130816.000000
    ========= ERROR SUMMARY: 0 errors
    $
    
    $cat t56.cu
    #包括
    #定义DSIZE512
    #定义nTPB 256
    #定义cudaCheckErrors(msg)\
    做{\
    cudaError\u t\u err=cudaGetLastError()\
    如果(_err!=cudaSuccess){\
    fprintf(标准,“致命错误:%s(%s位于%s:%d)\n”\
    msg,cudaGetErrorString(_err)\
    __文件(行)\
    fprintf(stderr,“***失败-中止\n”)\
    出口(1)\
    } \
    }而(0)
    typedef联合{
    浮动浮动[2];
    unsigned long long int ulong;//用于原子更新
    }我的原子学;
    __设备原子测试;
    __设备\uuuuu无符号长整型my_u原子添加\u2浮点(无符号长整型*地址,浮点值0,浮点值1)
    {
    我的原子学测试;
    无符号long old=*地址;
    做{
    loctest.ulong=旧的;
    我的原子学;
    loc.floats[0]=val0+loctest.floats[0];
    loc.floats[1]=val1+loctest.floats[1];
    old=atomicCAS(地址,loctest.ulong,loc.ulong);}
    while(old!=loctest.ulong);
    返老还童;
    }
    __全局无效最小值测试(常数浮点*数据)
    {
    intidx=(blockDim.x*blockIdx.x)+threadIdx.x;
    if(idx对于(int i=0;i如果结构中有浮点数[4],该如何扩展?它不能,因为4个浮点数总共是128位,CUDA硬件原子机制目前停止在64位。对于更大的结构更新,您可能需要考虑另外两个建议之一:并行缩减方案(可能最好)或者是一个关键部分。或者重新构建您的算法,使其不需要同时进行4次原子更新。如果您正在执行
    atomicAdd
    ,我不清楚您为什么需要一个“耦合”原子操作,如本文所讨论的。在我看来,您的
    浮动[4]
    case,您只需执行4个单独的
    float
    atomicAdd
    操作。