这是一个对任何平台、语言或编译器都开放的简单的一般问题。虽然我最好奇的是Aarch64、C++、GCC。
当根据I/O状态(编译器无法预测)对程序流中不可避免的分支进行编码时,并且我知道一种状态比另一种状态更有可能,我如何向编译器指示这一点?
这是更好的吗
if(true == get(gpioVal))
unlikelyFunction();
else
likelyFunction();
比这个?
if(true == get(gpioVal))
likelyFunction(); // performance critical, fill prefetch caches from this branch
else
unlikelyFunction(); // missed prediction not consequential on this branch
如果通信协议使更可能或关键的值为真(高)或假(低),这有帮助吗?
TL:DR:是的,在C或C++中,使用likely()
宏或C++20[[likely]]
来帮助编译器生成更好的asm。不过,这与影响实际的CPU分支预测是分开的。如果使用asm编写,请将代码布局为尽量减少占用的分支。
对于大多数ISA,asm中没有办法提示CPU是否可能进行分支。(有些例外包括Pentium 4(但不是早期或更高版本的x86)、PowerPC和一些MIPS,它们允许将分支提示作为条件分支asm指令的一部分。)
- 是否可以告诉分支预测器跟随分支的可能性有多大
但不使用直线代码比使用直线代码便宜,因此暗示高级语言以快速路径连续的方式布局代码无助于分支预测的准确性,但可能有助于(或损害)性能。(I-cache位置、前端带宽:记住,代码提取发生在连续的16或32字节块中,因此提取的分支意味着提取块的后面部分没有用处。此外,分支预测吞吐量;例如,Intel Skylake等一些CPU无法处理超过1/2时钟的预测提取分支,循环分支除外。这包括jmp等无条件分支或返回)
被砍的树枝很硬;未执行的分支可以让CPU保持警觉,但如果预测是准确的,这只是执行单元的正常指令(验证预测),前端没有什么特别的。另请参阅现代微处理器90分钟指南!其具有关于分支预测的部分。(总体而言非常出色。)
- 当skylake CPU预测错误一个分支时,会发生什么
- 通过提前计算条件避免管道停滞
- 分支预测器如何知道它是否不正确
许多人将源级分支提示误解为分支预测提示。如果为支持asm中分支提示的CPU进行编译,这可能是一个影响,但对大多数人来说,重要的影响是布局,以及决定是否使用无分支(cmov
);CCD_ 4条件也意味着它应该很好地预测。
对于一些CPU,尤其是较旧的CPU,分支的布局有时确实会影响运行时预测:如果CPU在其动态预测器中不记得任何关于分支的信息,则标准的静态预测启发式方法是不采用前向条件分支,假设采用后向条件(因为这通常是循环的底部。请参阅中的BTFNT部分https://danluu.com/branch-prediction/.
编译器可以以任何一种方式布置if(c) x else y;
,或者将源代码与jump over x if !c
匹配作为开头,或者交换if和else块并使用相反的分支条件。或者,它可以将一个块放在行外(例如,在函数末尾的ret
之后),这样快速路径就没有条件分支或其他分支,而不太可能的路径必须跳到那里,然后跳回来。
在高级源代码中使用分支提示很容易弊大于利,尤其是当周围的代码发生变化而不注意它们时,因此概要文件引导优化是编译器了解分支可预测性和可能性的最佳方式。(例如gcc -O3 -fprofile-generate
/使用一些以相关方式行使代码路径的代表性输入运行/gcc -O3 -fprofile-use
)
但在某些语言中也有提示的方法,如C++20[[likely]]
和[[unlikely]]
,它们是围绕__builtin_expect
的GNU Clikely()
/unlikely()
宏的可移植版本。
- https://en.cppreference.com/w/cpp/language/attributes/likelyC++20
[[likely]]
- 如何使用C++20';if-else语句语法帮助中的s可能/不可能属性
- GCC是否有编译器提示,强制分支预测始终按特定方式进行?(对于字面上的问题,否。对于实际需要的内容,向编译器提供分支提示,是的。)
- Linux内核中可能/不可能的宏是如何工作的?它们的好处是什么?使用
__builtin_expect
的GNU C宏,与C++20[[likely]]
效果相同,但语法不同 - GCC的优势是什么;s __builtin_expect在if-elses语句中?示例asm输出。(另请参阅CiroAntilli对其他一些问题的回答,他在其中举例说明。)
- 一个简单的例子,[[可能]]和[[不可能]]影响程序集
我不知道如何为GNU C/C++和ISO C++20以外的语言注释分支。
没有任何提示或配置文件数据
如果没有这一点,优化编译器就必须使用启发式方法来猜测分支的哪一边更有可能。如果它是一个循环分支,他们通常认为循环将运行多次。在if
上,他们根据实际情况以及可能控制的块中的内容进行了一些启发;IDK我还没有研究过gcc或clang做什么。
我注意到GCC确实关心这种情况。这并不像假设int
值是均匀随机分布那么天真,尽管我认为它通常假设if (x == 10) foo();
不太可能。
JVM中的JIT编译器有一个优势:它们可以在运行的早期阶段对分支进行检测,以便在进行最终优化的asm之前收集分支方向信息。OTOH他们需要快速编译,因为编译时间是总运行时间的一部分,所以他们不会努力制作好的asm,这是代码质量方面的一个主要缺点。