英特尔硬件上存储缓冲区的大小?到底什么是存储缓冲区?

Size of store buffers on Intel hardware? What exactly is a store buffer?

Intel optimization manual讲的是store buffer的数量,存在于处理器的很多部分,但是好像没有讲store buffer的大小。这是 public 信息还是作为微架构细节保存的存储缓冲区的大小?

我正在研究的处理器主要是 Broadwell 和 Skylake,但有关其他处理器的信息也很好。

此外,存储缓冲区到底有什么作用?

相关:what is a store buffer? and a basic / beginner-friendly intro to the concept of buffers in

也很好地描述了执行存储指令的步骤以及它最终如何提交到 L1d 缓存。


整个存储缓冲区由多个条目组成

每个内核都有自己的存储缓冲区1 以将执行和退役与提交到 L1d 缓存分离。即使是有序 CPU 也能从存储缓冲区中受益,以避免在缓存未命中存储时停滞,因为与加载不同,它们只需要 最终 可见。 (没有实际的 CPU 使用顺序一致性内存模型,因此至少允许 StoreLoad 重新排序,即使在 x86 和 SPARC-TSO 中也是如此。

对于推测性/乱序 CPUs,它还可以在检测到旧指令中的异常或其他错误推测后回滚存储,而推测性存储永远不会全局可见.这显然是正确性所必需的! (你不能回滚其他核心,所以你不能让他们看到你的商店数据,直到它被认为是非推测的。)


当两个逻辑核心都处于活动状态(超线程)时,英特尔将存储缓冲区一分为二;每个逻辑核心得到一半。来自一个逻辑核心的加载仅侦听其自己的一半存储缓冲区2

存储缓冲区将来自retired存储指令的数据尽可能快地按照程序顺序提交到L1d(尊重x86的强顺序内存模型3 )。要求存储提交 as 他们退出将不必要地延迟缓存未命中存储的退出。仍在存储缓冲区中的退役存储肯定会发生并且无法回滚,因此它们实际上会损害中断延迟。 (中断在技术上不需要序列化,但是 IRQ 处理程序完成的任何存储在现有的待处理存储被耗尽之前都不会变得可见。并且 iret 正在序列化,所以即使在最好的情况下,存储缓冲区返回前排水。)

这是一个常见的(?)误解,它必须被显式刷新以使数据对其他线程可见。内存障碍不会导致存储缓冲区被刷新,完全障碍使当前核心等待直到存储缓冲区耗尽本身 ,然后再允许任何后续加载发生(即读取 L1d)。原子 RMW 操作必须等待存储缓冲区耗尽,然后才能锁定缓存行并对该行进行加载和存储,而不允许它离开 MESI 修改状态,从而阻止系统中的任何其他代理在期间观察它原子操作。

为了实现 x86 的强序内存模型,同时仍然在微架构上允许提前/无序加载(并在架构上允许发生加载时检查数据是否仍然有效),加载缓冲区 + 存储缓冲区条目共同形成 内存顺序缓冲区 (MOB)。 (如果允许加载时缓存行 仍然存在,那是内存顺序错误推测。)这个结构大概是 mfencelocked 指令可以设置一个屏障来阻止 StoreLoad 重新排序而不阻止乱序执行。 (尽管 mfence on Skylake does block OoO exec of independent ALU instructions,作为实现细节。)

movnt 绕过缓存的存储(如 movntps)也通过存储缓冲区,因此它们可以像 OoO exec CPU 中的其他所有内容一样被视为推测性的。但是他们直接提交给 LFB(行填充缓冲区),也就是写组合缓冲区,而不是 L1d 缓存。


Intel 上的存储指令 CPU 解码为存储地址和存储数据微指令(微融合为一个融合域微指令)。 store-address uop 只是将地址(可能还有存储宽度)写入存储缓冲区,因此以后的加载可以设置存储->加载转发或检测它们是否不重叠。存储数据微指令写入数据。

Store-address 和 store-data 可以按任何顺序执行,以先准备好的为准: allocate/rename 将前端的 uops 写入后端的 ROB 和 RS 的阶段 在发布时为加载或存储微指令分配加载或存储缓冲区。或停顿直到有空位为止。由于分配和提交是按顺序进行的,这可能意味着 older/younger 很容易跟踪,因为它可以只是一个循环缓冲区,不必担心包装后旧的长期条目仍在使用中大约。 (除非缓存绕过/弱排序 NT 存储可以做到这一点?他们可以乱序提交到 LFB(行填充缓冲区)。与普通存储不同,他们直接提交到 LFB 以进行核外传输,而不是提交到 L1d .)


but what is the size of an entry?

存储缓冲区大小以条目而不是位来衡量。

窄存储不会在存储缓冲区中“使用更少 space”,它们仍然只使用 1 个条目。

Skylake 的存储缓冲区有 56 个条目(wikichip), up from 42 in Haswell/Broadwell, and 36 in SnB/IvB (David Kanter's HSW writeup on RealWorldTech has diagrams)。您可以在 Kanter 在 RWT 上的文章、Wikichip 的图表或各种其他来源中找到大多数早期 x86 uarche 的编号。

SKL/BDW/HSW 也有 72 个加载缓冲区条目,SnB/IvB 有 64 个。这是尚未执行或正在等待数据从外部到达的正在运行的加载指令的数量缓存。


每个 条目的大小(以位为单位)是一个实现细节,对您优化软件的方式影响为零。同样,我们不知道 uop 的比特大小(在前端、ROB、RS 中),或 TLB 实现细节,或许多其他事情,但我们知道有多少 ROB 和 RS条目有多少,不同uarches中有多少不同类型的TLB条目。

英特尔不会发布其 CPU 设计的电路图,并且(据我所知)这些尺寸通常不为人所知,因此我们甚至无法满足对设计细节/权衡的好奇心。


在存储缓冲区中写入合并:

到同一缓存行的背靠背窄存储可以(可能?)在提交之前合并到存储缓冲区中,因此可能只需要一个周期在 L1d 缓存的写入端口上提交多家商店。

我们确信某些非 x86 CPU 会这样做,并且我们有一些证据/理由怀疑英特尔 CPU 可能会这样做。但如果它发生了,它是有限的。 @BeeOnRope 和我目前认为英特尔 CPU 可能 不会 进行任何重大合并。如果他们这样做,最合理的情况是存储缓冲区末尾的条目(准备提交到 L1d)都进入同一缓存行可能会合并到一个缓冲区中,如果我们正在等待 RFO,则优化提交对于那个缓存行。请参阅 Are two store buffer entries needed for split line/page stores on recent Intel? 评论中的讨论。我提出了一些可能的实验,但还没有完成。

关于可能的存储缓冲区合并的早期内容:

查看以此评论开头的讨论:

而且 Unexpectedly poor and weirdly bimodal performance for store loop on Intel Skylake 也可能相关。

我们肯定知道像 Alpha 21264 这样的一些弱排序的 ISA 确实在它们的存储缓冲区中存储合并,因为 the manual documents it, along with its limitations on what it can commit and/or read to/from L1d per cycle. Also PowerPC RS64-II and RS64-III, with less detail, in docs linked from a comment here:

人们发表了关于如何在 TSO 内存模型(如 x86)中进行(更积极?)存储合并的论文,例如Non-Speculative Store Coalescing in Total Store Order

合并可以允许存储缓冲区条目在其数据提交到 L1d 之前被释放(大概只有在退休之后),如果它的数据被复制到存储到同一行。这只有在没有对其他行的存储将它们分开时才会发生,否则它会导致存储在程序顺序之外提交(变得全局可见),从而违反内存模型。但我们认为这可能发生在同一行的任何 2 个存储,甚至是第一个和最后一个字节。

这个想法的一个问题是SB条目分配可能是一个环形缓冲区,就像ROB一样。乱序释放条目意味着硬件需要扫描每个条目以找到空闲的条目,然后如果它们被乱序重新分配,那么它们就不会按程序顺序用于以后的存储。这可能会使分配和存储转发变得更加困难,因此这可能不太合理。

正如在 Are two store buffer entries needed for split line/page stores on recent Intel?,即使 SB 条目跨越高速缓存行边界,它也可以保存一个存储的所有内容。在 离开 SB 时提交到 L1d 缓存时,缓存行边界变得相关。我们知道存储转发可以用于跨缓存行拆分的存储。如果它们在商店端口中被分成多个 SB 条目,这似乎不太可能。


术语:我一直在使用“合并”来谈论在存储缓冲区中合并,而使用“写入合并”来谈论在 LFB 中合并的 NT 存储在(希望)进行没有 RFO 的全行写入之前。或者存储到执行相同操作的 WC 内存区域。

这个区别/约定只是我编造的。根据评论中的讨论,这可能不是标准的计算机体系结构术语。

Intel的手册(尤其是优化手册)由不同的作者写了很多年,而且他们的术语也不一致。将优化手册的大部分内容与一粒盐,尤其是当它谈论 Pentium4 时。关于 Sandybridge 和 Haswell 的新部分是可靠的,但旧部分可能有陈旧的建议,这些建议仅/主要与 P4 相关(例如 inc 与 add 1),或者某些优化规则的微体系结构解释可能令人困惑/错误。特别是第 3.6.10 节写组合。由于内存排序规则,第一个要点是关于在等待行到达以将缓存未命中存储到 WB 内存时使用 LFB 组合存储似乎不太合理。请参阅上面链接的我和 BeeOnRope 之间的讨论,以及此处的评论。


脚注 1:

用于缓冲来自内部缓存的回写(或直写)的写入组合缓存将具有不同的名称。例如Bulldozer 系列使用 16k 直写 L1d 缓存,以及一个小的 4k 回写缓冲区。 (请参阅 Why do L1 and L2 Cache waste space saving the same data? for details and links to even more details. See Cache size estimation on your system? 重写数组微基准测试,该基准测试在 Bulldozer 系列 CPU 上速度超过 4k。)

脚注 2:某些 POWER CPUs 让其他 SMT 线程窥探存储缓冲区中的退休存储:这可能导致不同线程对全局顺序不一致来自其他线程的存储。

脚注 3:具有弱内存模型的非 x86 CPUs 可以按任何顺序提交退役存储,允许更积极地将多个存储合并到同一行,并使缓存未命中存储不会拖延其他存储的提交。