【问题标题】:Why doesn't RFO after retirement break memory ordering?为什么退休后的 RFO 不中断内存排序?
【发布时间】:2020-10-04 04:49:45
【问题描述】:

我以为我了解 L1D write miss 是如何处理的,但仔细考虑却让我感到困惑。

这是一个汇编语言片段:

;rdi contains some valid 64-bytes aligned pointer
;rsi contains some data
mov [rdi], rsi
mov [rdi + 0x40], rsi        
mov [rdi + 0x20], rsi

假设 [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 注意到 [rdi] 的 RFO 正在进行中。数据被放入WC缓冲区。

  7. 轰! [rdi] RFO 恰好在 [rdi + 0x40] RFO 之前完成,因此 mov [rdi], rsimov [rdi + 0x20], rsi 的数据现在可以提交到缓存中。它会破坏内存排序。

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

【问题讨论】:

    标签: assembly x86-64 cpu-architecture cpu-cache rfo


    【解决方案1】:

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

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

    (鉴于@BeeOnRope 发现同一行的多个缓存未命中存储如何提交到一个 LFB,然后另一个 LFB 用于另一行,这可能是让多个 RFO 处于飞行状态的机制,而不仅仅是 SB 头. 我们必须检查 ABA 存储模式是否限制了内存级别的并行性。如果是这种情况,那么可能启动 RFO 与将数据从 SB 移动到 LFB 相同,释放那个 SB 条目。 但请注意,在那些未决的 RFO 完成并提交来自 LFB 的存储之前,SB 的新负责人仍然无法提交。)


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

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

    正如@HadiBrais 在回复Where is the Write-Combining Buffer located? x86 时所写的那样

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

    L1 DCache 可以从分配中维护多达 64 个加载微操作 直到退休。它可以维持多达 36 家商店的运营,从 分配,直到存储值提交到缓存,或写入 在非临时存储的情况下到行填充缓冲区 (LFB)。

    这对于考虑性能调整来说非常好,但可能不是MDS vulnerabilities,它可以推测性地使用从 LFB 或其他什么读取错误负载的陈旧数据。

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


    但就这么简单吗?没有

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

    @BeeOnRope 做了更多实验,发现一些证据表明,像 AAABBCCCC 这样的一系列商店可以引流到 A、B、C 线的三个 LFB。RWT thread 有一个实验证明该理论预测的 4 倍性能差异。

    这意味着 CPU 可以跟踪 LFB 之间的顺序,尽管当然仍然不能单个 LFB 中。像 AAABBCCCCA(或 ABA)这样的序列将无法提交超过最终的 A 存储,因为“当前头部”LFB 用于线路 C,并且已经有一个 LFB 等待线路 A 到达。第 4 行 (D) 可以,打开一个新的 LFB,但添加到已经打开的 LFB 以等待不是头部的 RFO 是不行的。见@Bee's summary in comments

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


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

    (此部分未根据@BeeOnRope 的新发现进行更新)。

    也没有确凿的证据表明商店中存在任何类型的商店合并/合并 现代 Intel 或 AMD CPU 上的缓冲区,或使用 WC 缓冲区(Intel 上的 LFB)在等待高速缓存行到达时保存存储数据。请参阅Are two store buffer entries needed for split line/page stores on recent Intel? 下的 cmets 中的讨论。我们不能排除在存储缓冲区提交端附近的一些次要形式。

    我们知道some weakly-ordered RISCs microarchitectures definitely do merge stores before they commit,尤其是创建一个完整的 4 字节或 8 字节写入缓存 ECC 颗粒以避免 RMW 循环。但英特尔 CPU 不会对缓存行内的狭窄或未对齐存储有任何惩罚。

    有一段时间,@BeeOnRope 和我认为有一些商店合并的证据,但我们改变了主意。 Size of store buffers on Intel hardware? What exactly is a store buffer? 有更多细节(以及旧讨论的链接)。

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


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

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

    脚注 2: 存储缓冲区条目按程序顺序分配(在缓冲区的尾部),因为指令/微指令被发送到无序后端并为它们分配后端资源。 (例如,用于写入寄存器的 uop 的物理寄存器,用于可能错误预测的条件分支 uop 的分支顺序缓冲区条目。)另见 Size of store buffers on Intel hardware? What exactly is a store buffer?。有序分配和提交保证了商店的程序订单可见性。存储缓冲区将全局可见的提交与存储地址和存储数据 uop(写入存储缓冲区条目)的无序推测执行隔离开,并且通常将执行与等待缓存未命中存储解耦,直到存储缓冲区已满。

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

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

    【讨论】:

    • @SomeName:是的,完全正确。由 MOB 来检测内存顺序错误推测并触发管道核弹。但请注意,您的问题的答案不涉及相对于负载订购商店;等到退休后提交存储以确保正确性给我们 LoadStore 订购免费(假设加载必须实际完成才能退休,而不仅仅是检查无故障)。因此,组合的加载+存储缓冲区 MOB 方面与这个特定问题无关,只是从 SB 本身按顺序提交存储排序。
    • 我又改变了主意。我相信错过的商店会在 RFO在某些条件下进行时进入 LFB。特别是,条件是不违反排序。如果一个 store 会流入一个已经为早期的非连续 store miss 分配的 LFB,那么就会违反 ordering,因此在这种情况下会有一个停顿。例如,如果 A、B、C 表示存储到不同的缓存行 A、B、C,则像 AAABBCCCC 这样的一系列存储可以排入三个 LFB,用于行 A、B、C。
    • CPU 只需要确保按 A、B、C 的顺序提交 LFB。但是,按顺序 AAABBCCCCA(或更简单的 ABA)最终存储无法进入打开 LFB,它将失去 store-store ordering 属性。 ABA 案例与 OP 的 [+ 0, + 0x40, + 0x20] 示例完全相同。所以它停止了:可能存储在存储缓冲区中等待。性能测试与这个理论是一致的,但不能证明它。
    • 我最近写了关于我的新视图on RWT,并使用与 OP 相同的 0、40、20 测试。 @SomeName 也许这个问题是由那篇帖子引起的?您可以在双峰性能测试的wip branch 中找到测试,它们分别称为write_aabbwrite_abab
    • “做一个实验来测试它做得很好” .... 实际上我觉得我没有直接测试它。有 ABAB vs AABB 测试,但我想这可能有其他解释。我正在计划一个更直接的测试,在不触发 ABA 的情况下检查它,例如,检查同一行的一长串未命中是否会耗尽,但我还没有写。
    猜你喜欢
    • 2011-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-05
    • 1970-01-01
    • 2015-11-11
    相关资源
    最近更新 更多