编译器可以对分支信息做什么?

mar*_*all 12 compiler-construction optimization x86 assembly branch-prediction

在现代奔腾上,似乎不再可能给处理器提供分支提示.假设一个分析编译器(如带有配置文件引导优化的gcc)可以获得有关可能的分支行为的信息,那么它可以做些什么来生成更快执行的代码?

我所知道的唯一选择是将不太可能的分支移动到函数的末尾.还有别的事吗?

更新.

http://download.intel.com/products/processor/manual/325462.pdf第2a卷第2.1.1节说

"分支提示前缀(2EH,3EH)允许程序向处理器提供关于分支最可能的代码路径的提示.仅将这些前缀用于条件分支指令(Jcc).其他使用分支提示前缀和/或保留其他具有Intel 64或IA-32指令的未定义操作码;此类使用可能会导致不可预测的行为."

我不知道这些实际上是否有任何影响.

另一方面,第3.4.1节.的http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf

"编译器生成的代码可以提高英特尔处理器中分支预测的效率.英特尔C++编译器通过以下方式实现:

  • 将代码和数据保存在不同的页面上
  • 使用条件移动指令来消除分支
  • 生成与静态分支预测算法一致的代码
  • 在适当的地方内联
  • 如果迭代次数是可预测的,则展开

通过配置文件引导优化,编译器可以布置基本块以消除函数的最频繁执行路径的分支或至少提高其可预测性.分支预测不一定是源级别的关注点.有关更多信息,请参阅英特尔C++编译器文档."

http://cache-www.intel.com/cd/00/00/40/60/406096_406096.pdf在"PGO的性能改进"中说

"PGO最适用于具有许多频繁执行的分支的代码,这些代码在编译时很难预测.例如,代码具有密集的错误检查,其中错误条件在大多数情况下都是错误的.不经常执行的(冷)错误处理代码可以重新定位,因此分支很少被错误预测.最小化冷代码交错到频繁执行的(热)代码可以改善指令缓存行为."

mig*_*gle 7

您想要的信息有两种可能的来源:

  1. 有英特尔64和IA-32架构软件开发人员手册(3卷).这是一项已经发展了数十年的巨大工作.这是我所知道的很多主题的最佳参考,包括浮点数.在这种情况下,您要检查第2卷,指令集参考.
  2. 有英特尔64和IA-32架构优化参考手册.这将以简短的术语告诉您对每个微体系结构的期望.

现在,我不知道你的意思是"现代奔腾"处理器,这是2013年,对吗?没有任何Pentiums ......

指令集确实支持告诉处理器是否期望通过条件分支指令(例如JC,JZ等)的前缀采用分支.参见(1)的第2A卷,第2.1.1节(我所拥有的版本)指令前缀.有2E和3E前缀未分别采取和采取.

至于这些前缀是否实际上有任何影响,如果我们可以获得该信息,它将在优化参考手册,您想要的微架构部分(我相信它不会是奔腾).

除了使用它们之外,还有关于该主题的优化参考手册的整个部分,即3.4.1节(我的版本).

在这里复制是没有意义的,因为您可以免费下载该手册.简述:

  • 使用条件指令消除分支(CMOV,SETcc),
  • 考虑静态预测算法(3.4.1.3),
  • 内联
  • 循环展开

此外,一些编译器,例如GCC,即使CMOV不可能,也经常执行逐位算术来选择计算的两个不同事物中的一个,从而避免分支.它在向量化循环时特别使用SSE指令.

基本上,静态条件是:

  • 预计将采取无条件分支(......有点可期待...)
  • 预计不会采用间接分支(由于数据依赖性)
  • 预计将采取向后条件(适用于循环)
  • 预计不会采用前向条件

您可能想要阅读整个3.4.1节.


sh1*_*sh1 3

如果很明显很少进入循环,或者通常很少迭代,那么编译器可能会避免展开循环,因为这样做会增加很多有害的复杂性来处理边缘条件(奇数次迭代等) .)。在这种情况下,尤其应避免矢量化。

编译器可能会重新排列嵌套测试,以便最常导致快捷方式的测试可用于避免对通过率为 50% 的内容执行测试。

可以优化寄存器分配,以避免在常见情况下出现很少使用的块强制寄存器溢出。

这些只是一些例子。我确信还有其他我没有想到的。