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

Gcc 实际使用的英特尔x86 0x2E/0x3E前缀分支预测?,gcc,x86,x86-64,branch-prediction,Gcc,X86,X86 64,Branch Prediction,在最新的《英特尔软件开发手册》中,它描述了两个操作码前缀: Group 2 > Branch Hints 0x2E: Branch Not Taken 0x3E: Branch Taken 它们允许跳转指令的显式分支预测(操作码,如Jxx) 我记得几年前读到过一篇文章,在x86上,显式分支预测在gccs分支预处理内部函数的上下文中基本上是不可操作的 我现在不清楚这些x86分支提示是否是一个新特性,或者它们实际上是否不是ops 有人能澄清一下吗 (也就是说:gccs分支预

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

Group 2 > Branch Hints

    0x2E: Branch Not Taken
    0x3E: Branch Taken
它们允许跳转指令的显式分支预测(操作码,如
Jxx

我记得几年前读到过一篇文章,在x86上,显式分支预测在gccs分支预处理内部函数的上下文中基本上是不可操作的

我现在不清楚这些x86分支提示是否是一个新特性,或者它们实际上是否不是ops

有人能澄清一下吗

(也就是说:gccs分支预测函数会生成这些x86分支提示吗?——当前的英特尔CPU不会忽略它们吗?——这是什么时候发生的?)

更新:

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

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

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

    return 3;
}
分解为以下各项:

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
0000000000 4004CC:
4004cc:55%推送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测试%rax,%rax
4004df:74 07 je 4004e8
4004e1:b8 01 00 mov$0x1,%eax
4004e6:eb 1b jmp 400503
4004e8:83 7d fc 02 cmpl$0x2,-0x4(%rbp)
4004ec:0f 94 c0集合%al
4004ef:0f b6 c0 movzbl%al,%eax
4004f2:48 85 c0测试%rax,%rax
4004f5:74 07 je 4004fe
4004f7:b8 02 00 mov$0x2,%eax
4004fc:eb 05 jmp 400503
4004fe:b8 03 00 mov$0x3,%eax
400503:5d pop%rbp
400504:c3 retq
400505:66 2e 0f 1f 84 00 nopw%cs:0x0(%rax,%rax,1)
40050c:00
40050f:90无

我看不到2E或3E?也许gcc出于某种原因而忽略了它们?

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

有关详细信息,请参阅Agner Fog的优化手册,特别是3。微体系结构:

“英特尔64和IA-32体系结构优化参考手册”中关于优化分支的部分(第3.4.1节)不再提及它们:


这些前缀是Netburst体系结构的(无害的)残余。在全面优化中,您可以使用它们来对齐代码,但这就是它们目前的优势所在。

gcc不生成前缀是正确的,因为它们对自奔腾4以来的所有处理器都没有影响


但是
\uuu builtin\u expect
还有其他效果,比如在代码或内联决策中将不期望的代码路径从缓存热点位置移开,因此它仍然很有用。

而奔腾4是唯一一个真正尊重分支提示指令的版本,大多数CPU都有某种形式的静态分支预测,可用于实现相同的效果。这个答案与最初的问题有点相切,但我认为这对任何来到这个页面的人来说都是有价值的信息

和(这里已经提到)都对这个特性有很好的描述


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

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

因此,静态预测算法可以预测代码中向前跳转的条件分支不会被采用

这与GCC似乎使用
\uuuu内置
生成的内容一致:“预期的”
返回1
/
返回2
代码被放置在条件分支的未采取路径中,该路径将被静态预测为未采取

此外:

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

  • 预测要采取的无条件分支

  • 预测不采用的间接分支

因此,在GCC将无条件
jmp
s放置到函数末尾的“预期”未采用路径中,这些跳跃将静态预测为已采用(即未跳过)

英特尔还表示:

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

因此,静态预测算法可以预测代码中向后跳转的条件分支

根据Agner Fog的说法,大多数奔腾也遵循以下算法:

在PPro、P2、P3、P4和P4E上,以前未看到的控制传输指令或不在分支目标缓冲区中的控制传输指令,如果向前,将被预测为失败,如果向后(例如循环),将被执行。在这些处理器上,静态预测比动态预测花费更长的时间

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

这些处理器不使用静态预测。预测器只是在第一次看到分支时进行随机预测,这取决于分配给新分支的BTB条目中发生了什么。对跳跃或不跳跃做出正确预测的几率仅为50%,但预测的目标是正确的

与AMD处理器一样,AMD处理器显然:

一个分支在第一次看到时不会被捕获。分支总是在第一次执行之后执行。动态预测仅在以下情况下使用: