我正在开发一个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似乎不支持双打原子。有什么方法吗?
答案 0 :(得分:3)
CUDA中只有一个atomicRead选项或类似的东西。
atomic
操作的想法是它允许组合多个操作,而不可能干预其他线程的操作。规范用法是用于读 - 修改 - 写。 RMW操作的所有3个步骤都可以相对于内存中的给定位置以原子方式执行,而不可能干预其他线程的活动。
因此,原子读取(仅本身)的概念在这种情况下并没有真正意义。这只是一个操作。在CUDA中,基本类型(int
,float
,double
等)的所有正确对齐读取都以原子方式发生,即一次性操作,而不会影响读取的其他操作或者读取的部分内容。
根据您所显示的内容,似乎应该满足您的用例的正确性,而不会对读取操作产生任何特殊行为。如果您只是想确保从全局值填充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
上运行atomicMax
和double
的示例:
__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位原子时,建议特别适用;它们将比普通的读写速度明显慢。