写缓冲区在不同的处理器中可以有不同的用途或不同的用途。此答案可能不适用于未特别提及的处理器。我想强调的是,“写缓冲区”一词在不同的上下文中可能意味着不同的东西。此答案仅与 Intel 和 AMD 处理器有关。
英特尔处理器上的写入组合缓冲区
每个缓存可能伴随着零个或多个行填充缓冲区(也称为填充缓冲区)。 L2 的填充缓冲区集合称为超级队列或超级队列(超级队列中的每个条目都是一个填充缓冲区)。如果高速缓存在逻辑核心或物理核心之间共享,那么相关的填充缓冲区也会在核心之间共享。每个填充缓冲区可以保存一个高速缓存行和描述高速缓存行(如果它被占用)的附加信息,包括高速缓存行的地址、内存类型和一组有效位,其中位数取决于跟踪高速缓存行的各个字节。在早期的处理器(例如Pentium II)中,只有一个填充缓冲区能够进行写组合(和写折叠)。随着处理器的更新,行缓冲区的总数和能够进行 write-combing 的缓冲区的总数稳步增加。
Nehalem 到 Broadwell 的每个 L1 数据缓存都包含 10 个填充缓冲区。 Core 和 Core2 每个物理内核有 8 个 LFB。根据this,Skylake 上有 12 个 LFB。 @BeeOnRope 观察到 Cannon Lake 上有 20 个 LFB。我在手册中找不到明确的声明,说明 LFB 在所有这些微架构上都与 WCB 相同。然而,英特尔的人写的这个article 说:
请参阅英特尔® 64 和 IA-32 架构优化参考
特定处理器中填充缓冲区数量的手册;
通常数字是 8 到 10。请注意,有时这些也是
称为“写入组合缓冲区”,因为在一些较旧的
仅支持处理器流式存储。
我认为 LFB 一词最初是由英特尔在英特尔酷睿微架构中引入的,在该微架构上,所有 8 个 LFB 也是 WCB。基本上,英特尔当时偷偷地将 WCB 重命名为 LFB,但此后并未在其手册中对此进行说明。
同样的引用还说,WCB 一词用于较旧的处理器,因为它们不支持流式加载。这可以解释为 LFB 也被流式加载请求 (MOVNTDQA) 使用。但是,第 12.10.3 节说流式加载将目标行提取到称为流式加载缓冲区的缓冲区中,这在物理上显然与 LFB/WCB 不同。
在以下情况下使用行填充缓冲区:
(1) 在缓存中的加载未命中(请求或预取)时分配填充缓冲区。如果没有可用的填充缓冲区,加载请求会不断堆积在加载缓冲区中,这最终可能导致问题阶段停止。在加载请求的情况下,分配的填充缓冲区用于临时保存来自内存层次结构较低级别的请求行,直到它们可以写入缓存数据阵列。但是,即使该行尚未写入高速缓存数据阵列,仍可将所请求的高速缓存行部分提供给目标寄存器。根据Patrick Fay (Intel):
如果您在 PDF 中搜索“填充缓冲区”,您会看到该行
填充缓冲区 (LFB) 在 L1D 未命中后分配。 LFB持有
数据,以满足 L1D 未命中但在所有数据之前
准备写入 L1D 缓存。
(2) 一个填充缓冲区被分配到 L1 缓存的可缓存存储上,并且目标行不处于允许修改的一致性状态。我的理解是,对于可缓存的存储,只有 RFO 请求保存在 LFB 中,但要存储的数据在存储缓冲区中等待,直到目标行被提取到为其分配的 LFB 条目中。 Intel 优化手册第 2.4.5.2 节中的以下语句支持这一点:
L1 DCache 可以从分配中维护多达 64 个加载微操作
直到退休。它可以维持多达 36 家商店的运营,从
分配,直到存储值提交到缓存,或写入
在非临时存储的情况下到行填充缓冲区 (LFB)。
这表明如果目标行不在 L1D 中,则不会将可缓存存储提交到 LFB。换句话说,存储必须在存储缓冲区中等待,直到目标行写入LFB,然后在LFB中修改该行,或者将目标行写入L1D,然后修改该行在 L1D 中。
(3) 填充缓冲区分配在 L1 高速缓存中不可高速缓存的写入组合存储上,无论该行是否处于高速缓存或其一致性状态。可以在单个 LFB 中组合和折叠到同一缓存行的 WC 存储(多次写入同一行中的同一位置将使程序顺序中的最后一个存储在它们成为全局可观察之前覆盖以前的存储)。当前在 LFB 中分配的请求之间不维护排序。因此,如果有两个 WCB 正在使用中,则不能保证哪个将首先被驱逐,而不管存储的顺序与程序顺序无关。这就是为什么 WC 存储可能会变得全局可观察到无序,即使所有存储都按顺序退役(尽管 WC 协议允许 WC 存储无序提交)。此外,WCB 不会被窥探,因此只有在它们到达内存控制器时才成为全局可观察的。更多信息可以在英特尔手册 V3 的第 11.3.1 节中找到。
有some AMD processors 使用与非临时存储的填充缓冲区分开的缓冲区。 P6(第一个实现 WCB)和 P4 中也有许多 WCB 缓冲区,专用于 WC 内存类型(不能用于其他内存类型)。在 P4 的早期版本中,有 4 个这样的缓冲区。对于支持超线程的 P4 版本,当启用超线程并且两个逻辑核心都在运行时,WCB 会在两个逻辑核心之间进行静态分区。然而,现代英特尔微架构竞争性地共享所有 LFB,但我认为至少为每个逻辑内核保留一个可用以防止饥饿。
(4) L1D_PEND_MISS.FB_FULL 的文档表明 UC 存储分配在相同的 LFB 中(无论该行是处于缓存还是其一致性状态)。与可缓存存储类似,但与 WC 不同的是,UC 存储不会在 LFB 中合并。
(5) 我通过实验观察到来自IN 和OUT 指令的请求也在LFB 中分配。如需更多信息,请参阅:How do Intel CPUs that use the ring bus topology decode and handle port I/O operations。
其他信息:
填充缓冲区由缓存控制器管理,该控制器连接到其他级别的其他缓存控制器(或在 LLC 的情况下连接到内存控制器)。当请求命中缓存时,不会分配填充缓冲区。因此,在缓存中命中的存储请求直接在缓存中执行,而在缓存中命中的加载请求则直接从缓存中提供服务。当从缓存中逐出行时,不会分配填充缓冲区。逐出的行被写入它们自己的缓冲区(称为写回缓冲区或逐出缓冲区)。这是来自 Intel 的 patent,它讨论了 I/O 写入的写入组合。
我进行了一项与我在here 中描述的非常相似的实验,以确定是否分配了单个 LFB,即使同一行有多个负载也是如此。事实证明,情况确实如此。第一次加载到回写 L1D 缓存中未命中的行会获得为其分配的 LFB。所有后来加载到同一缓存行的内容都被阻止,并且在其相应的加载缓冲区条目中写入一个块代码,以指示它们正在等待该 LFB 中保存的同一请求。当数据到达时,L1D 缓存向加载缓冲区发送一个唤醒信号,并且在该行上等待的所有条目都被唤醒(解除阻塞)并计划在至少一个加载端口可用时将其发送到 L1D 缓存.显然,内存调度程序必须在未阻塞的负载和刚刚从 RS 分派的负载之间进行选择。如果在所有等待的负载有机会得到服务之前,由于任何原因该线路被驱逐,那么它们将再次被阻塞,并且将再次为该线路分配 LFB。 store case我没有测试过,但是我觉得不管是什么操作,都是为一行分配一个LFB。 LFB 中的请求类型可以从预取提升到需求加载,再到推测 RFO 到需要时的需求 RFO。我还凭经验发现,在刷新管道时,不会删除从错误预测路径上的 uops 发出的推测性请求。它们可能会被降级为预取请求。我不知道。
AMD 处理器上的写入组合缓冲区
我之前根据article 提到,有一些 AMD 处理器使用与非临时存储的填充缓冲区分开的缓冲区。我从文章中引用:
在较旧的 AMD 处理器(K8 和 Family 10h)上,非临时存储
使用一组四个独立的“写组合寄存器”
用于 L1 数据缓存未命中的八个缓冲区中的一个。
“在较旧的 AMD 处理器上”部分让我很好奇。这在较新的 AMD 处理器上是否发生了变化?在我看来,所有较新的 AMD 处理器(包括最新的 Family 17h 处理器 (Zen))仍然如此。 Zen 微架构上的 WikiChip article 包括两个提到 WC 缓冲区的数字:this 和 this。在第一个图中,不清楚 WCB 是如何使用的。然而,在第二个中,很明显显示的 WCB 确实专门用于 NT 写入(WCB 和 L1 数据缓存之间没有连接)。第二个数字的来源似乎是这些slides1。我认为第一个数字是由 WikiChip 制作的(这解释了为什么 WCB 被置于不确定的位置)。事实上,WikiChip 文章并未提及 WCB。但是,我们可以通过查看Software Optimization Guide for AMD Family 17h Processors 手册中的图 7 和用于 Family 17h 处理器的加载和存储队列的patent 来确认所示的 WCB 仅用于 NT 写入。 AMD 优化手册指出,现代 AMD 处理器中每个内核有 4 个 WCB。我认为这适用于 K8 和所有后续处理器。不幸的是,没有提及扮演 Intel 填充缓冲区角色的 AMD 缓冲区。
1迈克尔·克拉克,A New, High Performance x86 Core Design from AMD,2016 年。