【问题标题】:Size of store buffers on Intel hardware? What exactly is a store buffer?英特尔硬件上的存储缓冲区大小?究竟什么是存储缓冲区?
【发布时间】:2019-07-19 10:44:29
【问题描述】:

Intel optimization manual 讨论了处理器的许多部分中存在的存储缓冲区的数量,但似乎没有讨论存储缓冲区的大小。这是公共信息还是存储缓冲区的大小作为微架构细节保留?

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

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

【问题讨论】:

  • @RobertHarvey 不太明白你为什么搁置这个问题。有什么不清楚的地方吗?什么特别宽泛?
  • @RobertHarvey 我提到他们都没有谈论存储缓冲区的大小。这是我想问的——是公共信息吗?如果不是,当人们想要估计这个时,他们会怎么做?你在这里推荐什么?
  • @RobertHarvey 我认为 OPs 的问题完全符合主题。他可能需要做的唯一改进是将其确定为特定的微架构和特定的存储缓​​冲区。并非所有问题都需要与软件开发问题有关,我认为您结束这个问题的速度太快了。
  • @RobertHarvey 我不明白是什么让你得出这个结论。我们能否重新提出问题,以激励人们提供相关详细信息的答案?
  • 这没有记录,但存储缓冲区条目至少包含存储数据、存储物理地址、存储线性地址、存储类型字段(基本上是存储操作码)、阻塞代码字段,和其他领域。例如,在具有 AVX2(但不是 AVX512)的微架构上,条目大小至少为 32 字节(用于数据)+ 39 位(物理地址)+ 48 位(线性地址)+ 其他较小的字段。我们并不确切知道。

标签: performance assembly x86 intel cpu-architecture


【解决方案1】:

相关:what is a store buffer?can a speculatively executed cpu branch contain opcodes that access RAM? 中对缓冲区概念的基本/适合初学者的介绍

How do the store buffer and Line Fill Buffer interact with each other? 也很好地描述了执行存储指令的步骤以及它最终如何提交到 L1d 缓存。


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

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

对于推测性/无序 CPU,它还可以在检测到旧指令中的异常或其他错误推测后回滚存储,而推测性存储永远不会全局可见。这显然对正确性至关重要! (您不能回滚其他内核,因此在确定其为非推测性之前,您不能让他们看到您的存储数据。)


当两个逻辑核心都处于活动状态(超线程)时,英特尔将存储缓冲区分成两部分;每个逻辑核心得到一半。从一个逻辑核心加载只窥探它自己的一半存储缓冲区2What will be used for data exchange between threads are executing on one Core with HT?

存储缓冲区按照程序顺序(以尊重 x86 的强排序内存模型3)尽快将 已退休 存储指令中的数据提交到 L1d。要求商店在他们退休时提交,这将不必要地拖延缓存未命中商店的退休。仍然在存储缓冲区中的退休存储肯定会发生并且无法回滚,因此它们实际上会损害中断延迟。 (中断在技术上不需要序列化,但是任何由 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 缓存。


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

Store-address 和 store-data 可以按任一顺序执行,以先就绪者为准:将 uops 从前端写入 ROB 和 RS 在后端的分配/重命名阶段也分配负载或存储缓冲区以在发布时加载或存储微指令。或者停顿直到有一个可用。由于分配和提交是按顺序进行的,这可能意味着更旧/更年轻的数据很容易跟踪,因为它可以只是一个循环缓冲区,不必担心旧的长期存在的条目在环绕后仍在使用中。 (除非缓存绕过/弱排序 NT 存储可以做到这一点?它们可以无序提交到 LFB(行填充缓冲区)。与普通存储不同,它们直接提交到 LFB 以进行核外传输,而不是 L1d .)


但是条目的大小是多少?

存储缓冲区大小以条目而不是位为单位。

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

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

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


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

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


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

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

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

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

查看以该评论开头的讨论:Are write-combining buffers used for normal writes to WB memory regions on Intel?

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

我们肯定知道,像 Alpha 21264 这样的弱排序 ISA 确实在其存储缓冲区中存储合并,因为 the manual documents it,以及它对每个周期可以提交和/或读取 L1d 的内容的限制。此外,PowerPC RS64-II 和 RS64-III,细节较少,在此处的评论链接的文档中:Are there any modern CPUs where a cached byte store is actually slower than a word store?

人们发表了关于如何在 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 条目,这似乎不太可能。


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

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

英特尔的手册(尤其是优化手册)由不同的作者编写多年,术语也不一致。 对优化手册的大部分内容持保留态度,尤其是如果它谈论奔腾4。关于 Sandybridge 和 Haswell 的新部分是可靠的,但旧部分可能有陈旧的建议,这些建议仅/主要与 P4 相关(例如 inc vs. add 1),或者某些优化规则的微架构解释可能令人困惑/错误。特别是第 3.6.10 节写组合。由于内存排序规则,在等待行到达缓存未命中存储到 WB 内存时使用 LFB 组合存储的第一个要点似乎不合理。请参阅上面链接的我和 BeeOnRope 之间的讨论,以及这里的 cmets。


脚注 1:

用于缓冲来自内部缓存的回写(或直写)的写入组合缓存将具有不同的名称。例如Bulldozer 系列使用 16k 直写式 L1d 缓存,以及一个小的 4k 回写缓冲区。 (有关详细信息和更多详细信息的链接,请参阅 Why do L1 and L2 Cache waste space saving the same data?。有关在 Bulldozer 系列 CPU 上减慢超过 4k 的重写阵列微基准测试,请参阅 Cache size estimation on your system?。)

脚注 2:一些 POWER CPU 让其他 SMT 线程窥探存储缓冲区中已停用的存储:这可能导致不同的线程对来自其他线程的全局存储顺序存在分歧。 Will two atomic writes to different locations in different threads always be seen in the same order by other threads?

脚注 3:具有弱内存模型的非 x86 CPU 可以按任何顺序提交已停用的存储,从而允许更积极地将多个存储合并到同一行,并使缓存未命中存储不会停止其他商店的提交。

【讨论】:

  • 评论不用于扩展讨论;这个对话是moved to chat
  • 该评论线程中有一些好东西,包括一些关于为什么当一个循环有超过 4 个输出流时我们会降低性能的理论。 (根据英特尔优化手册中令人困惑且可能部分错误的部分 3.6.10 Write Combining 部分,建议在那里使用循环裂变。)
  • 该网站确保我们的模组不会休眠:p
  • 关于“原子 RMW 操作必须等待存储缓冲区耗尽才能锁定缓存行并同时执行加载.....”的问题这意味着存在 [StoreLoad]在 Atomic RMW 操作之前? Atomic RMW 不仅仅是获取负载(+ 缓存行锁定)和顺序一致存储(+ 缓存行上的锁定释放)
  • @pveentjer:是的,但是核心不能让任何其他加载或存储提交到该加载或存储之间的任何其他缓存行,否则它不会是 原子的 RMW。 add [rdi], eax 不是原子的,lock add [rdi], eax 是。 Can num++ be atomic for 'int num'?。从任何可以观察到差异的 POV 来看,加载和存储都是对高速缓存行(原子 RMW)的同一次访问的一部分。创建这种错觉需要微码使用不属于正常内存目标指令的特殊微指令。
猜你喜欢
  • 2012-06-21
  • 2014-09-18
  • 1970-01-01
  • 2014-08-02
  • 1970-01-01
  • 2018-09-05
  • 2015-09-25
  • 1970-01-01
  • 2012-08-10
相关资源
最近更新 更多