我记得在我的架构课上认为L1缓存命中时间是1个周期(即与寄存器访问时间相同),但在现代x86处理器上是否真的如此?
L1缓存命中需要多少个时钟周期?它与寄存器访问相比如何?
我记得在我的架构课上认为L1缓存命中时间是1个周期(即与寄存器访问时间相同),但在现代x86处理器上是否真的如此?
L1缓存命中需要多少个时钟周期?它与寄存器访问相比如何?
这是一篇关于这个主题的好文章:
http://arstechnica.com/gadgets/reviews/2002/07/caching.ars/1
回答你的问题 - 是的,缓存命中的成本与寄存器访问大致相同。 当然,缓存未命中会非常昂贵 ;)
PS:
尽管具体数字会有所不同,但这个链接提供了一些很好的基本数据:
Approximate cost to access various caches and main memory?
Core i7 Xeon 5500 Series Data Source Latency (approximate)
L1 CACHE hit, ~4 cycles
L2 CACHE hit, ~10 cycles
L3 CACHE hit, line unshared ~40 cycles
L3 CACHE hit, shared line in another core ~65 cycles
L3 CACHE hit, modified in another core ~75 cycles remote
L3 CACHE ~100-300 cycles
Local DRAM ~30 ns (~120 cycles)
Remote DRAM ~100 ns
附: 这些数字代表的是更老、更慢的 CPU,但比例基本保持不变:
http://arstechnica.com/gadgets/reviews/2002/07/caching.ars/2
Level Access Time Typical Size Technology Managed By
----- ----------- ------------ --------- -----------
Registers 1-3 ns ?1 KB Custom CMOS Compiler
Level 1 Cache (on-chip) 2-8 ns 8 KB-128 KB SRAM Hardware
Level 2 Cache (off-chip) 5-12 ns 0.5 MB - 8 MB SRAM Hardware
Main Memory 10-60 ns 64 MB - 1 GB DRAM Operating System
Hard Disk 3M - 10M ns 20 - 100 GB Magnetic Operating System/User
吞吐量和延迟是不同的概念。你不能简单地将周期成本相加。对于吞吐量,参见最近 CPU 架构代的每个时钟周期的加载/存储- 对于大多数现代微架构,每个时钟周期可实现 2 次加载 吞吐量。同时参见缓存为什么如此快?以了解加载/存储执行单元的微架构细节,包括显示限制它们可以跟踪的内存级并行性的加载/存储缓冲区。本回答的其余部分将仅关注延迟,这与涉及指针追踪(如链表和树)的工作负载有关,并且需要隐藏多少延迟才能实现乱序执行。(L3 缓存未命中通常太长,无法完全隐藏。)
Single-cycle cache latency在低时钟速度的简单顺序流水线上曾经是一个问题(因此每个周期的纳秒数更多),特别是在较简单的缓存(较小、不那么关联,并且对于不纯粹虚拟寻址的缓存具有较小的TLB)中。例如,像MIPS I这样的经典的5级RISC流水线假定在缓存命中时内存访问需要1个周期,在EX中进行地址计算,在单个MEM流水线阶段中进行内存访问,然后进行WB。add
/ or
/ and
这样的简单指令可以以非常快的速度运行,仍然具有1个周期的延迟,但时钟速度很高。
关于周期计数和乱序执行的更多细节,请参见Agner Fog的微架构pdf以及x86标签wiki中的其他链接。
Intel Haswell的L1加载使用延迟对于指针跳转是4个周期,这是现代x86 CPU的典型特征。即在一个循环中运行mov eax,[eax]
有多快,指向自身的指针(或者对于命中缓存的链表,可以轻松进行微基准测试,使用闭环)。另请参见当基址+偏移量与基址不在同一页时是否会有惩罚?。那个4个周期的延迟特殊情况仅适用于指针直接来自另一个加载的情况,否则为5个周期。
在Intel CPU中,SSE / AVX向量的加载使用延迟比普通指令高1个周期。
http://www.7-cpu.com/cpu/Haswell.html有一张缓存延迟表格(我会在这里复制),还有一些其他实验数据,包括L2-TLB命中延迟(在L1DTLB未命中时)。
"Intel i7-4770 (Haswell),3.4 GHz(Turbo Boost关闭),22纳米。内存:32 GB(PC3-12800 cl11 cr2)。 L1数据缓存= 32 KB,64 B / line,8-WAY。 L1指令缓存= 32 KB,64 B / line,8-WAY。 L2缓存= 256 KB,64 B / line,8-WAY L3缓存= 8 MB,64 B / line。 L1数据缓存延迟=通过指针进行简单访问的4个周期(mov rax,[rax]
)
L1数据缓存延迟=通过复杂地址计算进行访问的5个周期(mov rax,[rsi + rax * 8]
)。
L2缓存延迟=12个周期。
L3缓存延迟=36个周期。
RAM延迟=36个周期+57纳秒。"2) 强制编译器将频繁访问的数据存储在处理器寄存器中,以进行粗暴的汇编优化是没有任何意义的。如果它们确实经常被访问,它们将存在于L1缓存中,并且由于这个原因,将具有与处理器寄存器相同的访问成本。
looptop:
/ inc [mem]
/ dec ecx
/jnz looptop
在大多数最新的x86上,由于存储转发的瓶颈,每6个周期运行一次。但是,如果使用inc edx
(并且在整个循环中没有存储/重新加载瓶颈),它可以以1/clock的速度运行。也许您在循环内部使用了内联汇编,并且没有启用优化,因此编译器在您的汇编周围创建了一个循环,该循环在内存目标增量上形成了瓶颈。在这种情况下,是的,通过避免内存无法获得太多收益,因为瓶颈仍然存在。 - Peter Cordes如果我没记错的话,大约需要1-2个时钟周期,但这只是一个估计值,新一代缓存可能会更快。这是我所拥有的一本《计算机体系结构》书籍中的信息,供AMD使用,因此Intel可能会略有不同,但我会将其限制在5到15个时钟周期之间,这似乎是一个很好的估计。
编辑:糟糕,L2需要10个时钟周期才能访问TAG,而L1需要1到2个周期,我的错误 : \
mov eax,[eax]
的循环中,如果指针指向自身,则每个迭代需要的周期数为几个?请参阅 http://www.7-cpu.com/cpu/Haswell.html 顶部的一些数字。 - Peter Cordes
inc eax
具有1个周期延迟,inc [mem]
具有6个周期延迟(ALU +存储器转发)。 - Peter Cordes