CUDA,有没有atomicRead?

时间:2016-03-19 12:32:32

标签: c++ cuda

我正在开发一个CUDA程序,其中所有块和线程需要动态确定迭代问题的最小步长。我希望块中的第一个线程负责将全局dz值读入共享内存,以便其余线程可以对其进行减少。与此同时,其他块中的其他线程可能正在写入其中。在CUDA中只有一个atomicRead选项或类似的东西。我想我可以用零或其他东西进行原子添加。或者这甚至是必要的吗?

template<typename IndexOfRefractionFunct>
    __global__ void _step_size_kernel(IndexOfRefractionFunct n, double* dz, double z, double cell_size)
    {
        int idx = blockIdx.x * blockDim.x + threadIdx.x;
        if(idx >= cells * cells)
            return;

        int idy = idx / cells;
        idx %= cells;

        double x = cell_size * idx;
        double y = cell_size * idy;

        __shared__ double current_dz;
        if(threadIdx.x == 0)
            current_dz = atomicRead(dz);

        ...

        atomicMin(dz, calculated_min);
    }

我也意识到cuda似乎不支持双打原子。有什么方法吗?

1 个答案:

答案 0 :(得分:3)

  

CUDA中只有一个atomicRead选项或类似的东西。

atomic操作的想法是它允许组合多个操作,而不可能干预其他线程的操作。规范用法是用于读 - 修改 - 写。 RMW操作的所有3个步骤都可以相对于内存中的给定位置以原子方式执行,而不可能干预其他线程的活动。

因此,原子读取(仅本身)的概念在这种情况下并没有真正意义。这只是一个操作。在CUDA中,基本类型(intfloatdouble等)的所有正确对齐读取都以原子方式发生,即一次性操作,而不会影响读取的其他操作或者读取的部分内容。

根据您所显示的内容,似乎应该满足您的用例的正确性,而不会对读取操作产生任何特殊行为。如果您只是想确保从全局值填充current_dz值,在任何线程有机会修改它之前,在块级别,可以使用__syncthreads()简单地对其进行整理:< / p>

    __shared__ double current_dz;
    if(threadIdx.x == 0)
        current_dz = dz;
    __syncthreads(); // no threads can proceed beyond this point until
                     // thread 0 has read the value of dz

    ...

    atomicMin(dz, calculated_min);

如果你需要确保在网格范围内强制执行此行为,那么我的建议是获得线程写入的dz的初始值,然后是atomicMin操作在另一个位置完成(即在内核级别将读/输出与写/输出分开)。

但是,我并不是说这对你的用例是必要的。如果您只想获取当前 dz值,则可以使用普通读取来执行此操作。您将获得“连贯”的价值。在网格级别,在读取之前可能发生了一些atomicMin个操作,有些操作可能在读取之后发生,但是没有一个操作会损坏读取,导致您读取一个虚假的价值。您读取的值将是那里的初始值,或者是atomicMin操作正确存放的某个值(基于您显示的代码)。

  

我也意识到cuda似乎不支持双打原子。有什么方法吗?

CUDA支持64位数量的有限原子操作集。特别是,有一个64位atomicCAS操作。 programming guide演示了如何在自定义函数中使用它来实现任意64位原子操作(例如atomicMin数量上的64位double。编程指南中的示例介绍了如何执行double atomicAdd操作。以下是在atomicMin上运行atomicMaxdouble的示例:

__device__ double atomicMax(double* address, double val)
{
  unsigned long long int* address_as_ull =(unsigned long long int*)address;
  unsigned long long int old = *address_as_ull, assumed;

  while(val > __longlong_as_double(old) ) {
    assumed = old;
    old = atomicCAS(address_as_ull, assumed, __double_as_longlong(val));
  }

  return __longlong_as_double(old);
}
__device__ double atomicMin(double* address, double val)
{
  unsigned long long int* address_as_ull =(unsigned long long int*)address;
  unsigned long long int old = *address_as_ull, assumed;

  while(val < __longlong_as_double(old) ) {
    assumed = old;
    old = atomicCAS(address_as_ull, assumed, __double_as_longlong(val));
  }

  return __longlong_as_double(old);
}

作为一种优秀的编程实践,尽管开普勒全局32位原子非常快,但应该谨慎使用原子。但是当使用这些类型的自定义64位原子时,建议特别适用;它们将比普通的读写速度明显慢。