【发布时间】:2014-11-06 21:41:59
【问题描述】:
编辑:我意识到,不幸的是,我忽略了第一个示例代码中 while 语句末尾的分号,并自己误解了它。所以实际上对于threadIdx.x != s 的线程有一个空循环,在该循环之后有一个收敛点,并且在这一点上等待所有其他线程而不增加s 变量。我将下面的原始(未更正)问题留给任何对此感兴趣的人。请注意,在第一个示例中,第二行末尾缺少一个分号,因此 s++ 与循环体没有任何共同之处。
--
我们在 CUDA 课上学习序列化,我们的老师告诉我们这样的代码:
__shared__ int s = 0;
while (s != threadIdx.x)
s++; // serialized code
最终会导致硬件死锁,因为 nvcc 编译器在 while (s != threadIdx.x) 和 s++ 语句之间放置了一个重新收敛点。如果我理解正确,这意味着一旦一个线程达到再收敛点,该线程就会停止执行并等待其他线程,直到它们也到达该点。然而,在这个例子中,这永远不会发生,因为线程 #0 进入了 while 循环的主体,在没有增加 s 变量的情况下到达了重新收敛点,并且其他线程陷入了无限循环。
一个可行的解决方案应该如下:
__shared__ int s = 0;
while (s < blockDim.x)
if (threadIdx.x == s)
s++; // serialized code
这里,一个块中的所有线程都进入循环体,都评估条件,只有线程#0在第一次迭代中增加s变量(循环继续)。
我的问题是,如果第一个示例挂起,为什么第二个示例有效?更具体地说,if 语句只是另一个分歧点,就汇编语言而言,应该编译成与循环中的条件相同的条件跳转指令。那么为什么在第二个示例中s++ 之前没有任何重新收敛点,而实际上它在语句之后立即消失了呢?
在其他来源中,我只发现每个分支独立计算不同的代码 - 例如在if/else 语句中,首先计算if 分支,将所有else 分支线程屏蔽在同一个warp 中,然后其他线程在第一次等待时计算else 分支。在 if/else 语句之后有一个再收敛点。那么为什么第一个示例冻结,没有将循环分成两个分支(一个线程的true 分支和一个等待中的所有其他线程的false 分支)?
谢谢。
【问题讨论】:
标签: serialization cuda