【发布时间】:2020-03-27 21:44:28
【问题描述】:
很多资源都提供了 CUDA 中自旋锁的实现:
- https://devtalk.nvidia.com/default/topic/1014009/try-to-use-lock-and-unlock-in-cuda/
- Cuda Mutex, why deadlock?
- How to implement Critical Section in cuda?
- Implementing a critical section in CUDA
- https://wlandau.github.io/gpu/lectures/cudac-atomics/cudac-atomics.pdf。
它们遵循相同的模式:
- LOCK:等待lock的值从0到1的原子变化
- 做一些关键操作
- UNLOCK:通过将锁的值设置为0来释放锁
假设我们没有 warp-divergence,或者换句话说,我们不使用锁来进行 interwarp 同步。
实施步骤 1 的正确方法是什么?
一些答案建议使用atomicCAS,而其他atomicExch。两者是等价的吗?
while (0 != (atomicCAS(&lock, 0, 1))) {}
while (atomicExch(&lock, 1) != 0) {}
实施步骤 3 的正确方法是什么?
几乎所有消息来源都建议为此使用atomicExch:
atomicExch(&lock, 0);
一位用户提出了一个替代方案 (Implementing a critical section in CUDA),这也很有意义,但对他不起作用(因此可能会导致 CUDA 中的未定义行为):
lock = 0;
似乎对于 CPU 上的一般自旋锁,这样做是有效的:https://stackoverflow.com/a/7007893/8044236。为什么我们不能在 CUDA 中使用它?
我们是否必须在第 2 步中使用内存栅栏和 volatile 说明符进行内存访问?
关于原子的 CUDA 文档 (https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#atomic-functions) 说他们不保证排序约束:
原子函数不充当内存栅栏,也不意味着内存操作的同步或排序约束
这是否意味着我们必须在临界区 (2) 的末尾使用内存栅栏来确保临界区 (2) 内的更改在解锁 (3) 之前对其他线程可见?
CUDA 是否保证其他线程会看到在步骤 (1) 和 (3) 中具有原子操作的线程所做的更改?
这不适用于内存栅栏 (https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#memory-fence-functions):
内存栅栏函数只影响线程内存操作的顺序;它们不确保这些内存操作对其他线程可见(就像 __syncthreads() 对块内的线程所做的那样(请参阅同步函数))。
所以原子操作可能也不是这样?如果是,CUDA 中的所有自旋锁实现都依赖于 UB。
我们如何在存在 warp 的情况下实现可靠的自旋锁?
现在,如果我们对上述所有问题都有答案,那么让我们移除我们没有翘曲发散的假设。这种情况下是否可以实现自旋锁?
主要问题(死锁)在https://wlandau.github.io/gpu/lectures/cudac-atomics/cudac-atomics.pdf 的幻灯片 30 中表示:
是否是在步骤 (1) 中将 while 循环替换为 if 并将所有 3 个步骤包含在单个 while 循环中的唯一选项,例如,在 Thread/warp local lock in cuda 或 CUDA, mutex and atomicCAS() 中?
【问题讨论】:
-
我认为this mechanism 相当强大。
标签: cuda