因此 mov 本身的延迟应该是 1。
不,mov 是负载。数据也不需要经过 ALU mov 操作。
Agner Fog 的指令表不包含加载使用延迟(就像您正在测量的那样)。它们在他的 microarch PDF 中的“缓存和内存访问”部分的表中每个 uarch。例如SnB/IvB(第 9.13 节)有一个“1 级数据”行,其中包含“32 kB,8 路,64 B 行大小,延迟 4,每个内核”。
这 4 个周期的延迟是像 mov rax, [rax] 这样的依赖指令链的加载使用延迟。 您正在测量 5 个周期,因为您使用的是 [reg + 0..2047] 以外的寻址模式。 对于小的位移,加载单元推测直接使用基址寄存器作为 TLB 查找的输入将给出结果与使用加法器结果相同。 Is there a penalty when base+offset is in a different page than the base?。因此,您的寻址模式[disp32 + rax] 使用正常路径,在加载端口中开始 TLB 查找之前,再等待一个周期等待加法器结果。
对于不同域之间的大多数操作(如整数寄存器和 XMM 寄存器),您只能真正测量像 movd xmm0,eax / mov eax, xmm0 这样的往返,并且很难将其分开并弄清楚延迟是多少每条指令分别1.
对于加载,您可以链接到另一个加载来测量缓存加载使用延迟,而不是存储/重新加载链。
Agner 出于某种原因决定只查看他的表的存储转发延迟,并完全任意选择如何在存储之间拆分存储转发延迟和重新加载。
(来自他的指令表电子表格的“术语定义”表,在介绍之后的左侧)
无法测量内存读取或写入的延迟
用软件方法进行教学。只能测量
内存写入的组合延迟,然后是从内存读取
同一个地址。 这里测量的实际上并不是缓存访问
时间, 因为在大多数情况下,微处理器足够聪明,可以使
直接从写入单元到读取单元的“存储转发”
而不是等待数据进入缓存并再次返回。
这个存储转发过程的延迟是任意划分的
表中的写入延迟和读取延迟。但实际上,
对性能优化有意义的唯一值是总和
写入时间和读取时间。
这显然是不正确的:L1d 加载使用延迟是指针追逐通过间接级别的事情。您可能会争辩说它只是可变的,因为某些负载可能会在缓存中丢失,但是如果您要选择要放入表中的内容,则最好选择 L1d 负载使用延迟。然后计算存储延迟数,使得存储+加载延迟=存储转发延迟,就像现在一样。英特尔凌动的存储延迟 = -2,因为它具有 3c L1d load-use latency,但根据 Agner 的 uarch 指南进行 1c 存储转发。
例如,这对于加载到 XMM 或 YMM 寄存器来说不太容易,但在计算出 movq rax, xmm0 的延迟后仍然可能。 x87 寄存器更难,因为无法通过 ALU 直接将数据从 st0 获取到 eax/rax,而不是通过存储/重新加载。但也许您可以使用像 fucomi 这样直接设置整数 FLAGS 的 FP 比较来做一些事情(在具有它的 CPU 上:P6 及更高版本)。
不过,至少整数加载延迟来反映指针追逐延迟会好得多。 IDK 如果有人提议为他更新 Agner 的表格,或者他是否愿意接受这样的更新。不过,需要对大多数 uarch 进行新的测试,以确保您对不同的寄存器集具有正确的加载使用延迟。
脚注 1:例如,http://instlatx64.atw.hu 不会尝试,只是在延迟列中显示“diff.reg.set”,而有用数据仅在吞吐量列中。但是他们有 MOVD r64, xmm+MOVD xmm, r64 往返线路,in this case 在 IvB 上总共有 2 个周期,所以我们可以非常确信它们单程只有 1c。不是零一种方式。 :P
但是对于整数寄存器的加载,它们确实显示了 IvB 的 MOV r32, [m32] 的 4 周期加载使用延迟,因为显然它们使用 [reg + 0..2047] 寻址模式进行测试。
https://uops.info/ 相当不错,但在延迟方面给出了相当宽松的界限:IIRC,它们构造了一个往返循环(例如存储和重新加载,或 xmm->integer 和 integer-> xmm),然后给出延迟的上限,假设每隔一个步骤只有 1 个周期。请参阅What do multiple values or ranges means as the latency for a single instruction? 了解更多信息。
缓存延迟信息的其他来源:
https://www.7-cpu.com/ 为许多其他 uarch 提供了很好的详细信息,甚至包括许多非 x86,如 ARM、MIPS、PowerPC 和 IA-64。
这些页面还有其他详细信息,例如缓存和 TLB 大小、TLB 时间、分支未命中实验结果和内存带宽。缓存延迟详细信息如下所示:
(from their Skylake page)
- L1 数据缓存延迟 = 4 个周期,用于通过指针进行简单访问
- L1 数据缓存延迟 = 5 个周期,用于复杂地址计算的访问 (
size_t n, *p; n = p[n])。
- L2 缓存延迟 = 12 个周期
- L3 缓存延迟 = 42 个周期(核心 0)(i7-6700 Skylake 4.0 GHz)
- L3 缓存延迟 = 38 个周期(i7-7700K 4 GHz,Kaby Lake)
- RAM 延迟 = 42 个周期 + 51 ns (i7-6700 Skylake)