英特尔 JCC 勘误表 - 用于缓解的前缀有什么影响?

Ale*_*iev 5 x86 assembly intel cpu-architecture micro-optimization

英特尔建议使用指令前缀来减轻 JCC 勘误对性能的影响。

如果使用 MSVC 进行编译,则会/QIntel-jcc-erratum遵循建议,并插入带前缀的指令,如下所示:

3E 3E 3E 3E 3E 3E 3E 3E 3E 48 8B C8   mov rcx,rax ; with redundant 3E prefixes
Run Code Online (Sandbox Code Playgroud)

他们说,当前缀不可用时,MSVC 会求助于 NOP。

Clang 有-mbranches-within-32B-boundaries这个选项,nop如果需要,它更喜欢多字节(https://godbolt.org/z/399nc5Msq通知xchg ax, ax

3E 前缀的后果是什么,具体来说:

  • 为什么 Intel 推荐这个而不是多字节 NOP?
  • 对于未受影响的 CPU 会产生什么后果?
  • 据报道,AMD 上的程序运行速度更快/QIntel-jcc-erratum,可能的解释是什么?

Ale*_*iev 2

NOP 是一条单独的指令,必须单独解码并通过管道。最好用前缀填充指令以实现所需的对齐,而不是插入 NOP,如哪些方法可用于有效扩展现代 x86 上的指令长度(但仅限于不会在某些无法处理大量前缀的 CPU 上造成严重停顿的方式)。

也许英特尔认为工具链在这种情况下这样做是值得的,因为这实际上是在内循环内部,而不仅仅是内循环外部的 NOP。(在前面的一条指令中添加前缀相对简单。)


我现在有一些数据点。/QIntel-jcc-erratumAMD FX 8300的基准测试结果很差

对于特定基准测试,速度下降了十进制数量级,而英特尔 Skylake 在同一基准测试中的优势约为 20%。这与彼得的评论一致:

我检查了 Agner Fog 的微架构指南,AMD Zen 对于单条指令上任意数量的前缀都没有问题,就像 Core2 以来的主流 Intel 一样。AMD Bulldozer 系列对于解码具有 3 个以上前缀的指令有“非常大”的惩罚,例如 4-7 个前缀需要 14-15 个周期

认为 Bulldozer 系列已经过时到不再关心它是有一定道理的,尽管肯定仍然有一些 APU 台式机和笔记本电脑,但它们肯定会在循环中显示出较大的回归,其中编译器在其中放置 4 个或更多前缀热内循环内的指令(包括现有的前缀,如 REX 或 66h)。比 SKL 上 MITE 传统解码的 3% 差得多。

虽然推土机系列确实已经过时了,但我认为我无法承受这么大的影响。我还担心其他 CPU 可能会以同样的方式因额外的前缀而阻塞。所以我的结论是不要用于/QIntel-jcc-erratum通用目标软件。除非在特定的翻译单元中启用它并动态调度到那里,否则大多数时候这很麻烦。


在 MSVC 上可能安全的一件事是停止使用/Osflag。发现/Osflag至少:

  • 避免跳转表而有利于条件跳转
  • 避免循环开始填充

尝试以下示例(https://godbolt.org/z/jvezPd9jM):

void loop(int i, char a[], char b[])
{
    char* stop = a + i;
    while (a != stop){
        *b++ = *a++;
    }
}

void jump_table(int i, char a[], char b[])
{
    switch (i)
    {
                            case 7: 
            a[6] = b[6];    case 6: 
            a[5] = b[5];    case 5: 
            a[4] = b[4];    case 4: 
            a[3] = b[3];    case 3: 
            a[2] = b[2];    case 2: 
            a[1] = b[1];    case 1: 
            a[0] = b[1];    case 0:  break;
            default: __assume(false);
    }
}
Run Code Online (Sandbox Code Playgroud)

这会导致更频繁地遇到 JCC 性能问题(避免跳转表产生一系列 JCC,并且避免对齐会使小于 16b 的小循环有时也会触及边界)