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 前缀的后果是什么,具体来说:
/QIntel-jcc-erratum
,可能的解释是什么?NOP 是一条单独的指令,必须单独解码并通过管道。最好用前缀填充指令以实现所需的对齐,而不是插入 NOP,如哪些方法可用于有效扩展现代 x86 上的指令长度?(但仅限于不会在某些无法处理大量前缀的 CPU 上造成严重停顿的方式)。
也许英特尔认为工具链在这种情况下这样做是值得的,因为这实际上是在内循环内部,而不仅仅是内循环外部的 NOP。(在前面的一条指令中添加前缀相对简单。)
我现在有一些数据点。/QIntel-jcc-erratum
AMD 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 上可能安全的一件事是停止使用/Os
flag。发现/Os
flag至少:
尝试以下示例(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 的小循环有时也会触及边界)
归档时间: |
|
查看次数: |
325 次 |
最近记录: |