【问题标题】:x86 opcode alignment references and guidelinesx86 操作码对齐参考和指南
【发布时间】:2011-01-29 22:53:05
【问题描述】:

我正在 JIT 编译器中动态生成一些操作码,我正在寻找操作码对齐的指南。

1) 我读过 cmets 通过在调用后添加 nop 来简要“推荐”对齐

2) 我还阅读了有关使用 nop 优化序列以实现并行性的信息。

3) 我读到操作对齐对“缓存”性能有好处

通常这些 cmets 不提供任何支持性参考。阅读博客或评论说“这样做是个好主意”是一回事,但实际编写实现特定操作序列并在线实现大多数材料(尤其是博客)的编译器是另一回事用于实际应用。所以我相信自己会发现事情(反汇编等,看看现实世界的应用程序做了什么)。这是我需要一些外部信息的一种情况。

我注意到编译器通常会在之前的任何指令序列之后立即启动奇数字节指令。因此,在大多数情况下,编译器并没有特别注意。我在这里或那里看到“nop”,但通常似乎很少使用 nop,如果有的话。操作码对齐有多重要?您能否为我可以实际用于实施的案例提供参考?谢谢。

【问题讨论】:

  • 作为一个独立的答案,我接受了 DigitalRoss',但两者都很好。 Martin 提供的链接被证明是最有用的(一个真正的金矿),并且回答了我今晚的很多后续问题。谢谢你们。

标签: assembly x86 x86-64 memory-alignment micro-optimization


【解决方案1】:

我建议不要插入除分支目标对齐之外的 nop。在某些特定的 CPU 上,分支预测算法可能会惩罚控制传输以控制传输,因此 nop 可能能够充当标志并反转预测,但否则它不太可能有帮助。

无论如何,现代 CPU 都会将您的 ISA 操作转换为 micro-ops。这可能会使经典的对齐技术变得不那么重要,因为微操作转码器可能会忽略 nop 并改变秘密真正机器操作的大小和对齐方式。

但是,出于同样的原因,基于第一原则的优化应该不会造成任何伤害。

理论是通过在缓存行边界开始循环来更好地利用缓存。如果循环从缓存行的中间开始,那么缓存行的前半部分将不可避免地加载并在循环期间保持加载,如果循环长于 1/ 则这将浪费缓存中的空间2 个缓存行。

另外,对于分支目标,缓存行的初始加载会在目标对齐时加载指令流的最大前向窗口。

关于用 nop 分隔不是分支目标的内联指令,在现代 CPU 上这样做的理由很少。 (曾经有一段时间,RISC 机器有 delay slots,这通常会导致在控制传输后插入 nop。)解码指令流很容易流水线化,如果架构具有奇字节长度的操作,您可以放心,它们被合理解码。

【讨论】:

  • +1 感谢您的另一个好答案。是的,我刚刚从 Martin 的链接中阅读了有关微操作和重新排序的信息。
【解决方案2】:

所有这些微优化的最佳来源是Agner Fog's x86 optimization manuals。这些文件应该有你需要的一切,然后是一些。 :)

我能想到的一件事是对齐循环,以便循环代码不会跨越任何缓存行边界,即循环小于 64 字节,并且从可被 64 整除的地址开始。然后整个循环将适合一个缓存行,并为其他事情留下更多的缓存行。不过,我怀疑这在现实世界的程序中是否重要,无论该特定循环碰巧有多“热”。

【讨论】:

    猜你喜欢
    • 2011-09-18
    • 1970-01-01
    • 2022-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-03
    相关资源
    最近更新 更多