Performance 分支分歧真的那么糟糕吗?

Performance 分支分歧真的那么糟糕吗?,performance,cuda,branch,Performance,Cuda,Branch,我在互联网上看到了许多关于分支机构分歧以及如何避免分歧的问题。然而,即使在阅读了几十篇关于CUDA如何工作的文章之后,我似乎也看不出在大多数情况下避免分支分歧有什么帮助。在有人伸出爪子跳到我面前之前,请允许我描述一下我认为的“大多数情况”。 在我看来,分支分歧的大多数实例都涉及许多真正不同的代码块。例如,我们有以下场景: if (A): foo(A) else: bar(B) 如果有两个线程遇到这种分歧,线程1将首先执行,路径为A。接下来,线程2将路径为B。为了消除分歧,我们可以将上面

我在互联网上看到了许多关于分支机构分歧以及如何避免分歧的问题。然而,即使在阅读了几十篇关于CUDA如何工作的文章之后,我似乎也看不出在大多数情况下避免分支分歧有什么帮助。在有人伸出爪子跳到我面前之前,请允许我描述一下我认为的“大多数情况”。

在我看来,分支分歧的大多数实例都涉及许多真正不同的代码块。例如,我们有以下场景:

if (A):
  foo(A)
else:
  bar(B)
如果有两个线程遇到这种分歧,线程1将首先执行,路径为A。接下来,线程2将路径为B。为了消除分歧,我们可以将上面的块改为如下:

foo(A)
bar(B)
假设在线程2上调用
foo(A)
和在线程1上调用
bar(B)
是安全的,那么性能可能会有所提高。然而,我的看法是:

在第一种情况下,线程1和2以串行方式执行。称之为两个时钟周期

在第二种情况下,线程1和2并行执行
foo(A)
,然后并行执行
bar(B)
。在我看来,这仍然像两个时钟周期,区别在于,在前一种情况下,如果
foo(A)
涉及从内存读取,我想象线程2可以在该延迟期间开始执行,这导致延迟隐藏。如果是这种情况,分支发散代码会更快。

您假设(至少是您给出的示例和唯一的参考)避免分支发散的唯一方法是允许所有线程执行所有代码

在这种情况下,我同意没有太大区别

但是,避免分支分歧可能更多地与在更高级别上重新构造算法有关,而不仅仅是添加或删除一些if语句,并使代码在所有线程中“安全”执行

我将举一个例子。假设我知道奇数线程需要处理像素的蓝色部分,偶数线程需要处理绿色部分:

#define N 2 // number of pixel components
#define BLUE 0
#define GREEN 1
// pixel order: px0BL px0GR px1BL px1GR ...


if (threadIdx.x & 1)  foo(pixel(N*threadIdx.x+BLUE));
else                  bar(pixel(N*threadIdx.x+GREEN));
这意味着每个备用线程都采用给定的路径,无论是
foo
还是
bar
。所以现在我的扭曲需要两倍的时间来执行

但是,如果我重新排列像素数据,使颜色分量可能以32个像素的块连续: BL0 BL1 BL2。。。GR0 GR1 GR2

我可以编写类似的代码:

if (threadIdx.x & 32)  foo(pixel(threadIdx.x));
else                   bar(pixel(threadIdx.x));
看起来我还是有发散的可能。但由于发散发生在扭曲边界上,给定扭曲执行
if
路径或
else
路径,因此不会发生实际发散


这是一个微不足道的例子,可能很愚蠢,但它说明了可能有一些方法可以解决扭曲发散问题,而不需要运行所有发散路径的所有代码。

简单的回答是,只有当一个扭曲中的所有线程在一个分支中不遵循相同的路径时,分支才有问题。当这种情况发生时,您会得到指令重播,这会降低指令吞吐量和性能。但是编译器足够聪明,可以将“次要”分支转换为条件执行,这基本上是无惩罚的。所以这通常是一个被大大夸大的问题。@talonmies我不知道其中的一些术语。指令重播?我假设这意味着在同一个扭曲中的不同线程上串行执行同一条指令,但我发现的唯一一条似乎表明,指令重放是由于没有执行主机发送的所有指令造成的,在这种情况下,我不确定您的意思。至于条件执行,我所知道的唯一定义就是分支发散。分支发散有不同程度的开销。在许多情况下,编译器可以使用谓词来消除分支或对扭曲活动掩码的更改。在这些情况下,执行条件测试和设置谓词只会受到指令执行延迟的影响。如果代码实际上发散了,那么问题是有多少额外的指令被执行到多个代码路径。分支本身和分支的簿记会增加开销,并导致扭曲暂停,等待分支地址解析和获取。这可能会被更高的入住率所掩盖。我只想指出你的问题中可能存在的一个错误。您提到“区别在于,在前一种情况下,如果foo(A)涉及从内存读取,我认为线程2可以在该延迟期间开始执行”,但我认为这不会发生,因为线程1和线程2处于相同的扭曲中,这意味着它们总是执行相同的指令(尽管其中一个可能被蒙住,而另一个则没有)有一篇关于减少分支分歧的有趣文章:。它很好地解释了当两个分支不完全不同时,可以使用哪些类型的优化。我不认为真正的分歧经常发生。因此,基本上你所说的不是分支分歧总体上不好,而是应该消除分歧何时有机会利用GPU的SIMD体系结构(如上面的示例所示)?是的。分支分歧很糟糕(在某种程度上)任何地方。CPU制造商都在CPU推测性执行和分支预测方面投入了大量精力,以解决分支分歧的负面影响。在GPU上,它的影响是非常明显的。如果你能通过巧妙地重新设计算法来避免这种情况,那就很好了。但如果不能,机器至少会给你fle以这种方式编写代码的灵活性使程序员的工作更容易接受。机器可以在代码中处理分支这一事实比一些更严格的体系结构更有利。好的,你正在进入分支预测,我认为这意味着你上面的文章涵盖了GPU关于分支发散的具体问题,并且我已经理解了你的c没错,那