英特尔x86 0x2E/0x3E前缀分支预测实际使用?

And*_*zos 25 x86 gcc x86-64 branch-prediction

在最新的英特尔软件开发手册中,它描述了两个操作码前缀:

Group 2 > Branch Hints

    0x2E: Branch Not Taken
    0x3E: Branch Taken
Run Code Online (Sandbox Code Playgroud)

这些允许跳转指令的显式分支预测(像操作码一样Jxx)

我记得在几年前读过x86显式分支预测本质上是gccs分支谓词内在函数上下文中的无操作.

我现在还不清楚这些x86分支提示是否是一个新功能,或者它们在实践中是否基本上是无操作.

任何人都可以清除这个吗?

(那就是:gccs分支预测函数会生成这些x86分支提示吗? - 并且当前的Intel CPU不会忽略它们吗? - 这是什么时候发生的?)

更新:

我创建了一个快速测试程序:

int main(int argc, char** argv)
{
    if (__builtin_expect(argc,0))
        return 1;

    if (__builtin_expect(argc == 2, 1))
        return 2;

    return 3;
}
Run Code Online (Sandbox Code Playgroud)

拆卸以下内容:

00000000004004cc <main>:
  4004cc:   55                      push   %rbp
  4004cd:   48 89 e5                mov    %rsp,%rbp
  4004d0:   89 7d fc                mov    %edi,-0x4(%rbp)
  4004d3:   48 89 75 f0             mov    %rsi,-0x10(%rbp)
  4004d7:   8b 45 fc                mov    -0x4(%rbp),%eax
  4004da:   48 98                   cltq   
  4004dc:   48 85 c0                test   %rax,%rax
  4004df:   74 07                   je     4004e8 <main+0x1c>
  4004e1:   b8 01 00 00 00          mov    $0x1,%eax
  4004e6:   eb 1b                   jmp    400503 <main+0x37>
  4004e8:   83 7d fc 02             cmpl   $0x2,-0x4(%rbp)
  4004ec:   0f 94 c0                sete   %al
  4004ef:   0f b6 c0                movzbl %al,%eax
  4004f2:   48 85 c0                test   %rax,%rax
  4004f5:   74 07                   je     4004fe <main+0x32>
  4004f7:   b8 02 00 00 00          mov    $0x2,%eax
  4004fc:   eb 05                   jmp    400503 <main+0x37>
  4004fe:   b8 03 00 00 00          mov    $0x3,%eax
  400503:   5d                      pop    %rbp
  400504:   c3                      retq   
  400505:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40050c:   00 00 00 
  40050f:   90                      nop
Run Code Online (Sandbox Code Playgroud)

我没有看到2E或3E?也许gcc出于某种原因将他们排除在外?

Chr*_*ris 28

这些指令前缀对现代处理器没有任何影响(比Pentium 4更新).它们只花费一个字节的代码空间,因此,不生成它们是正确的.

有关详细信息,请参阅Agner Fog的优化手册,特别是3.微体系结构:http://www.agner.org/optimize/

"英特尔®64和IA-32架构优化参考手册"不再在关于优化分支的部分中提及它们(第3.4.1节):http: //www.intel.de/content/dam/doc/manual/64 -ia-32体系结构优化-手册.pdf

这些前缀是Netburst架构的(无害)残留.在全面优化中,您可以使用它们来对齐代码,但这就是它们现在的好处.


Gun*_*iez 12

gcc是不生成前缀的权利,因为它们对Pentium 4以来的所有处理器都没有影响.

但是__builtin_expect还有其他影响,比如将一个不期望的代码路径从代码中的缓存热点位置或内联决策中移开,因此它仍然有用.


Cau*_*ite 9

虽然Pentium 4是唯一真正尊重分支提示指令的一代,但大多数CPU都有某种形式的静态分支预测,可用于实现相同的效果.这个答案与原始问题有点相似,但我认为这对于访问此页面的任何人来说都是有价值的信息.

英特尔优化指南昂纳雾指南(已在这里已经提到)都具有此功能的极好说明.


英特尔有这样的说法比Core 2更新几代:

使条件分支后面的连通代码成为具有前向目标的分支的可能目标

因此,静态预测算法预测在代码中向前跳跃的条件分支不被采用.

这与GCC似乎使用的结果一致__builtin_expect:'expected' return 1/ return 2code放置在条件分支的未采用路径中,这些路径将被静态预测为未采用.

另外:

使用静态预测算法预测分支目标缓冲区中没有历史记录的分支:

  • 预测无条件分支.

  • 预测未采取间接分支.

因此,在GCC将无条件jmps 放置到函数末尾的"预期"未采用路径中,这些跳转将被静态预测为采用(即不跳过).

英特尔还说:

使条件分支后面的连通代码成为具有后向目标的分支的不太可能的目标

因此,通过静态预测算法预测将采用在代码中向后跳跃的条件分支.

据昂纳晴间多云,大部分的Pentium也按照这个算法:

在PPro,P2,P3,P4和P4E上,如果一个控制传输指令在前面没有看到,或者不在分支目标缓冲区中,它预计会在它向前发生时失效,如果它向后移动则被采用. (例如一个循环).静态预测比这些处理器上的动态预测花费更长的时间.

但是,Core 2系列(和Pentium M)有一个完全不同的政策:

这些处理器不使用静态预测.预测器只是在第一次看到分支时进行随机预测,这取决于分配给新分支的BTB条目中发生的情况.只有50%的机会做出正确的跳跃预测或没有跳跃,但预测的目标是正确的.

AMD处理器一样显然:

预计在第一次看到分支时不会进行分支.预计总是在第一次拍摄之后拍摄一个分支.动态预测仅在分支被采用后才被使用.分支提示前缀无效.

还有一个需要考虑的因素:CPU通常喜欢以线性方式执行,因此即使正确预测的采用分支通常比正确预测的未采用分支更昂贵.