为了明确一下,我不需要任何可移植性的解决方案,所以任何会绑定到特定机器上的解决方案都是可以接受的。
基本上,我有一个if语句,在99%的情况下将计算为true,现在我想尽可能地提高性能,是否可以发出某种编译器命令(使用GCC 4.1.2和x86 ISA,如果有关系)来告诉分支预测器应该缓存这个分支?
为了明确一下,我不需要任何可移植性的解决方案,所以任何会绑定到特定机器上的解决方案都是可以接受的。
基本上,我有一个if语句,在99%的情况下将计算为true,现在我想尽可能地提高性能,是否可以发出某种编译器命令(使用GCC 4.1.2和x86 ISA,如果有关系)来告诉分支预测器应该缓存这个分支?
是的,但它没有任何影响。除了旧的(废弃的)Netburst架构之前的一些体系结构会例外,即使在这种情况下也不会有明显的影响。
Intel引入了“分支提示”操作码,以及对某些较旧架构的冷跳转(向后预测为取,向前预测为不取)的默认静态分支预测。GCC使用__builtin_expect(x, prediction)
来实现这一点,其中prediction通常为0或1。
编译器发出的操作码在所有新的处理器架构(>= Core 2)上都被忽略了。这个小的特殊情况是在旧的Netburst架构上进行冷跳转的情况下才有可能实际起作用。Intel现在建议不要使用静态分支提示,可能是因为他们认为代码大小的增加比可能的边际加速更有害。
除了用于预测器的无用分支提示外,__builtin_expect
还有其用途,编译器可以重新排序代码以提高缓存使用率或节省内存。
有多个原因它并不能像预期那样工作。
是的。 http://kerneltrap.org/node/4705
__builtin_expect
是一种方法,gcc(版本>= 2.96)为程序员提供,以向编译器指示分支预测信息。__builtin_expect
的返回值是传递给它的第一个参数(只能是整数)。
if (__builtin_expect (x, 0))
foo ();
[This] would indicate that we do not expect to call `foo', since we
expect `x' to be zero.
另请参见为什么英特尔在这些年里改变了静态分支预测机制?:自Sandybridge以来,据我们所知,英特尔根本不使用静态预测,这是通过试图反向工程CPU执行的性能实验得出的结论。(许多旧CPU在动态预测失误时采用静态预测作为后备。正常的静态预测是前向分支不被采纳,而后向分支被采纳(因为后向分支通常是循环分支)。)
__builtin_expect
来实现likely()
/unlikely()
宏的效果(就像Drakosha的答案所提到的那样)并不会直接将BP提示插入到汇编代码中。(在编译其他任何内容时都不会这样做,但在使用gcc -march=pentium4
编译时可能会这样做。)
实际效果是布局代码,使快速路径具有较少的taken分支和可能更少的指令总数。这将有助于静态预测起作用的情况下的分支预测(例如,在动态预测器很冷的情况下,对于CPU,它会回退到静态预测而不仅仅是让分支在预测器缓存中别名)。
请参见What is the advantage of GCC's __builtin_expect in if else statements?以获取特定的代码生成示例。
taken分支的成本略高于未taken分支,即使完全预测。当CPU以16字节的块抓取代码以并行解码时,taken分支意味着该抓取块中的后续指令不是要执行的指令流的一部分。它会在前端创建气泡,这可能会成为高吞吐量代码的瓶颈(它在缓存未命中时不会在后端停顿,并具有高指令级并行性)。
跳转到不同的代码块还可能触及更多的缓存行,增加L1i缓存占用,如果它是冷的,也可能导致更多的指令缓存未命中。(以及潜在的uop缓存占用)。因此,快速通道短且线性也有另一个优点。-fprofile-generate
和-fprofile-use
。如何在g ++中使用概要文件指导的优化?
否则,如果您没有使用likely/unlikely宏和PGO,则GCC必须使用各种启发式方法来进行猜测。默认情况下,在-O1
及更高级别上启用-fguess-branch-probability
。
https://www.phoronix.com/scan.php?page=article&item=gcc-82-pgo&num=1在Xeon Scalable服务器CPU(Skylake-AVX512)上使用gcc8.2对PGO和常规方式进行了基准测试。每个基准测试都获得了至少一点加速,有些获得了约10%的好处。(其中大部分可能来自于热循环中的循环展开,但其中一些显然来自于更好的分支布局和其他效果。)if
语句。SUN C Studio为此情况定义了一些pragma。
#pragma rarely_called ()
如果条件表达式的一部分是函数调用或以函数调用开头,则此方法有效。
但是,无法标记通用的if/while语句。
不,因为没有汇编命令可以让分支预测器知道。不用担心,分支预测器非常聪明。
此外,强制性评论关于过早优化以及它是邪恶的。
编辑:Drakosha提到了一些适用于GCC的宏。但是,我认为这是代码优化实际上与分支预测无关。
对我来说,这听起来有些过度 – 这种类型的优化只能节省微小的时间。例如,使用更新的gcc版本会对优化产生更大的影响。此外,尝试启用和禁用所有不同的优化标志;它们并不都能提高性能。
基本上,相比于其他许多有益的途径,这似乎极不可能产生任何重大差异。
编辑:感谢评论。我已经将其设置为社区wiki,但仍然保留了评论以供他人查看。