Cuda 条件同步线程&;死锁(或不死锁)

Cuda 条件同步线程&;死锁(或不死锁),cuda,Cuda,A跟进问题:以及 根据以上链接,下面的代码应该是死锁。 请解释为什么这不是死锁。(费米上的Cuda 5) \uuuuu设备\uuuuuuu内部添加[144]; __设备输入结果; 添加();//召唤 __全局无效添加(){ 对于(idx=72>>1;idx>0;idx>>=1){ if(thrdIdx

A跟进问题:以及

根据以上链接,下面的代码应该是死锁。
请解释为什么这不是死锁。(费米上的Cuda 5)

\uuuuu设备\uuuuuuu内部添加[144];
__设备输入结果;
添加();//召唤
__全局无效添加(){
对于(idx=72>>1;idx>0;idx>>=1){
if(thrdIdx
从技术上讲,这是一个定义不清的程序

大多数(但不是全部)(例如G80不支持)NVIDIA GPU以这种方式支持提前退出,因为硬件为每个块保持活动线程计数,并且该计数用于屏障同步,而不是块的初始线程计数

因此,当到达代码中的
\uuu syncthreads()
时,硬件不会等待任何已返回的线程,并且程序运行时不会出现死锁

此样式更常见的用法是:

__global__ void foo(int n, ...) {
  int idx = threadIdx.x + blockIdx.x * blockDim.x;
  if (idx >= n) return;
  ... // do some computation with remaining threads
}
重要提示:屏障计数是按经线更新的(请参阅),而不是按线更新的。因此,您可能会遇到这样的情况,例如,只有少数(或零)线程提前返回。这意味着屏障计数不会减少。但是,只要每个经纱中至少有一条线到达屏障,它就不会死锁

所以一般来说,你需要小心使用障碍物。但具体来说,像这样的早期退出模式确实有效

编辑:针对您的具体情况

迭代Idx==36:2个活动扭曲,因此屏障出口计数为64。从扭曲0开始的所有线程都达到屏障,使计数从0增加到32。来自扭曲1的4条线到达屏障,使计数从32增加到64,扭曲0和1从屏障释放。阅读上面的链接,了解为什么会发生这种情况

迭代Idx==18:1活动扭曲,因此屏障出口计数为32。从扭曲0开始的18条线到达屏障,使计数从0增加到32。满足屏障要求,并释放扭曲0


等等

您的启动配置是什么?(例如块和网格维度)您给出的代码不会接近编译,更不用说死锁了。顺便说一句,由于不正确使用syncthreads而导致的死锁是一种可能性,而不是一种保证。处理syncthreads的不当使用的正确方法是得出这样的结论:行为是错误的。不要关注func正在做什么,而是看看它是如何做的。关于这个问题的其他问题似乎暗示“哦,你不能那样做”。根据我的经验,情况并非如此。这似乎工作可靠,而不是未定义。我正在努力理解原因,以便更好地利用它。上面使用的#开始w/2翘曲,并迅速下降到1。THRD在途中被丢弃。第二次扭曲中的THRD,都可以看到早期返回。所以当第一个经纱还在运行时,他们看不到障碍物。这似乎与您的“不会死锁”评论不符。你能详细说明一下吗?是的,活动线程计数(不提前返回的线程)和屏障计数是两种不同的。您可以从不同的角度回答:只要每个活动扭曲中至少有一个活动线程,它就不会死锁。这意味着对扭曲大小进行操作的减少也不会死锁。i、 e.在每个经纱中启动最多32次。最终,在每个经纱中,将这32个经纱减少到至少一个仍在工作的经纱。是的,“经纱同步”方法非常普遍。那个链接对我不起作用。我想是这样的:
__global__ void foo(int n, ...) {
  int idx = threadIdx.x + blockIdx.x * blockDim.x;
  if (idx >= n) return;
  ... // do some computation with remaining threads
}