CUDA中自旋锁的正确实现

CUDA中自旋锁的正确实现,cuda,Cuda,很多资料都提供了CUDA中自旋锁的实现: 它们遵循相同的模式: LOCK:等待LOCK值从0到1的原子更改 做一些关键的操作 解锁:通过将其值设置为0来释放锁定 让我们假设我们没有翘曲发散,或者换句话说,我们没有使用锁进行翘曲间同步 实施步骤1的正确方法是什么? 一些答案建议使用atomicCAS,而另一些答案建议使用atomicExch。两者是否相等 while (0 != (atomicCAS(&lock, 0, 1))) {} while (atomicExch(&a

很多资料都提供了CUDA中自旋锁的实现:

它们遵循相同的模式:

  • LOCK:等待LOCK值从0到1的原子更改
  • 做一些关键的操作
  • 解锁:通过将其值设置为0来释放锁定
  • 让我们假设我们没有翘曲发散,或者换句话说,我们没有使用锁进行翘曲间同步

    实施步骤1的正确方法是什么? 一些答案建议使用
    atomicCAS
    ,而另一些答案建议使用
    atomicExch
    。两者是否相等

    while (0 != (atomicCAS(&lock, 0, 1))) {}
    while (atomicExch(&lock, 1) != 0) {}
    
    实施步骤3的正确方法是什么? 几乎所有来源都建议使用atomicExch:

    atomicExch(&lock, 0);
    
    一位用户提出了一个备选方案(),这个方案也有意义,但对他不起作用(因此可能会导致CUDA中未定义的行为):

    似乎对于CPU上的常规自旋锁,这样做是有效的:。为什么我们不能在CUDA中使用它

    在步骤2中,我们是否必须使用内存围栏和
    volatile
    说明符进行内存访问? CUDA关于atomics()的文档说,它们不能保证排序约束:

    原子函数不充当内存围栏,也不意味着内存操作的同步或排序约束

    这是否意味着我们必须在临界段(2)的末端使用内存围栏,以确保在解锁(3)之前,临界段(2)内的更改对其他线程可见

    CUDA是否保证其他线程会在步骤(1)和(3)中看到原子操作线程所做的更改? 对于内存围栏(),情况并非如此:

    内存栅栏函数只影响线程对内存操作的排序;它们不能确保这些内存操作对其他线程可见(就像uu syncthreads()对块内的线程可见(请参见同步函数))

    那么,对于原子操作来说,这可能也是不正确的?如果是,CUDA中的所有自旋锁实现都依赖于UB

    如何在存在翘曲的情况下实现可靠的自旋锁? 现在,假设我们对上面所有的问题都有答案,让我们去掉不存在扭曲散度的假设。在这种情况下可以实现自旋锁吗

    主要问题(死锁)如幻灯片30所示:

    在步骤(1)中,用
    if
    替换
    while
    循环,并按照建议将所有3个步骤包含在单个
    while
    循环中(例如,在或中)是唯一的选项吗

    实施步骤1的正确方法是什么?一些答案建议使用
    atomicCAS
    而其他
    atomicExch
    。两者是否相等

    while (0 != (atomicCAS(&lock, 0, 1))) {}
    while (atomicExch(&lock, 1) != 0) {}
    
    不,它们不是,只有
    atomicCas
    是正确的。该代码的要点是检查给定线程将锁的状态从unlocked更改为locked是否有效。
    atomicExch
    版本不会执行此操作,因为它不会在执行分配之前检查初始状态是否已解锁

    似乎对于CPU上的常规自旋锁,这样做是有效的:。为什么我们不能在CUDA中使用它


    如果您阅读该答案上的注释,您将看到它在CPU上也无效

    在步骤2中,我们是否必须使用内存围栏和易失性说明符进行内存访问

    这完全取决于您的最终用途,以及您为什么首先使用关键部分。显然,如果您希望给定线程对全局可见内存的操作是全局可见的,那么您需要一个围栏或一个原子事务来做到这一点,并且您需要确保编译器不会将值缓存在寄存器中

    [sic]CUDA是否保证其他线程会看到在步骤(1)和(3)中使用原子操作的线程所做的更改

    是的,但仅适用于以原子方式执行的其他操作。原子操作意味着对给定地址执行的所有内存事务进行序列化,当线程执行操作时,它们返回地址的前一个状态

    如何在存在翘曲的情况下实现可靠的自旋锁


    翘曲发散是不相关的。原子内存操作的序列化意味着当来自同一个扭曲的多个线程尝试获取锁时扭曲发散

    您说“atomicExch在执行赋值之前不会检查初始状态是否已解锁”,但实际上如果它未解锁,则会赋值1(已锁定),从而导致无操作,所以在某种意义上,它会检查“如果你读了答案上的注释,你会发现它在CPU上也无效。”你能指出确切的注释吗?我看不出有人明确地说,
    lock=false;//释放锁
    不正确“扭曲发散不相关”我对问题添加了解释。扭曲分歧导致死锁所有您添加的关于死锁的信息都是无关的。你能解释一下死锁有什么问题吗?我是否误解了幻灯片和其他答案(例如),或者这些幻灯片是错误的?我认为是相当健壮的。