缓存怎么能这么快?
这个CPU有...
2 个内核 每个内核一个 32 KB 指令和 32 KB 数据一级缓存 (L1)
由于有两个内核,我们可以期望基准测试并行运行两个线程。不过,他们的网站提供的信息非常少,但如果我们看这里,具有更多内核的 CPU 似乎会相应地提供更高的 L1 吞吐量。所以我认为显示的是所有内核并行工作的总吞吐量。因此,对于您的 CPU,我们应该为一核和一缓存除以二:
Read 93 GB/s
Write 47 GB/s
Copy 90 GB/s
现在,“复制”比“写入”快 2 倍这一事实非常可疑。它怎么能复制得比它写得快?我敢打赌,基准显示为“复制”的是读+写吞吐量的总和,在这种情况下,它会以 45 GB/s 的速度读写,但显示为 90,因为它是一个基准,并且谁他妈的相信基准?所以让我们忽略“复制”。
Read 93 GB/s => 30 bytes/clock
Write 47 GB/s => 15 bytes/clock
现在,一个 128 位寄存器是 16 字节,足够接近,所以听起来这个缓存每个时钟可以进行两次 128 位读取和一次写入。
这正是您想要真正简化那些 SSE 数字运算指令:每个周期两次读取和一次写入。
这很可能通过大量并行数据线来实现,这是在芯片内快速传输大量数据的常用方法。
@peufeu 的回答指出这些是系统范围的聚合带宽。L1 和 L2 是英特尔 Sandybridge 系列中的私有每核缓存,因此数量是单核的 2 倍。但这仍然给我们留下了令人印象深刻的高带宽和低延迟。
L1D 缓存直接内置于 CPU 内核中,并且与加载执行单元(和存储缓冲区)紧密耦合。同样,L1I 高速缓存紧邻内核的指令获取/解码部分。(实际上我没有看过 Sandybridge 硅平面图,所以这可能不是真的。前端的问题/重命名部分可能更接近“L0”解码的 uop 缓存,这样可以节省电力并具有更好的带宽比解码器。)
但是使用 L1 缓存,即使我们可以在每个周期读取......
为什么停在那里?自 Sandybridge 以来的 Intel 和自 K8 以来的 AMD 可以每个周期执行 2 次加载。多端口缓存和 TLB 是一回事。
David Kanter 的Sandybridge 微架构文章有一个很好的图表(也适用于您的 IvyBridge CPU):
(“统一调度程序”持有 ALU 和内存微指令等待它们的输入准备好,和/或等待它们的执行端口。(例如,如果前一个尚未执行,则vmovdqa ymm0, [rdi]
解码为必须等待的加载微指令,例如示例)。 英特尔在发布/重命名时将 uops 调度到端口。此图仅显示内存 uops 的执行端口,但未执行的 ALU uops 也会竞争它。发布/重命名阶段将 uops 添加到 ROB 和调度程序. 它们一直在 ROB 中直到退休,但在调度器中只在调度到执行端口之前。(这是英特尔术语;其他人使用 issue 和 dispatch 不同))。 AMD 对整数 / FP 使用单独的调度程序,但寻址模式始终使用整数寄存器rdi
add rdi,32
如图所示,只有 2 个 AGU 端口(地址生成单元,采用类似寻址模式[rdi + rdx*4 + 1024]
并产生线性地址)。它每个时钟可以执行 2 个内存操作(每个 128b / 16 字节),其中一个是存储。
但它有一个窍门:SnB/IvB 作为单个 uop 运行 256b AVX 加载/存储,在加载/存储端口中需要 2 个周期,但在第一个周期中只需要 AGU。这让存储地址 uop 在第二个周期期间在端口 2/3 上的 AGU 上运行,而不会丢失任何负载吞吐量。因此,对于 AVX(Intel Pentium/Celeron CPU 不支持 :/),SnB/IvB 可以(理论上)每个周期支持 2 个负载和1 个存储。
您的 IvyBridge CPU 是 Sandybridge 的缩影(有一些微架构改进,如mov-elimination、 ERMSB (memcpy/memset) 和下一页硬件预取)。之后的一代 (Haswell) 通过将从执行单元到 L1 的数据路径从 128b 扩大到 256b,使每时钟 L1D 带宽翻倍,因此 AVX 256b 负载可以维持每时钟 2 个。它还为简单的寻址模式添加了一个额外的 store-AGU 端口。
Haswell/Skylake 的峰值吞吐量是每个时钟加载 + 存储 96 个字节,但英特尔的优化手册表明 Skylake 的持续平均吞吐量(仍然假设没有 L1D 或 TLB 未命中)为每周期约 81B。(根据我在 SKL 上的测试,一个标量整数循环可以维持每个时钟 2 个负载 + 1 个存储,每个时钟从 4 个融合域微指令执行 7 个(未融合域)微指令。但是使用 64 位操作数而不是32 位,因此显然存在一些微架构资源限制,这不仅仅是将存储地址微指令调度到端口 2/3 并从负载中窃取周期的问题。)
我们如何根据其参数计算缓存的吞吐量?
你不能,除非参数包括实际的吞吐量数字。如上所述,即使是 Skylake 的 L1D 也无法完全跟上 256b 向量的加载/存储执行单元。虽然它很接近,但它可以用于 32 位整数。(加载单元的数量多于缓存的读取端口数是没有意义的,反之亦然。您只会忽略永远无法充分利用的硬件。请注意,L1D 可能有额外的端口来发送/接收线路到/来自其他核心,以及来自核心内部的读/写。)
仅查看数据总线宽度和时钟并不能了解全部情况。 L2 和 L3(和内存)带宽可能受到 L1 或 L2 可以跟踪的未完成未命中数的限制。带宽不能超过latency * max_concurrency,并且L3延迟较高的芯片(如多核Xeon)的单核L3带宽比相同微架构的双/四核CPU少得多。请参阅此 SO 答案的“延迟绑定平台”部分。Sandybridge 系列 CPU 有 10 个行填充缓冲区来跟踪 L1D 未命中(NT 存储也使用)。
(在大型 Xeon 上,具有许多活动内核的总 L3/内存带宽是巨大的,但在相同时钟速度下,单线程代码的带宽比四核更差,因为更多的内核意味着环形总线上的更多停止,因此更高延迟 L3。)
缓存延迟
这样的速度是怎么做到的?
L1D 缓存的 4 个周期加载使用延迟令人印象深刻,但仅适用于指针追逐的特殊情况(当它最重要时)。在其他情况下,它是 5 个周期,考虑到它必须以类似 的寻址模式开始,这仍然令人印象深刻[rsi + rdi * 4 + 32]
,因此它必须在它甚至拥有虚拟地址之前进行地址生成。然后它必须将其转换为物理以检查缓存标签是否匹配。
(有关当reg 来自先前加载时的特殊情况的更多信息,请参阅当 base+offset 与 base 在不同的页面时是否会受到惩罚;似乎英特尔乐观地根据地址与加法并行地探测 TLB , 并且必须在加载端口重试 uop,如果它不起作用。非常适合在节点早期具有指针的列表/树节点。[base + 0-2047]
base
base
另请参阅Intel 的优化手册,Sandybridge 部分 2.3.5.2 L1 DCache。这也假设没有段覆盖,段基地址为0
,这是正常的;那些可能会使它比 5 个周期更糟)
加载端口还必须探测存储缓冲区以查看加载是否与任何早期存储重叠。即使早期(按程序顺序)存储地址 uop 尚未执行,它也必须弄清楚这一点,因此存储地址是未知的(在这种情况下,它是动态预测的;错误预测会导致内存顺序管道核弹)。但据推测,这可能与检查 L1D 命中同时发生。如果事实证明不需要 L1D 数据,因为存储转发可以提供来自存储缓冲区的数据,那么这不会有任何损失。
英特尔像几乎所有其他人一样使用 VIPT(虚拟索引物理标记)缓存,使用的标准技巧是使缓存足够小并具有足够高的关联性,使其表现得像 PIPT 缓存(无别名),具有 VIPT 的速度(可以索引与 TLB 虚拟->物理查找并行)。
英特尔的 L1 高速缓存为 32kiB,8 路关联。页面大小为 4kiB。这意味着“索引”位(选择哪一组 8 种方式可以缓存任何给定的行)都低于页面偏移量;即这些地址位是页面的偏移量,并且在虚拟地址和物理地址中始终相同。
有关这方面的更多详细信息以及为什么小型/快速缓存有用/可能的其他详细信息(并且在与较大的较慢缓存配对时运行良好),请参阅我关于L1D 为什么比 L2 更小/更快的答案。
小缓存可以做一些在大缓存中过于耗电的事情,比如在获取标签的同时从集合中获取数据数组。因此,一旦比较器发现哪个标签匹配,它只需复用已从 SRAM 获取的八个 64 字节缓存线之一。
(实际上并没有那么简单:Sandybridge / Ivybridge 使用分组的 L1D 缓存,具有 8 个 16 字节块的组。如果对不同缓存行中的同一组的两次访问尝试在同一循环中执行,则可能会发生缓存组冲突。 (有 8 个存储体,所以这可能发生在地址相隔 128 倍数的情况下,即 2 个高速缓存行。)
只要不跨越 64B 高速缓存行边界,IvyBridge 也不会因未对齐访问而受到惩罚。我猜它会根据低地址位确定要获取的存储库,并设置为获得正确的 1 到 16 字节数据所需的任何移位。
在缓存行拆分上,它仍然只是一个 uop,但会进行多个缓存访问。惩罚仍然很小,除了 4k 分割。Skylake 甚至使 4k 分割相当便宜,延迟大约 11 个周期,与具有复杂寻址模式的普通高速缓存行分割相同。但是 4k-split 的吞吐量明显比 cl-split 的 non-split 差。
资料来源:
- David Kanter 的 SnB 微架构文章(带有出色的图表,并且更多地关注 uarch 设计而不是如何对其进行优化)。
- Intel的优化手册
- Agner Fog 的 microarch pdf(以及指令表和优化指南)。
- SO 的 x86 标记 wiki提供指向这些以及更多内容的链接。
L1 缓存是相当宽的内存结构。英特尔处理器中的 L1 缓存架构可以在本手册中找到(由 next-hack 提供)。但是,对某些参数的解释是不正确的,“缓存线大小”不是“数据宽度”,而是原子数据访问的串行块大小。
表 2-17(第 2.3.5.1 节)表明,在加载(读取)时,缓存带宽为 2x16 = 32 Bytes per core per CYCLE。仅此一项就在 3GHz 内核上提供了 96 Gb/s 的理论带宽。目前尚不清楚引用的基准报告是什么,看起来它测量了两个并行工作的内核,因此两个内核的速度为 192 Gbps。