【问题标题】:Intel x86 0x2E/0x3E Prefix Branch Prediction actually used?Intel x86 0x2E/0x3E Prefix Branch Prediction 实际使用过吗?
【发布时间】:2012-12-29 06:18:53
【问题描述】:

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

Group 2 > Branch Hints

    0x2E: Branch Not Taken
    0x3E: Branch Taken

这些允许对跳转指令进行显式分支预测(操作码如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;
}

反汇编成以下内容:

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:作为更新添加。
  • 通常,__builtin_expect 结构只会影响 GCC 优化器。 (效果非常微妙。)您是否尝试过指定 -march-mcpu 标志让 GCC 知道您的 CPU 支持这些前缀?
  • @duskwuff:尝试使用-march=corei7 并给出相同的输出
  • 好的,在这种情况下,我怀疑 GCC 根本不会生成 2E/3E 前缀。

标签: gcc x86 x86-64 branch-prediction


【解决方案1】:

这些指令前缀对现代处理器(任何比 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-architectures-optimization-manual.pdf

这些前缀是 Netburst 架构的(无害的)遗物。在全面优化中,您可以使用它们来对齐代码,但这就是它们现在的全部优点。

【讨论】:

    【解决方案2】:

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

    __builtin_expect 有其他效果,例如将不期望的代码路径从代码中的高速缓存热位置移开或内联决策,因此它仍然有用。

    【讨论】:

      【解决方案3】:

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

      Intel optimisation guideAgner Fog's guide(这里已经提到过)都对这个特性有很好的描述。


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

      使条件分支之后的贯穿代码成为具有正向目标

      的分支的可能目标

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

      这与 GCC 似乎使用 __builtin_expect 生成的内容一致:“预期的”return 1 / return 2 代码放置在条件分支的未采用路径中,这将被静态预测为未采用- 拍摄。

      另外:

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

      • 预测要采用的无条件分支。

      • 预测不采用的间接分支。

      因此,在 GCC 将无条件 jmps 置于函数末尾的“预期”未采用路径中,这些跳转将被静态预测为已采用(即未跳过)。

      英特尔还说:

      使条件分支之后的贯穿代码成为具有向后目标

      的分支的不太可能的目标

      因此,在代码中向后跳转的条件分支被静态预测算法预测为被采用。

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

      在 PPro、P2、P3、P4 和 P4E 上,一个以前没有见过的或不在分支目标缓冲区中的控制转移指令,如果前进,预计会失败,如果它倒退(例如循环)。在这些处理器上,静态预测比动态预测需要更长的时间。

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

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

      显然 AMD 处理器也是如此:

      一个分支在第一次出现时被预测为没有被采用。一个分支总是在第一次被采用之后被预测。动态预测仅在已采用分支然后未采用之后使用。分支提示前缀无效。

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

      【讨论】:

      • 像往常一样,事情很复杂,现代英特尔可能仍会使用一些静态预测,according to Matt Godbolt's experiments and research
      • 您说“这与 GCC 似乎使用生成的内容一致”,当我们查看 OP 的程序集时,这似乎是错误的 AFAICT。但我认为这里的问题是 OP 没有开启任何优化。打开优化后,它将生成一个无分支版本(甚至更好)。但是,如果我们更改示例,因为它根据参数调用某个函数(无法优化为数学技巧),我们开始看到 GCC 通过指令排序尊重提示。所以,从 -O1 开始,这个提示会有所不同。
      【解决方案4】:

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

      一些早期的微架构使用这些作为分支提示,但最近 代没有,它们被保留以供将来提示使用。

      【讨论】:

      • 这更像是一个评论而不是一个答案,因为现有的答案已经说明了这一点。有趣的是,英特尔确实将它们正式保留以供将来使用。
      猜你喜欢
      • 2013-01-13
      • 2014-04-15
      • 2013-01-18
      • 2017-02-08
      • 2011-11-10
      • 1970-01-01
      • 2018-01-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多