Intel x86中的0x2E/0x3E前缀分支预测是否实际使用?

35

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

Group 2 > Branch Hints

    0x2E: Branch Not Taken
    0x3E: Branch Taken

这些指令允许对跳转指令(例如Jxx)进行显式分支预测。

我记得几年前阅读过一篇文章,称在 x86 上,使用 gcc 分支预测内置函数时,显式分支预测基本上没有用。

现在我不确定这些 x86 分支提示是新功能还是实际上基本没有用。

有人能否解决这个问题?

也就是说:gcc 的分支预测函数是否会产生这些 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;
}

分解成以下内容:

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

我没有看到2E或3E?也许gcc因某种原因省略了它们?


gcc没有选项可以让它输出汇编代码吗?您能否使用这些内置函数编写一个简短的程序并查看它是否生成了这些代码?(我知道这并没有回答问题的另一半) - Damien_The_Unbeliever
1
@Damien_The_Unbeliever:作为更新添加。 - Andrew Tomazos
通常情况下,__builtin_expect 结构只影响 GCC 优化器。 (影响相当微妙。)您是否尝试指定 -march-mcpu 标志,让 GCC 知道您的 CPU 支持这些前缀? - user149341
@duskwuff:尝试使用“-march=corei7”,输出相同。 - Andrew Tomazos
好的,那么我怀疑GCC只是没有生成2E/3E前缀。 - user149341
4个回答

35
这些指令前缀对于现代处理器(任何比Pentium 4更新的版本)没有影响。它们只占用一个字节的代码空间,因此,不生成它们是正确的选择。
有关详细信息,请参阅Agner Fog的优化手册,特别是第3章微架构:http://www.agner.org/optimize/ 在“Intel® 64和IA-32体系结构优化参考手册”中,不再在优化分支的部分(第3.4.1节)提到它们: http://www.intel.de/content/dam/doc/manual/64-ia-32-architectures-optimization-manual.pdf 这些前缀是Netburst架构的(无害的)遗物。在全面优化中,您可以使用它们来对齐代码,但现在它们唯一有用的就是对齐代码了。

16

gcc正确地不生成前缀,因为自 Pentium 4 以来对所有处理器都没有影响。

但是__builtin_expect具有其他影响,例如将未预期的代码路径移出缓存热点位置或内联决策,因此仍然很有用。


14

虽然 Pentium 4 是唯一真正支持分支提示指令的一代,但大多数 CPU 都具有某种形式的静态分支预测,可用于实现相同的效果。这个答案与原问题有些离题,但我认为这对任何来到本页的人都是有价值的信息。

英特尔优化指南Agner Fog 的指南(已在此提到)都对此功能有很好的描述。


关于高于 Core 2 的世代,英特尔有如下说法:

让条件分支后面的代码成为带有前向目标的分支的可能目标

所以,在代码中向前跳转的条件分支被预测为不取,使用静态预测算法。

这与 GCC 使用 __builtin_expect 生成的代码一致:'expected' return 1 / return 2 代码被放置在条件分支的不取路径上,这些路径将被静态预测为不取。

此外:

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

  • 预测无条件分支被执行。

  • 预测间接分支不会被执行。

所以,在 GCC 将无条件的 jmp 放置在 'expected' 不取路径的情况下,这些跳转将被静态预测为已执行(即不会被跳过)。

英特尔还说:

使有条件分支的跳转代码成为带有反向目标分支的不太可能目标。

因此,向后跳转的有条件分支被静态预测算法预测将被执行。

根据Agner Fog的说法,大多数Pentiums也遵循此算法:

在PPro、 P2、 P3、 P4和P4E上,控制传输指令(该指令未被看到过或者不在Branch Target Buffer中)如果向前跳转,则预测会落空;如果向后跳转(例如,循环),则预测将被执行。在这些处理器上,静态预测比动态预测需要更长的时间。

然而,Core 2家族和Pentium M则具有完全不同的策略:

这些处理器不使用静态预测。第一次看到分支时,预测器只是根据新分支分配的BTB条目中存在的内容随机进行预测。正确预测跳转或不跳转的概率只有50%,但预测的目标是正确的。

另外,AMD处理器显然也是如此:

第一次看到分支时,预测将不会被执行。在分支第一次被执行后,预测将总是被执行。仅在分支被执行后未被执行时才使用动态预测。分支提示前缀没有效果。

还有一个额外的因素需要考虑:CPU通常喜欢按线性顺序执行,因此正确预测的跳转分支通常比正确预测的不跳转分支更加昂贵。


1
通常情况下,事情很复杂,而现代英特尔可能仍然使用一些静态预测,根据Matt Godbolt的实验和研究。 - Peter Cordes
你说“这与GCC似乎生成的内容一致”,但根据我对OP的程序汇编的了解,这个观点是错误的。但我认为这里的问题是OP没有打开任何优化选项。打开优化选项后,它将生成一个无分支版本(效果更好)。但如果我们改变示例,使其根据参数调用某些函数(无法优化为数学技巧),我们会开始看到GCC通过指令排序来尊重提示。因此,从-O1开始,这种提示会产生影响。 - sellibitze

3

英特尔® 64和IA-32体系结构软件开发人员手册 -> 卷2:指令集参考,A-Z -> 第2章:指令格式 -> 2.1 受保护模式、实地址模式和虚拟8086模式下的指令格式 -> 2.1.1 指令前缀

一些早期的微架构将它们用作分支提示,但最近的几代已不再使用它们,并为未来的提示使用保留。


这更像是一条评论而不是答案,因为现有的答案已经说过了。有趣的是英特尔确实将它们保留给未来使用。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接