在x86架构中,L1缓存命中与寄存器的循环/成本比是多少?

35

我记得在我的架构课上认为L1缓存命中时间是1个周期(即与寄存器访问时间相同),但在现代x86处理器上是否真的如此?

L1缓存命中需要多少个时钟周期?它与寄存器访问相比如何?


1
它因处理器而异,但我不知道有任何处理器与寄存器一样快 - 大约比寄存器慢1到5个时钟是相当典型的。 - Jerry Coffin
3
我不知道有哪些架构中L1具有单周期延迟。此外,我也不知道有哪些x86架构中,寄存器访问本身具有可测量的延迟(由于其他因素可能会感知到一些延迟)。 - harold
1
请参阅http://www.7-cpu.com/cpu/Haswell.html:其中包含一些缓存和TLB延迟数字以及一些实验数据。还可以查看[Agner Fog的微架构pdf](http://agner.org/optimize/),以及[x86标签wiki](http://stackoverflow.com/tags/x86/info)中的其他链接。 Haswell的L1加载使用延迟为4个周期,这是现代x86 CPU的典型值。存储器重新加载延迟为5个周期,并且与缓存命中或未命中无关(它是存储器转发,而不是缓存)。正如Harold所说,寄存器访问时间为0个周期(例如,inc eax具有1个周期延迟,inc [mem]具有6个周期延迟(ALU +存储器转发)。 - Peter Cordes
4个回答

47

这是一篇关于这个主题的好文章:

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

5
访问L3缓存需要100-300个时钟周期,而本地DRAM访问仅需要约120个周期,这可能怎么可能呢?这是否意味着L3缓存比用于主存储器的DRAM慢两倍以上? - user2316602
@user2316602:对我来说似乎是虚假的,除非该表行应该用于不同插座中CPU的L3缓存。 (这是Nehalem Xeon系统,因此主内存和L3是NUMA。) - Peter Cordes
在英特尔CPU上,L3(和DRAM)延迟较低的核心数较少,例如双核或四核i7:环形总线上的跳数较少,uncore更简单。请参见https://dev59.com/UlkS5IYBdhLWcg3w8ai-。最大的Xeon的L3命中延迟比Woodcrest表格差得多。 - Peter Cordes

15

吞吐量和延迟是不同的概念。你不能简单地将周期成本相加。对于吞吐量,参见最近 CPU 架构代的每个时钟周期的加载/存储- 对于大多数现代微架构,每个时钟周期可实现 2 次加载 吞吐量。同时参见缓存为什么如此快?以了解加载/存储执行单元的微架构细节,包括显示限制它们可以跟踪的内存级并行性的加载/存储缓冲区。本回答的其余部分将仅关注延迟,这与涉及指针追踪(如链表和树)的工作负载有关,并且需要隐藏多少延迟才能实现乱序执行。(L3 缓存未命中通常太长,无法完全隐藏。)

Single-cycle cache latency在低时钟速度的简单顺序流水线上曾经是一个问题(因此每个周期的纳秒数更多),特别是在较简单的缓存(较小、不那么关联,并且对于不纯粹虚拟寻址的缓存具有较小的TLB)中。例如,像MIPS I这样的经典的5级RISC流水线假定在缓存命中时内存访问需要1个周期,在EX中进行地址计算,在单个MEM流水线阶段中进行内存访问,然后进行WB。
现代高性能CPU将流水线分成更多阶段,使得每个周期变得更短。这使得像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个周期。


Store-reload延迟为5个周期,与缓存命中或未命中无关(这是store-forwarding,从store buffer读取尚未提交到L1d cache的store数据)。
正如Harold所评论的,寄存器访问时间为0个周期。例如:
- inc eax的延迟为1个周期(仅为ALU操作) - add dword [mem],1的延迟为6个周期,直到从[dword mem]加载准备就绪(ALU + store-forwarding)。例如,将循环计数器保留在内存中会限制每6个周期一次的循环。 - mov rax,[rsi]从rsi准备好到L1 hit上rax准备好的延迟为4个周期(L1 load-use延迟)。

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纳秒。"
顶级基准测试页面是http://www.7-cpu.com/utils.html,但仍然没有真正解释不同测试大小的含义,但代码可用。测试结果包括 Skylake,在此测试中与Haswell几乎相同。@paulsm4的答案有一个多插槽Nehalem Xeon的表格,包括一些远程(其他插槽)内存/L3数字。

