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的末尾,因此线程等待其他扭曲完成循环,但其他线程也等待第一个线程。。。但我认为我错了,所以如果你能解释我:)
谢谢
\u syncthreads()
)控制threadblock内的行为$ 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
$