Optimization CUDA:同步线程

Optimization CUDA:同步线程,optimization,cuda,synchronization,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 ma

几乎在我读到的有关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.
buffer[x1] += (d1 < 0.5);
buffer[x2] += (d2 < 0.5);
//一些数学代码,计算d1,d2
如果(d1<0.5)
{
缓冲区[x1]+=1;//缓冲区在全局内存中
}
如果(d2<0.5)
{
缓冲区[x2]+=1;
}
//更多的数学代码。
一些线程可能会根据条件进入一个线程,一些线程可能会同时进入两个线程,而另一些线程可能不会进入任何一个线程

现在,为了让所有线程在条件结束后再次“做同样的事情”,我应该在条件结束后使用
\uu syncthreads()
同步它们吗?或者这是自动发生的吗?

两个线程是否可以不做相同的事情,因为其中一个线程落后于一个操作,从而破坏了每个线程的运行?或者,是否有一些幕后工作让他们在分支机构成立后再次做同样的事情?

摘自《CUDA最佳实践指南》第6.1节:

任何流量控制指令(if、switch、do、for、while)都会显著影响 通过使同一扭曲的线程发散来提高指令吞吐量;就是, 遵循不同的执行路径。如果发生这种情况,不同的执行路径 必须序列化,从而增加为此执行的指令总数 弯曲当所有不同的执行路径都完成时,线程会聚 返回到相同的执行路径


因此,您不需要做任何特殊的事情。

在一个扭曲中,没有任何线程会“领先”任何其他线程。如果有一个条件分支,并且它由warp中的一些线程执行,而不是由其他线程执行(也称为warp“发散”),那么其他线程将一直处于空闲状态,直到分支完成,并且它们都在一条公共指令上“聚合”在一起。所以,如果您只需要线程的内部扭曲同步,那么这种情况会“自动”发生

但不同的扭曲不会以这种方式同步。因此,如果您的算法要求跨多个扭曲完成某些操作,则需要使用显式同步调用(请参阅CUDA编程指南,第5.4节)


编辑:重新组织了接下来的几段,以澄清一些问题

这里有两个不同的问题:指令同步和内存可见性

  • \uuuu syncthreads()
    强制指令同步并确保内存可见性,但只能在块内,而不能跨块(CUDA编程指南,附录B.6)。它对于在共享内存上先写后读很有用,但不适合同步全局内存访问

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

  • 内核内不可能进行全局指令同步。如果在对任何线程调用
    g()
    之前需要对所有线程执行
    f()
    ,请将
    f()
    g()
    拆分为两个不同的内核,并从主机连续调用它们

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

最后,请记住,对全局内存的任何操作,特别是同步功能(包括原子)都会对性能造成不利影响


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

您的问题的答案是否定的。您不需要做任何特殊的事情。 无论如何,您可以修复此问题,而不是您的代码,您可以执行以下操作:

// 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.
buffer[x1] += (d1 < 0.5);
buffer[x2] += (d2 < 0.5);
buffer[x1]+=(d1<0.5);
缓冲区[x2]+=(d2<0.5);
您应该检查是否可以使用共享内存并以合并模式访问全局内存。还要确保您不想在多个线程中写入同一索引。

在Gabriel的回答中:

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

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

让我看看我是否理解。因此,如果条件分支执行相同的工作量,这不会影响性能,因为每个线程不会空闲太长时间。我说得对吗?@omegatai我知道你的评论很老,但其他人可能想知道,所以这里是这样的:一个warp一次只能处理一条指令,所以如果warp中的一些线程做一件事,而其他线程做其他事情,那么总时间就是这两组线程的时间总和。扭曲内没有时间重叠。性能会受到影响。诀窍很微妙,但这样做根本不是分支!