这是一个对任何平台、语言或编译器开放的天真的一般性问题。虽然我对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
如果通信协议使更有可能或临界值变为真(高)或假(低),这会有帮助吗?
发布于 2022-01-06 02:47:04
TL:是的,在C或C++中,使用likely()
宏或C++20 [[likely]]
来帮助编译器实现更好的asm。但这与影响实际CPU分支的预测是不同的。如果用asm编写,请列出代码以最小化所使用的分支。
对于大多数ISAs来说,asm中没有办法提示CPU是否可能使用分支。(一些例外包括奔腾4(但不是早期或以后的x86)、PowerPC和一些MIPS,它们允许将分支提示作为条件分支asm指令的一部分。)
但是,不使用直线代码比使用直线代码便宜,因此暗示高级语言使用快速路径连续部署代码,这无助于分支预测的准确性,但可以帮助(或损害)性能。(i-缓存局部性,前端带宽:请记住代码提取发生在相邻的16或32字节块中,因此一个已捕获的分支意味着该取取块的后面的部分是无用的。此外,分支预测吞吐量;例如,一些CPU,比如Intel Skylake,除了循环分支之外,不能以每2个时钟超过1的速度处理预测的分支。),包括无条件的分支,如jmp或ret。)
使用分支是困难的;不接受的分支使CPU保持警觉,但是如果预测是准确的,那么它只是对执行单元的正常指令(验证预测),对前端没有什么特殊的指示。也请参阅Modern Microprocessors A 90-Minute Guide!,它有一个关于分支预测的部分。(而且总体上非常出色。)
许多人将源级分支提示误解为分支预测提示。如果为支持asm中的分支提示的CPU进行编译,这可能是一种效果,但对于大多数情况而言,最重要的影响是布局,并决定是否使用无分支(cmov
);[[likely]]
条件也意味着它应该能够很好地预测。
带有一些CPU的(尤其是旧的)分支的布局有时会影响运行时预测:如果CPU在其动态预测器中不记得有关分支的任何信息,标准的静态预测启发式是不接受前向条件分支,假定采用反向条件分支(因为这是normally the bottom of a loop )。请参阅https://danluu.com/branch-prediction/中的BTFNT部分。
编译器可以任意布置一个if(c) x else y;
,要么将源代码与jump over x if !c
作为开头部分匹配,要么交换if和else块,然后使用相反的分支条件。或者,它可以将一个块放置在行外(例如,在函数末尾的ret
之后),这样快速路径就没有附加条件的分支或其他分支,而不太可能的路径必须跳到那里然后跳回去。
在高级源代码中使用分支提示很容易造成比好处更大的危害,特别是如果周围的代码更改没有注意到它们,所以概要文件引导的优化是编译器了解分支可预测性和可能性的最佳方法。(例如,gcc -O3 -fprofile-generate
/ run与一些具有代表性的输入一起运行,这些输入以相关的方式/gcc -O3 -fprofile-use
执行代码路径)
但是在某些语言中有一些提示方法,比如C++20 [[likely]]
和[[unlikely]]
,它们是GNU likely()
/ unlikely()
宏的可移植版本。
[[likely]]
__builtin_expect
,这与C++20 [[likely]]
的其他一些问题的回答
除了GNU / C++和C++20之外,我不知道如何对分支进行注释。
没有任何提示或配置文件数据
否则,优化编译器必须使用启发式来猜测分支的哪一方更有可能。如果是循环分支,则通常假定循环将多次运行。在if
上,他们有一些基于实际情况的启发式方法,可能是块中被控制的东西;IDK我还没有研究gcc或clang做什么。
不过,我注意到GCC确实关心这种情况。它不像假设int
值是均匀随机分布的那样天真,尽管我认为它通常假设if (x == 10) foo();
有点不太可能。
像JVM中的JIT编译器在这里有一个优点:它们可以在运行的早期阶段对分支进行测试,在进行最终优化的asm之前收集分支方向的信息。OTOH他们需要快速编译,因为编译时间是总运行时间的一部分,所以他们不那么努力地做好asm,这在代码质量方面是一个主要的缺点。
https://stackoverflow.com/questions/70600597
复制相似问题