为什么退休后 RFO 不会打破记忆排序?

Som*_*ame 5 assembly x86-64 cpu-architecture cpu-cache rfo

我以为我了解L1D写未命中是如何处理的,但仔细想想却让我感到困惑。

这是一个汇编语言片段:

;rdi contains some valid 64-bytes aligned pointer
;rsi contains some data
mov [rdi], rsi
mov [rdi + 0x40], rsi        
mov [rdi + 0x20], rsi
Run Code Online (Sandbox Code Playgroud)

假设[rdi][rdi + 0x40]行在 l1d 中不处于 Exclusive 或 Modified 状态。然后我可以想象以下动作序列:

  1. mov [rdi], rsi 退休。
  2. mov [rdi], rsi尝试将数据写入 l1d。RFO 启动,数据放入 WC 缓冲区。
  3. mov [rdi + 0x40], rsi退休mov [rdi], rsi已经退休,所以有可能)
  4. mov [rdi + 0x40], rsi 为连续的缓存行启动 RFO,数据被放入 WC 缓冲区。
  5. mov [rdi + 0x20], rsi退休mov [rdi + 0x40], rsi已经退休,所以有可能)
  6. mov [rdi + 0x20], rsi注意到有 RFO for [rdi]in progress。数据被放入 WC 缓冲区。

  7. 繁荣![rdi]RFO是发生之前完成[rdi + 0x40]RFO这样的数据mov [rdi], rsimov [rdi + 0x20], rsi现在可以将提交给高速缓存。它打破了内存排序。

如何处理这种情况以保持正确的内存顺序?

Pet*_*des 7

启动 RFO 可以与将存储数据放入 LFB 分开;例如,对于尚未位于存储缓冲区头部的条目,尽早启动 RFO 可以允许存储的内存级并行性。 您已经证明,要实现这一点,存储数据不能总是移动到 LFB(行填充缓冲区,也用于 NT/WC 存储)中。

如果 RFO 只能通过将存储数据从存储缓冲区 (SB) 移动到 LFB 来发生,那么是的,您只能对 SB 的头部进行 RFO,而不能同时用于任何分级条目。(“毕业”商店是指其 uops 已从 ROB 退休的商店,即变得非投机性的商店)。但是如果您没有这个要求,您甚至可以更早地进行RFO,甚至是推测性的,但您可能不想这样做。1

(鉴于@BeeOnRope 关于同一行的多个缓存未命中存储如何提交到 LFB,然后将另一个 LFB 提交到另一行的发现,这可能是多个 RFO 运行的机制,而不仅仅是 SB 头。我们d具有检查的ABA店模式有限的内存级并行。如果是这样的话,那么也许在开始RFO一样从SB将数据移动到一个LFB,释放该SB条目。但请注意,新的头在那些未决的 RFO 完成并从 LFB 提交存储之前,SB 仍然无法提交。)


一个非常接近现实的简单心智模型

在存储未命中时,存储缓冲区条目会保存存储数据,直到 RFO完成,并直接提交到 L1d(将行从 Exclusive 翻转为 Modified 状态)。通过从存储缓冲区2的头部按顺序提交来确保强排序。

正如@HadiBrais 在回答Write-Combining Buffer 的位置时所写的那样?x86

我的理解是,对于可缓存的存储,只有 RFO 请求保存在 LFB 中,但要存储的数据在存储缓冲区中等待, 直到目标行被提取到为其分配的 LFB 条目中。英特尔优化手册第 2.4.5.2 节中的以下声明支持这一点:

从分配到停用,L1 DCache 最多可以维护 64 个加载微操作。从分配到存储值提交到缓存,或在非临时存储的情况下写入行填充缓冲区 (LFB),它最多可以维护 36 个存储操作。

这对于考虑性能调优来说非常好,但可能不是MDS 漏洞,它可以推测性地使用从 LFB 读取的错误负载或其他任何内容的陈旧数据。

任何存储合并或其他技巧都必须遵守内存模型。


但有那么简单吗?不

我们知道 CPU 不能违反它们的内存模型,并且推测 + 回滚不是提交到全局可见状态(如 L1d)或一般分级存储的选项,因为 uops 已经从 ROB 中消失了。就本地 OoO 执行官而言,它们已经发生了,只是它们何时会被其他内核看到的问题。我们也知道 LFB 本身不是全局可见的。(有一些迹象表明 LFB 被来自这个内核的负载所监听,比如存储缓冲区,但就 MESI 而言,它们更像是存储缓冲区的扩展。)

@BeeOnRope 做了更多的实验,找到了一些证据表明像 AAABBCCCC 这样的一系列商店可以流入三个 LFB,对于 A、B、C线RWT 线程通过一个实验证明了这个理论预测的 4 倍性能差异。

这意味着CPU可以跟踪LFBs之间的秩序,但仍然没有场单LFB。像 AAABBCCCCA(或 ABA)这样的序列将无法提交通过最终A存储,因为“当前头”LFB 用于线路 C,并且已经有一个 LFB 等待线路 A 到达。第 4 行 (D) 可以,打开一个新的 LFB,但添加到一个已经打开的 LFB 等待不是头部的 RFO 是不行的。在评论中查看@Bee 的摘要

