我正在查看Intel Architectures Optimization Reference Manual 2017 (第 759 页)。我正在寻找 Haswell 和 Skylake 架构。该表中故意省略了 MOV、PUSH、JMP、CALL 指令。没有给出延迟信息。这是为什么?尽管如此,第 776 页上给出了 Atom 处理器的指令延迟。
有趣的是 2012 optimization manual来自英特尔的指令有 MOV
、PUSH
和 CALL
指令延迟。
阿格纳的instruction tables为 MOV
和 PUSH
提供延迟,但会跳过 JMP
和 CALL
等控制指令。知道为什么吗?
最佳答案
简单的回答是,对于控制指令以及许多类型的独立 mov 指令来说,在实践中延迟实际上并不是一个有意义的指标。
在您提到的评论中:
I was referring to Intel's manual for control instructions. What I mean by average latency for control instructions is that we get some data for number of instructions retired over a period of time and then take time/(number of instructions).
当我们谈论指令的延迟时,我们通常指的是从输入生成结果所需的时间,而不是多少个结果可以在给定的时间内生产。这相当于在一个城市生一个 child 需要 9 个月(延迟)与一个月内出生 100 个婴儿(吞吐量)之间的差异。
测量延迟的常用方法是将一系列指令链接在一起,其中一条指令的输出用作下一条指令的输入。由于它们是相关的,因此您会得到延迟测量,因为它们是串行执行的。例如,如果您想测量 add
的延迟,您可以使用如下序列:
add eax, eax
add eax, eax
add eax, eax
...
注意输出寄存器eax
如何在输入中反馈到下一个add
。
现在,控制流指令没有明显的显式“输出”可以反馈到其输入中。它们的输出是指令流的变化,但尚不清楚如何将其反馈到下一条指令中。此外,控制流的整个机制通常被解耦到分支预测引擎中,该引擎试图在执行控制流指令之前正确引导前端,从而在延迟方面进一步困惑。
您最多可以谈论这些结构的吞吐量:现代英特尔通常可以每个周期执行两个分支,最多可以采用其中一个。
您在使用 mov
指令传入或传出内存时遇到了同样的问题。在这里,输出和输入是明确的,但它们位于不同的域(寄存器与内存)。因此,您不一定将存储指令的输出提供给后续存储指令,因为存储具有“内存”输出但“寄存器”输入。您可以做的是将同一位置上的加载和存储指令对链接在一起,并获得该对的组合延迟:在现代 Intel 上,这通常运行 3 到 7 个周期,具体取决于寻址模式和其他因素。
特别是对于加载,您可以在下一次加载的地址计算中使用加载的结果(寄存器域),从而给您带来加载到加载地址的延迟(有些人称之为加载到使用) ,但我认为这令人困惑),在现代英特尔上通常最多 4 个周期,对于复杂的寻址模式或向量加载,每个周期还需要 1 个额外的周期。
对于寄存器到寄存器的移动,延迟通常为 0 个周期(由于 mov 消除),或者当 mov 无法消除时为 1 个周期。
这些问题可能就是您在英特尔指南甚至 Agner 等其他指南中看不到这些结构的延迟数据的原因。
关于x86 - 英特尔较新架构的控制指令和移动指令延迟是多少?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49306693/