对于我正在编写的教程,我正在寻找一个“现实的”简单的例子,说明因无知SIMT / SIMD而造成的死锁。
我提出了这个片段,这似乎是一个很好的例子。
任何意见都会受到赞赏。
…
int x = threadID / 2;
if (threadID > x) {
value[threadID] = 42;
barrier();
}
else {
value2[threadID/2] = 13
barrier();
}
result = value[threadID/2] + value2[threadID/2];
我知道,它既不适合CUDA C也不适合OpenCL C.
答案 0 :(得分:7)
新手CUDA程序员实际上很容易捕获的简单死锁是当一个人尝试为单个线程实现一个关键部分时,最终应该由所有线程执行。它或多或少像这样:
__global__ kernel() {
__shared__ int semaphore;
semaphore=0;
__syncthreads();
while (true) {
int prev=atomicCAS(&semaphore,0,1);
if (prev==0) {
//critical section
semaphore=0;
break;
}
}
}
atomicCAS
指令确保exaclty一个线程获得0分配给prev,而所有其他线程获得1.当一个线程完成其临界区时,它将信号量设置回0,以便其他线程有机会进入临界区。
问题是,当1个线程获得prev = 0时,属于相同SIMD单元的31个线程获得值1.在if语句处,CUDA调度程序将该单个线程置于保持状态(将其屏蔽掉)并且让其他31个线程继续他们的工作。在正常情况下,这是一个很好的策略,但在这种特殊情况下,你最终会得到一个永不执行的关键部分线程和31个等待无穷大的线程。死锁。
另请注意,break
的存在导致控制流超出while
循环。如果你省略break指令并在if-block之后有一些应该由所有线程执行的操作,那么它实际上可以帮助调度程序避免死锁。
关于问题中给出的示例:在CUDA中,明确禁止将__syncthreads()
放入SIMD分歧代码中。编译器不会捕获它,但手册中说的是“未定义的行为”。实际上,在前费米器件上,所有__syncthreads()
被视为相同的障碍。有了这个假设,你的代码实际上会终止而不会出错。但是,不应该依赖此行为。