我记得在我的架构类中假设 L1 缓存命中是 1 个周期(即与寄存器访问时间相同),但在现代 x86 处理器上真的是这样吗?
L1 缓存命中需要多少个周期?它与注册访问相比如何?
我记得在我的架构类中假设 L1 缓存命中是 1 个周期(即与寄存器访问时间相同),但在现代 x86 处理器上真的是这样吗?
L1 缓存命中需要多少个周期?它与注册访问相比如何?
这是一篇关于这个主题的精彩文章:
http://arstechnica.com/gadgets/reviews/2002/07/caching.ars/1
要回答您的问题 - 是的,缓存命中的成本与寄存器访问的成本大致相同。当然,缓存未命中是非常昂贵的;)
PS:
具体情况会有所不同,但此链接有一些不错的大致数字:
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
PP:
这些数字代表了更老、更慢的 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 缓存未命中通常太长而无法完全隐藏。)
单周期缓存延迟曾经是在较低时钟速度下的简单有序管道上的问题(因此每个周期更多纳秒),特别是对于更简单的缓存(更小,没有关联性,并且缓存的 TLB 更小) t 纯粹是虚拟寻址。)例如,经典的 5 级 RISC 流水线(如 MIPS I)假设缓存命中时的内存访问为 1 个周期,在 WB 之前在 EX 中进行地址计算,在单个 MEM 流水线阶段中进行内存访问。
现代高性能 CPU 将流水线划分为更多阶段,从而使每个周期更短。add
这让像//这样的简单指令运行得非常快or
,and
仍然有 1 个周期的延迟,但时钟速度很高。
有关循环计数和乱序执行的更多详细信息,请参阅Agner Fog 的 microarch pdf以及x86 标签 wiki中的其他链接。
Intel Haswell 的 L1 负载使用延迟是 4 个周期的指针追踪,这是现代 x86 CPU 的典型特征。即在一个循环中运行的速度有多快mov eax, [eax]
,带有一个指向自身的指针。(或者对于在缓存中命中的链表,很容易用闭环进行微基准测试)。另请参阅当基数+偏移量与基数位于不同页面时是否会受到惩罚?仅当指针直接来自另一个负载时,该 4 周期延迟特殊情况才适用,否则为 5 个周期。
对于英特尔 CPU 中的 SSE/AVX 向量,负载使用延迟要高 1 个周期。
存储重载延迟为 5 个周期,与缓存命中或未命中无关(它是存储转发,从存储缓冲区读取尚未提交到 L1d 缓存的存储数据)。
正如哈罗德评论的那样,寄存器访问是 0 个周期。因此,例如:
inc eax
有 1 个周期延迟(只是 ALU 操作)add dword [mem], 1
有 6 个周期延迟,直到加载dword [mem]
就绪。(ALU + 存储转发)。例如,在内存中保留一个循环计数器将一个循环限制为每 6 个周期进行一次迭代。mov rax, [rsi]
rsi
从准备就绪到rax
L1 命中准备就绪有 4 个周期延迟(L1 负载使用延迟。)http://www.7-cpu.com/cpu/Haswell.html有一个每个缓存的延迟表(我将在此处复制)和一些其他实验数字,包括 L2-TLB 命中延迟(在 L1DTLB 未命中时) )。
Intel i7-4770(Haswell),3.4 GHz(Turbo Boost 关闭),22 nm。内存:32 GB (PC3-12800 cl11 cr2)。
L1 数据缓存 = 32 KB,64 B/行,8 路。
L1 指令缓存 = 32 KB,64 B/行,8 路。
L2 缓存 = 256 KB,64 B/行,8 路
L3 缓存 = 8 MB,64 B/行
L1 数据缓存延迟 = 4 个周期,用于通过指针 (
mov rax, [rax]
)进行简单访问L1 数据缓存延迟 = 5 个周期,用于复杂地址计算的访问 (
mov rax, [rsi + rax*8]
)。L2 缓存延迟 = 12 个周期
L3 缓存延迟 = 36 个周期
RAM 延迟 = 36 个周期 + 57 ns
顶级基准页面是http://www.7-cpu.com/utils.html,但仍然没有真正解释不同测试大小的含义,但代码是可用的。测试结果包括Skylake,在本次测试中与 Haswell 几乎相同。
@paulsm4 的答案有一张多插槽 Nehalem Xeon 的表格,包括一些远程(其他插槽)内存/L3 编号。
如果我没记错的话,大约是 1-2 个时钟周期,但这是一个估计值,较新的缓存可能会更快。这是我的计算机体系结构书,这是 AMD 的信息,因此英特尔可能会略有不同,但我会将其限制在 5 到 15 个时钟周期之间,这对我来说似乎是一个不错的估计。
编辑:哎呀 L2 是 10 个周期,带有 TAG 访问权限,L1 需要 1 到两个周期,我的错误:\
实际上,L1 缓存命中的成本几乎与寄存器访问的成本相同。这让我很惊讶,但这是事实,至少对我的处理器(Athlon 64)来说是这样。前段时间,我编写了一个简单的测试应用程序来测试多处理器系统中访问共享数据的效率。应用程序主体是一个简单的内存变量,在预定义的时间段内递增。为了配合,我首先对非共享变量进行了基准测试。在那次活动中,我捕获了结果,但是在应用程序反汇编过程中,我发现编译器欺骗了我的期望,并对我的代码进行了不需要的优化。它只是将变量放在 CPU 寄存器中,并在寄存器中迭代地递增它,而无需访问内存。但是在我强制编译器使用内存变量而不是寄存器变量之后,真正的惊喜出现了。在更新的应用程序上,我获得了几乎相同的基准测试结果。性能下降实际上可以忽略不计(~1-2%),看起来与一些副作用有关。
结果:
1)我认为您可以将 L1 缓存视为非托管处理器寄存器池。
2)通过强制编译器将频繁访问的数据存储在处理器寄存器中来应用残酷的组装优化是没有任何意义的。如果它们真的被频繁访问,它们将存在于 L1 缓存中,因此将具有与处理器寄存器相同的访问成本。