我在IvyBridge上,我写了以下简单的程序来测量mov
:的延迟
section .bss
align 64
buf: resb 64
section .text
global _start
_start:
mov rcx, 1000000000
xor rax, rax
loop:
mov rax, [buf+rax]
dec rcx,
jne loop
xor rdi, rdi
mov rax, 60
syscall
perf
显示结果:
5,181,691,439 cycles
因此,每次迭代都有5个周期的延迟。我从多个在线资源中搜索,一级缓存的延迟是4。因此CCD_ 3本身的延迟应该是1。
然而,Agner指令表显示mov r64, m64
对于IveBridge有2个周期的延迟。我不知道其他地方可以找到这种延迟。
我在上面的测量程序中出错了吗?为什么这个程序显示mov
延迟是1而不是2?
(我通过使用L2缓存得到了相同的结果:如果buf+rax
是L1缺少L2命中,类似的测量显示mov rax, [buf+rax]
有12个周期延迟。IvyBridge有11个周期延迟的L2缓存,所以mov
延迟仍然是1个周期)
因此mov本身的延迟应该是1。
否,mov
是负载。数据也不需要经过ALUmov
运算。
Agner Fog的指令表不包含负载使用延迟(就像您测量的那样)它们在他的微阵列PDF中的";高速缓存和存储器访问";每个uarch的部分。例如SnB/IvB(第9.13节)具有";级别1数据";用";32kB,8路,64B线路大小,延迟4,每个核心";。
这个4周期延迟是像mov rax, [rax]
这样的依赖指令链的负载使用延迟您正在测量5个周期,因为您使用的是[reg + 0..2047]
以外的寻址模式对于小位移,加载单元推测,直接使用基址寄存器作为TLB查找的输入将给出与使用加法器结果相同的结果。当基准+偏移量在不同于基准的页面时,会有惩罚吗?。因此,您的寻址模式[disp32 + rax]
使用正常路径,在加载端口中开始TLB查找之前,再等待一个周期的加法器结果。
对于不同域之间的大多数操作(如整数寄存器和XMM寄存器),您只能真正测量像movd xmm0,eax
/mov eax, xmm0
这样的往返,很难将其分开并分别计算出每条指令的延迟1。
对于加载,您可以链接到另一个加载来测量缓存加载使用延迟,而不是存储/重新加载链。
由于某种原因,Agner决定只查看他的表的存储转发延迟,并对如何在存储和重新加载之间分配存储转发延迟做出完全任意的选择
(来自他的指令表电子表格的"术语定义"表,位于引言后的左侧)
无法测量内存读取或写入的延迟使用软件方法进行指导。只能测量从地址相同此处测量的实际上不是缓存访问时间,因为在大多数情况下,微处理器足够智能,可以a";存储转发";直接从写入单元到读取单元而不是等待数据进入高速缓存并再次返回。此存储转发过程的延迟是任意划分的表中的写入延迟和读取延迟。但实际上,对性能优化唯一有意义的值是总和写入时间和读取时间的总和。
这显然是不正确的:L1d加载使用延迟是指针在间接级别中追逐的事情。您可能会争辩说,它只是可变的,因为一些加载可能会在缓存中丢失,但如果您要选择要放在表中的内容,您还可以选择L1d加载使用延迟。然后计算存储延迟数,使存储+加载延迟=像现在这样的存储转发延迟。根据Agner的uarch指南,Intel Atom将具有存储延迟=-2,因为它具有3c L1d加载使用延迟,但具有1c存储转发。
例如,这对于加载到XMM或YMM寄存器来说不太容易,但一旦计算出movq rax, xmm0
的延迟,这仍然是可能的。x87寄存器更难,因为没有办法直接通过ALU将数据从st0
获取到eax
/rax
,而不是存储/重新加载。但是,也许您可以使用像fucomi
这样的FP比较来直接设置整数FLAGS(在具有它的CPU上:P6及更高版本)。
尽管如此,至少整数加载延迟反映指针追逐延迟会好得多。IDK是否有人愿意为他更新阿格纳的表格,或者他是否会接受这样的更新。不过,需要对大多数uarche进行新的测试,才能确保不同寄存器集具有正确的加载使用延迟。
脚注1:例如,http://instlatx64.atw.hu没有尝试,只是说"diff.reg.set";在latency列中,有用的数据仅在throughput列中。但他们有MOVD r64, xmm+MOVD xmm, r64
往返的线路,在这种情况下,IvB上总共有2个循环,所以我们可以非常确信他们每次只有1c。不是零单向。:P
但对于加载到整数寄存器的情况,它们确实显示了MOV r32, [m32]
的IvB的4周期加载使用延迟,因为显然它们使用[reg + 0..2047]
寻址模式进行测试。
https://uops.info/非常好,但在延迟方面给出了相当宽松的界限:IIRC,他们构造了一个往返循环(例如存储和重新加载,或xmm->integer和integer->xmm),然后给出了延迟的上限,假设每隔一步只有1个周期。请参阅多个值或范围对单个指令的延迟意味着什么?了解更多信息。
缓存延迟信息的其他来源:
https://www.7-cpu.com/为许多其他uarche提供了良好的细节,甚至包括许多非x86,如ARM、MIPS、PowerPC和IA-64。
页面还有其他详细信息,如缓存和TLB大小、TLB定时、分支未命中实验结果和内存带宽。缓存延迟详细信息如下:
(从他们的Skylake页面)
- 一级数据缓存延迟=4个周期,用于通过指针进行简单访问
- L1数据缓存延迟=5个周期,用于复杂地址计算的访问(
size_t n, *p; n = p[n]
)- 二级缓存延迟=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)