所有这些仅针对英特尔 CPU AFAIK 进行了测试。


在此之前,我们认为 Intel/AMD 上没有存储合并,但长期以来一直对 Intel 手册中有关 LFB 作为存储到正常(强排序)WB 内存的 WC 缓冲区的提示感到困惑

(本节未根据@BeeOnRope 的新发现进行更新)。

也没有确凿的证据表明现代 Intel 或 AMD CPU 上的存储缓冲区中存在任何类型的存储合并/合并,或者使用 WC 缓冲区(Intel 上的 LFB)在等待缓存行到达时保存存储数据。请参阅在最近的 Intel 上拆分行/页存储是否需要两个存储缓冲区条目下的评论中的讨论. 我们不能排除它在存储缓冲区提交结束附近的一些次要形式。

我们知道,一些弱序 RISC 微架构在提交之前肯定会合并存储,尤其是创建一个完整的 4 字节或 8 字节写入缓存 ECC 颗粒以避免 RMW 循环。但是英特尔 CPU 对缓存线中的狭窄或未对齐的存储没有任何惩罚。

有一段时间@BeeOnRope 和我认为有一些商店合并的证据,但我们改变了主意。 英特尔硬件上的存储缓冲区大小?究竟什么是存储缓冲区?有更多详细信息(以及指向旧讨论的链接)。

(更新:现在终于有了存储合并的证据,并解释了一种有意义的机制。)


脚注 1: RFO 会占用共享带宽并从其他内核中窃取线路,从而减慢它们的速度。如果 RFO 过早,您可能会在真正投入之前再次失去这条线。加载也需要 LFB,您不想饿死它(因为在等待加载结果时执行会停止)。负载与存储根本不同,并且通常具有优先级。

因此,至少等待商店毕业是一个很好的计划,并且可能只为头部之前的最后几个商店缓冲区条目启动 RFO。(您需要在启动 RFO 之前检查 L1d 是否已经拥有该行,并且至少需要一个缓存读取端口用于标记,尽管不是数据。我可能猜想存储缓冲区一次检查 1 个条目并标记一个条目可能不需要 RFO。)另请注意,1 个 SB 条目可能是未对齐的缓存拆分存储并接触 2 个缓存行,最多需要 2 个 RFO...

脚注 2: 存储缓冲区条目按程序顺序分配(在缓冲区的尾部),因为指令 / uop 被发布到乱序后端并为其分配了后端资源。(例如,用于写入寄存器的 uops 的物理寄存器,用于可能错误预测的条件分支 uops 的分支顺序缓冲区条目。)另请参阅英特尔硬件上存储缓冲区的大小?究竟什么是存储缓冲区?. 有序分配和提交保证存储的程序顺序可见性。存储缓冲区将全局可见提交与存储地址和存储数据 uops(写入存储缓冲区条目)的无序推测执行隔离,并将执行与等待缓存未命中存储分离,直到存储缓冲区填满。

PS Intel 将存储缓冲区 + 加载缓冲区统称为内存顺序缓冲区 (MOB),因为它们需要相互了解以跟踪推测性早期加载。这与您的问题无关,仅适用于推测性早期加载和检测内存顺序错误推测和破坏管道的情况。

对于退役的存储指令(更具体地说,它们的“分级”存储缓冲区条目),只有存储缓冲区必须按程序顺序提交给 L1d。

  • 我对此又改变了主意。我相信在 RFO 进行期间(在某些条件下),错过的商店会进入 LFB。特别是,条件是不违反顺序。如果存储将排入已为较早的非连续存储未命中分配的 LFB,则会违反排序,因此在这种情况下会出现停顿。例如,如果 A、B、C 表示对不同高速缓存线 A、B、C 的存储,则像 AAABBCCCC 这样的一系列存储可以排入线 A、B、C 的三个 LFB。 (2认同)
  • CPU 只需确保按 A、B、C 的顺序提交 LFB。但是,按照 AAABBCCCCA(或更简单地说 ABA)的顺序,最终存储无法进入打开的 LFB,它将丢失商店-商店订购属性。ABA 的情况与 OP 的“[+ 0, + 0x40, + 0x20]”示例完全相同。所以它停止了:可能存储在存储缓冲区中等待。性能测试与该理论一致,但不证明这一点。 (2认同)
  • 我最近写了关于 RWT 的新观点(https://www.realworldtech.com/forum/?threadid=173441&curpostid=192262),并使用与 OP 相同的 0、40、20 测试。@SomeName 也许这个问题是由那篇文章引发的?您可以在双峰性能测试的[wip分支](https://github.com/travisdowns/bimodal-performance/commits/wip)中找到该测试,它们称为“write_aabb”和“write_abab”。 (2认同)
  • “干得好,做了一个实验来测试它”......实际上我觉得我还没有直接测试它。有 ABAB 与 AABB 测试,但我想可能还有其他解释。我正在计划一个更直接的测试,在不触发 ABA 的情况下检查它,例如,检查同一行的一长串未命中是否看起来耗尽,但我还没有编写它。 (2认同)

归档时间:

查看次数:

211 次

最近记录:

5 年,4 月 前