mov r64,m64是一个周期还是两个周期的延迟?

时间:2019-01-07 10:44:53

标签: assembly x86 cpu-cache microbenchmark micro-architecture

我在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个周期的延迟。我从多个在线资源中搜索,L1缓存的延迟为4。因此mov本身的延迟应为1。

但是,Agner指令表显示mov r64, m64对于IveBridge具有2个周期的延迟。我不知道其他地方可以找到这种延迟。

上述测量程序是否出错?为什么该程序显示mov延迟是1而不是2?

(通过使用L2缓存,我得到了相同的结果:如果buf+rax是L1缺少L2命中,类似的测量表明mov rax, [buf+rax]具有12个周期的延迟。IvyBridge具有11个周期的L2缓存,因此{ {1}}延迟仍然是1个周期)

1 个答案:

答案 0 :(得分:3)

  

因此mov本身的延迟应为1。

否,mov 负载。数据也不必经过ALU mov操作。


Agner Fog的指令表不包含负载使用延迟(就像您正在测量的那样)。它们位于他的microarch PDF中的“缓存和内存访问”部分,每个君主。例如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负载使用延迟。然后计算存储延迟数,这样像现在这样存储+加载延迟=存储转发延迟。然后,英特尔Atom的存储等待时间= -2,因为它具有3c L1d load-use latency,但是根据Agner的使用者指南,存储转发为1c。

例如,这对于加载到XMM或YMM寄存器中不太容易,但是一旦计算出movq rax, xmm0的延迟,仍然可以实现。 x87寄存器更难,因为无法通过ALU直接将数据从st0获取到eax / rax中,而不是存储/重新加载。但是也许您可以使用fucomi这样的FP比较来做一些事情,该FP直接设置整数FLAGS(在具有P6和更高版本的CPU上)。

不过,对于至少整数加载延迟而言,反映指针追赶延迟要好得多。如果有人提出要为他更新Agner的表,或者他愿意接受这样的更新,则为IDK。不过,对于大多数架构,都需要重新进行测试,以确保您对不同的寄存器集具有正确的加载使用延迟。


脚注1:例如,http://instlatx64.atw.hu不会尝试,只是在等待时间列中说“差异注册表设置”,而有用数据仅在吞吐量列中。但是他们有MOVD r64, xmm+MOVD xmm, r64往返路线,in this case在IvB上总共有2个周期,因此我们可以很确定地知道它们的单向传播只有1c。一种方法不是零。 :P

但是对于加载到整数寄存器中,它们确实显示了MOV r32, [m32]的IvB的4周期加载使用延迟,因为显然它们是在[reg + 0..2047]寻址模式下进行测试的。


其他缓存延迟信息来源:

https://www.7-cpu.com/对于许多其他架构,甚至包括ARM,MIPS,PowerPC和IA-64等许多非x86架构,都有很好的细节。

这些页面还有其他详细信息,例如缓存和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)
  •