Cuda互斥,为什么死锁?

Cuda互斥,为什么死锁?,cuda,Cuda,我正在尝试实现一个基于原子的互斥 我成功了,但我有一个关于扭曲/僵局的问题 这段代码运行良好 bool blocked = true; while(blocked) { if(0 == atomicCAS(&mLock, 0, 1)) { index = mSize++; doCriticJob(); atomicExch(&mLock, 0); blocked = false; } } 但是

我正在尝试实现一个基于原子的互斥

我成功了,但我有一个关于扭曲/僵局的问题

这段代码运行良好

bool blocked = true;

while(blocked) {
    if(0 == atomicCAS(&mLock, 0, 1)) {
        index = mSize++;

        doCriticJob();

        atomicExch(&mLock, 0);
        blocked = false;
    }
}
但是这个不

while(true) {
    if(0 == atomicCAS(&mLock, 0, 1)) {
        index = mSize++;

        doCriticJob();

        atomicExch(&mLock, 0);
        break;
    }
}
我认为这是一个退出循环的位置。在第一个循环中,退出发生在条件为的位置,在第二个循环中,退出发生在if的末尾,因此线程等待其他扭曲完成循环,但其他线程也等待第一个线程。。。但我认为我错了,所以如果你能解释我:)

谢谢

  • 这里还有其他关于互斥体的问题。你可能想看看其中的一些。例如,搜索“cuda关键部分”

  • 假设一个可以工作,另一个不能,因为它似乎对您的测试用例有效,这是危险的。管理互斥体或关键部分,特别是当协商在同一个扭曲中的线程之间时,是出了名的困难和脆弱。一般的建议是避免这样做。如其他地方所讨论的,如果必须使用互斥体或关键部分,请在threadblock中使用单个线程协商任何需要它的线程,然后使用线程块内同步机制(如
    \u syncthreads()
    )控制threadblock内的行为

  • 如果不了解编译器对各种执行路径的排序方式,这个问题(IMO)就无法真正得到回答。因此,我们需要查看SASS代码(机器代码)。您可以使用来完成此操作,并且可能希望同时引用和。这也意味着您需要完整的代码,而不仅仅是您提供的代码片段

  • 以下是我的分析代码:

    $ cat t830.cu
    #include <stdio.h>
    
    
    __device__ int mLock = 0;
    
    __device__ void doCriticJob(){
    
    }
    
    __global__ void kernel1(){
    
      int index = 0;
      int mSize = 1;
      while(true) {
        if(0 == atomicCAS(&mLock, 0, 1)) {
            index = mSize++;
    
            doCriticJob();
    
            atomicExch(&mLock, 0);
            break;
        }
      }
    }
    
    __global__ void kernel2(){
    
      int index = 0;
      int mSize = 1;
      bool blocked = true;
    
      while(blocked) {
        if(0 == atomicCAS(&mLock, 0, 1)) {
            index = mSize++;
    
            doCriticJob();
    
            atomicExch(&mLock, 0);
            blocked = false;
        }
      }
    }
    int main(){
    
     kernel2<<<4,128>>>();
     cudaDeviceSynchronize();
    }
    
    考虑到使用任一代码的单个扭曲,所有线程都必须获得一次锁(通过atomicCAS),以便代码成功完成。无论使用哪种代码,在任何给定时间,扭曲中只有一个线程可以获得锁,为了让扭曲中的其他线程(稍后)获得锁,该线程必须有机会释放锁(通过
    atomicExch

    因此,这些实现之间的关键区别在于编译器如何针对条件分支调度
    atomicExch
    指令

    让我们考虑“死锁”代码(<代码> KNEL1)。在这种情况下,

    ATOM.E.EXCH
    指令在一条(也是唯一一条)条件分支(
    @0x18;
    )指令之后才会出现。CUDA代码中的条件分支表示可能的扭曲发散点,扭曲发散后的执行在某种程度上是未指定的,取决于机器的具体情况。但是考虑到这种不确定性,获取锁的线程可能会在执行
    atomicExch
    指令之前等待其他线程完成它们的分支,这意味着其他线程将没有机会获取锁,因此我们出现了死锁

    然后,如果我们将其与“工作”代码进行比较,就会发现一旦发出
    ATOM.E.CAS
    指令,该点与发出
    ATOM.E.EXCH
    指令的点之间就没有条件分支,因此释放了刚刚获得的锁。由于获得锁的每个线程(通过
    ATOM.E.CAS
    )都会在任何条件分支发生之前释放锁(通过
    ATOM.E.EXCH
    ),因此(给定此代码实现)不可能发生以前(使用
    kernel1
    )看到的那种死锁

    @P0
    是一种谓词形式,您可以在PTX参考中阅读它,了解它如何导致条件分支。)


    <强>注:< /强>我认为这两个代码都是危险的,可能有缺陷。尽管当前的测试似乎没有发现“工作”代码的问题,但我认为未来的CUDA编译器可能会选择不同的调度方式,并破坏代码。在这里,为不同的机器体系结构编译可能会产生不同的代码。我认为一种机制更健壮,这完全避免了经线争用。然而,即使这样的机制也可能导致线程块间死锁。任何互斥都必须在特定的编程和使用限制下使用。

    非常感谢您的解释。我看到了你的方法,但是我有一个没有volatile的死锁,volatile能修复这个死锁吗?还是只对只有一个线程的块起作用?我需要这里的所有线程,因为它用于光子跟踪部分:)。非常感谢:)。我描述的方法仅用于每个块的一个线程获取锁。一旦threadblock获得锁,它并不排除使用多个线程的可能性。当它说“在这里开始关键部分”时,所有线程都将在该点参与。再说一次,如果你想让同一个扭曲中的多个线程竞争锁,如果你愿意,欢迎你尝试,但在我看来,这是困难的,而且可能是危险的。好的,非常感谢所有情况:)。您帮助我了解了如何调试程序很多:)。下次遇到问题时,我将阅读汇编:)。
    $ cuobjdump -sass ./t830
    
    Fatbin elf code:
    ================
    arch = sm_20
    code version = [1,7]
    producer = <unknown>
    host = linux
    compile_size = 64bit
    
            code for sm_20
    
    Fatbin elf code:
    ================
    arch = sm_20
    code version = [1,7]
    producer = cuda
    host = linux
    compile_size = 64bit
    
            code for sm_20
                    Function : _Z7kernel1v
            .headerflags    @"EF_CUDA_SM20 EF_CUDA_PTX_SM(EF_CUDA_SM20)"
            /*0000*/         MOV R1, c[0x1][0x100];            /* 0x2800440400005de4 */
            /*0008*/         MOV32I R4, 0x1;                   /* 0x1800000004011de2 */
            /*0010*/         SSY 0x48;                         /* 0x60000000c0000007 */
            /*0018*/         MOV R2, c[0xe][0x0];              /* 0x2800780000009de4 */
            /*0020*/         MOV R3, c[0xe][0x4];              /* 0x280078001000dde4 */
            /*0028*/         ATOM.E.CAS R0, [R2], RZ, R4;      /* 0x54080000002fdd25 */
            /*0030*/         ISETP.NE.AND P0, PT, R0, RZ, PT;  /* 0x1a8e0000fc01dc23 */
            /*0038*/     @P0 BRA 0x18;                         /* 0x4003ffff600001e7 */
            /*0040*/         NOP.S;                            /* 0x4000000000001df4 */
            /*0048*/         ATOM.E.EXCH RZ, [R2], RZ;         /* 0x547ff800002fdd05 */
            /*0050*/         EXIT;                             /* 0x8000000000001de7 */
                    ............................
    
    
                    Function : _Z7kernel2v
            .headerflags    @"EF_CUDA_SM20 EF_CUDA_PTX_SM(EF_CUDA_SM20)"
            /*0000*/         MOV R1, c[0x1][0x100];            /* 0x2800440400005de4 */
            /*0008*/         MOV32I R0, 0x1;                   /* 0x1800000004001de2 */
            /*0010*/         MOV32I R3, 0x1;                   /* 0x180000000400dde2 */
            /*0018*/         MOV R4, c[0xe][0x0];              /* 0x2800780000011de4 */
            /*0020*/         MOV R5, c[0xe][0x4];              /* 0x2800780010015de4 */
            /*0028*/         ATOM.E.CAS R2, [R4], RZ, R3;      /* 0x54061000004fdd25 */
            /*0030*/         ISETP.NE.AND P1, PT, R2, RZ, PT;  /* 0x1a8e0000fc23dc23 */
            /*0038*/    @!P1 MOV R0, RZ;                       /* 0x28000000fc0025e4 */
            /*0040*/    @!P1 ATOM.E.EXCH RZ, [R4], RZ;         /* 0x547ff800004fe505 */
            /*0048*/         LOP.AND R2, R0, 0xff;             /* 0x6800c003fc009c03 */
            /*0050*/         I2I.S32.S16 R2, R2;               /* 0x1c00000008a09e84 */
            /*0058*/         ISETP.NE.AND P0, PT, R2, RZ, PT;  /* 0x1a8e0000fc21dc23 */
            /*0060*/     @P0 BRA 0x18;                         /* 0x4003fffec00001e7 */
            /*0068*/         EXIT;                             /* 0x8000000000001de7 */
                    ............................
    
    
    
    Fatbin ptx code:
    ================
    arch = sm_20
    code version = [4,2]
    producer = cuda
    host = linux
    compile_size = 64bit
    compressed
    $