由于某种原因,我从这些网站上看不到L1i延迟值。在P6上,命中/ITLB命中的延迟为2个周期,后来的微架构上它仍然是2个周期吗?我希望如此。 - Lewis Kelsey
@LewisKelsey:好问题,但我不知道。随着时钟频率的提高和32KiB / 8路大小(与IceLake之前的L1d相同),我怀疑它是否保持了低延迟。在阶段之间进行缓冲,并具有良好的分支预测,可以帮助隐藏即使在高吞吐量代码中也存在的气泡。此外,最热门的代码通常从uop缓存中运行,这意味着在许多情况下,L1i命中延迟并不重要。我预计4或5个周期的延迟,如果它可以是只读单端口,并且不需要支持非对齐加载,则可能为3个周期。而且不需要探测存储缓冲区。 - Peter Cordes
@LewisKelsey:没有供应商的声明,很难进行测量。非常难将其他管道长度/重新导向效应与实际的L1i延迟分离开来。在实践中,分支失误恢复时间是您可以测量的内容,用于uop-cache命中与uop-cache未命中+ L1i命中。 - Peter Cordes
实际上,晚期BPU清除会在Westemere上引起3个周期的气泡,这似乎发生在ILD阶段。这意味着如果它可以在第5个周期的高边将新IP重新定向到第一个周期的低边,然后有一个3个周期的气泡(在第1个和第5个周期之间),这意味着在ILD之前有4个周期的空间,所以实际上可能是4个周期的常规命中。我找不到缓存查找管道阶段的任何图表,但也许由于更快的时钟速度,一些原始时钟现在被分成了2个。 - Lewis Kelsey

1
实际上,L1缓存命中的成本几乎与寄存器访问的成本相同。这对我来说很惊讶,但至少对于我的处理器(Athlon 64)是真的。不久前,我编写了一个简单的测试应用程序,以评估在多处理器系统中访问共享数据的效率。该应用程序主体是在预定义时间段内递增的简单内存变量。为了进行比较,我首先对非共享变量进行了基准测试。在那个活动期间,我捕获了结果,但在应用程序反汇编期间,我发现编译器欺骗了我的期望,并对我的代码应用了不必要的优化。它只是将变量放在CPU寄存器中,并在寄存器中迭代地递增它,而没有进行内存访问。但真正的惊喜是在我强制编译器使用内存变量而不是寄存器变量之后实现的。在更新的应用程序中,我实现了几乎相同的基准测试结果。性能下降确实微不足道(约1-2%),看起来与某些副作用有关。
因此:
1)我认为您可以将L1缓存视为未经管理的处理器寄存器池。

2) 强制编译器将频繁访问的数据存储在处理器寄存器中,以进行粗暴的汇编优化是没有任何意义的。如果它们确实经常被访问,它们将存在于L1缓存中,并且由于这个原因,将具有与处理器寄存器相同的访问成本。


此外,我可以推测,与基于寄存器的指令相比,涉及内存访问的指令具有更高的延迟,这是由于更复杂的解码和地址生成单元参与指令处理,而不是由于缓存访问。 - ZarathustrA
指令延迟是指依赖指令可以使用结果之前的时间。这并不意味着要等到退休,因为在乱序执行的 CPU 中 所有 指令都是推测性的。在长时间运行的循环中,CPU 无法隐藏循环传递的依赖链的延迟(即跨迭代连接)。例如:为什么在展开的 ADD 循环内重新初始化寄存器会使其运行更快,即使循环内有更多的指令? - Peter Cordes
1
looptop: / inc [mem] / dec ecx/jnz looptop 在大多数最新的x86上,由于存储转发的瓶颈,每6个周期运行一次。但是,如果使用inc edx(并且在整个循环中没有存储/重新加载瓶颈),它可以以1/clock的速度运行。也许您在循环内部使用了内联汇编,并且没有启用优化,因此编译器在您的汇编周围创建了一个循环,该循环在内存目标增量上形成了瓶颈。在这种情况下,是的,通过避免内存无法获得太多收益,因为瓶颈仍然存在。 - Peter Cordes
你测试的是哪个CPU?是Zen还是Ice Lake?这些CPU在特定情况下具有零延迟存储转发的新功能,至少当地址在存储数据之前就准备好时。 (https://www.agner.org/forum/viewtopic.php?t=41 / https://en.wikichip.org/wiki/amd/microarchitectures/zen#Stack_Engine) 这是一个特殊情况,大多数CPU(尚)没有这个功能;我在早期的评论中忘记了这种可能性。在新的混合CPU(如Alder Lake)中,低功耗“效率”核心也不会有这个功能。大多数x86服务器仍然是Intel Skylake或更早版本。 - Peter Cordes
让我们在聊天中继续这个讨论 - ZarathustrA
显示剩余9条评论

1

如果我没记错的话,大约需要1-2个时钟周期,但这只是一个估计值,新一代缓存可能会更快。这是我所拥有的一本《计算机体系结构》书籍中的信息,供AMD使用,因此Intel可能会略有不同,但我会将其限制在5到15个时钟周期之间,这似乎是一个很好的估计。

编辑:糟糕,L2需要10个时钟周期才能访问TAG,而L1需要1到2个周期,我的错误 : \


你确定你说的是“命中”而不是“未命中”吗? - user541686
是的,我相信 TAG 访问需要 2 个时钟周期,其余时间来自缓存访问和加载。 - Jesus Ramos
@Mehrdad,我之前提供了L2的信息,我的错误,已经更新了正确的信息。 - Jesus Ramos
我有点怀疑有些奇怪。 :) 谢谢。 - user541686
CPU 频率越高,相同的真实时间内所需的周期数就会增加。现代 CPU 的 L1 加载使用延迟大约为 4 个时钟周期(Intel Haswell)。例如,在包含 mov eax,[eax] 的循环中,如果指针指向自身,则每个迭代需要的周期数为几个?请参阅 http://www.7-cpu.com/cpu/Haswell.html 顶部的一些数字。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接