CUDA:同步线程

时间:2009-10-29 16:35:16

标签: optimization cuda synchronization

我几乎在读到有关CUDA编程的任何地方都提到了warp中所有线程都做同样事情的重要性。
在我的代码中,我遇到了无法避免某种情况的情况。它看起来像这样:

// some math code, calculating d1, d2
if (d1 < 0.5)
{
    buffer[x1] += 1;  // buffer is in the global memory
}
if (d2 < 0.5)
{
    buffer[x2] += 1;
}
// some more math code.

某些线程可能会因条件而进入一个线程,有些线程可能会进入两个线程,而其他线程可能不会进入其中。

现在为了让所有线程在条件之后再次回到“做同样的事情”,我应该使用__syncthreads()在条件之后同步它们吗?或者这是否会以某种方式自动发生? 两个线程可以做同样的事情,因为其中一个是后面的一个操作,因此毁掉了每个人吗?或者是否有一些幕后努力让他们在分支后再次做同样的事情?

4 个答案:

答案 0 :(得分:35)

在变形中,任何线程都不会“超前”。如果存在一个条件分支并且它由warp中的某些线程而不是其他线程(也就是warp“divergence”)占用,则其他线程将一直处于空闲状态,直到分支完成并且它们全部“聚合”在一起共同指令上。因此,如果您只需要线程内部同步,则会“自动”发生。

但不同的warp不会以这种方式同步。因此,如果您的算法要求在许多warp中完成某些操作,那么您将需要使用显式同步调用(请参阅CUDA编程指南,第5.4节)。


编辑:重新组织了接下来的几段以澄清一些事情。

这里确实存在两个不同的问题:指令同步和内存可见性。

  • __syncthreads()强制执行指令同步并确保内存可见性,但仅限于块内,而不是跨块(CUDA编程指南,附录B.6)。它对共享内存上的写入然后读取很有用,但不适合同步全局内存访问。

  • __threadfence()确保全局内存可见性但不执行任何指令同步,因此根据我的经验,它的用途有限(但请参阅附录B.5中的示例代码)。

    < / LI>
  • 内核中无法进行全局指令同步。如果在任何线程上调用f()之前需要在所有线程上完成g(),请将f()g()拆分为两个不同的内核,并从主机中串行调用它们。

  • 如果您只需要增加共享或全局计数器,请考虑使用原子增量函数atomicInc()(附录B.10)。对于上面的代码,如果x1x2不是全局唯一的(跨网格中的所有线程),非原子增量将导致竞争条件,类似于最后一段附录B.2.4。

最后,请记住,对全局内存的任何操作,特别是同步函数(包括原子)都不利于性能。

在不知道您正在解决的问题的情况下很难推测,但也许您可以重新设计算法以在某些地方使用共享内存而不是全局内存。这将减少同步的需要并为您带来性能提升。

答案 1 :(得分:2)

来自CUDA最佳实践指南的第6.1节:

  

任何流量控制指令(if,switch,do,for,while)都会对此产生重大影响   通过使相同warp的线程发散的指令吞吐量;那是,   遵循不同的执行路径。如果发生这种情况,则执行不同的路径   必须序列化,增加为此执行的指令总数   经。当所有不同的执行路径完成后,线程会聚   回到相同的执行路径。

所以,你不需要做任何特别的事情。

答案 2 :(得分:2)

在加布里埃尔的回答中:

“内核中无法进行全局指令同步。如果在任何线程上调用g()之前需要对所有线程执行f(),请将f()和g()拆分为两个不同的内核,并从中串行调用主持人。“

如果您在同一个线程中需要f()和g()的原因是因为您正在使用寄存器存储器,并且您希望f中的寄存器或共享数据到达g? 也就是说,对于我的问题,跨块同步的全部原因是因为g中需要来自f的数据 - 并且突破到内核需要大量额外的全局内存来将寄存器数据从f传输到g,这就是我我想避免

答案 3 :(得分:1)

你的问题的答案是否定的。你不需要做任何特别的事情。 无论如何,你可以解决这个问题,而不是你的代码,你可以做这样的事情:

buffer[x1] += (d1 < 0.5);
buffer[x2] += (d2 < 0.5);

您应该检查是否可以使用共享内存并以合并模式访问全局内存。另外请确保您不想在多个线程中写入相同的索引。

相关问题