首页 > 解决方案 > Intel-x86:WC、WB和UC Memory的交互

问题描述

我不清楚 x86 架构上不同内存区域的内存排序保证。具体而言,英特尔手册指出 WC、WB 和 UC 遵循不同的内存顺序,如下所示。

WC:弱排序(例如可以重新排序不同位置的两个商店)

WB(以及 WT 和 WP,即所有可缓存的内存类型):处理器排序(又名 TSO,其中较年轻的负载可以在不同位置的较旧存储之前重新排序)

UC:强排序(所有指令都按程序顺序执行,不能重新排序)

我不清楚的是 UC 和其他地区之间的互动。具体来说,手册提到:

(A) UC 访问是强排序的,因为它们总是按程序顺序执行,不能重新排序;和

(B) WC 访问是弱排序的,因此可以重新排序。

因此,在 (A) 和 (B) 之间,不清楚 UC 访问和 WC/WB 访问是如何相互排序的。

1a) [UC-store/WC-store 排序] 例如,假设 x 在 UC 内存中,y 是 WC 内存。那么在下面的多线程程序中,是否可以从y加载1,从x加载0呢?如果线程 0 中的两个存储可以重新排序,这将是可能的。(我mfence在两个负载之间放置了一个,希望它能阻止负载重新排序,因为我不清楚 WC/UC 负载是否可以重新排序;请参见下面的 3a)

       thread 0       |   thread 1
     store [x] <-- 1  |   load [y]; mfence 
     store [y] <-- 1  |   load [x]

1b) 如果相反(对称地)x 在 WC 内存中而 y 在 UC 内存中怎么办?

2a) [UC-store/WB-load ordering] 同样,UC-store 和 WB-load(在不同位置)可以重新排序吗?让我们假设 x 在 UC 内存中,z 在 WB 内存中。那么在下面的多线程程序中,有没有可能两个load都加载0呢?如果 x 和 z 由于存储缓冲而都在 WB 内存中,则这是可能的(或者可以证明:每个线程中较年轻的负载可以在较旧的存储之前重新排序,因为它们位于不同的位置)。但由于对 x 的访问是在 UC 内存中,因此尚不清楚这种行为是否可能。

       thread 0       |   thread 1
     store [x] <-- 1  |   store [z] <-- 1 
     load [z]         |   load [x]

2b) [UC-store/WC-load ordering] 如果 z 在 WC 内存中(而 x 在 UC 内存中)怎么办?那么两个负载都可以加载0吗?

3a) [UC-load/WC-load 排序] UC-load 和 WC-load 可以重新排序吗?再一次,让我们假设 x 在 UC 内存中,而 y 在 WC 内存中。那么,在下面的多线程程序中,是不是可以从y加载1,从x加载0呢?如果可以重新排序两个负载,这将是可能的(我相信两个商店由于干预而无法重新排序sfencesfence根据对 1a 的回答,可能不需要)。

       thread 0               |   thread 1
     store [x] <-- 1; sfence  |   load [y] 
     store [y] <-- 1          |   load [x]

3b) 如果相反(对称地)x 在 WC 内存中而 y 在 UC 内存中怎么办?

4a) [WB-load/WC-load ordering] 如果在上面 3a 的示例中,x 在 WB 内存中(而不是 UC)而 y 在 WC 内存中(和以前一样)怎么办?

4b) 如果(对称地)x 在 WC 内存中而 y 在 WB 内存中怎么办?

标签: memoryx86memory-model

解决方案


英特尔对 UC 内存类型的描述散布在手册第 3 卷的许多地方。我将专注于与内存排序相关的部分。主要的来自第 8.2.5 节:

强未缓存 (UC) 内存类型强制对内存访问使用强排序模型。在这里,对 UC 内存区域的所有读取和写入都出现在总线上,并且不执行乱序或推测访问。

这表明跨不同指令的 UC 内存访问保证按照程序顺序进行观察。第 11.3 节中出现了类似的声明。两者都没有说明 UC 和其他内存类型之间的排序。有趣的是,由于所有 UC 访问的全局可观察性是有序的,因此从 UC 存储到 UC 负载的存储转发是不可能发生的。此外,UC 存储不会在 WCB 中合并或组合,尽管它们确实会通过这些缓冲区,因为这是从核心到非核心的所有请求都必须经过的物理路径。

以下两个引用讨论了 UC 加载和存储与任何类型的先前或以后存储之间的排序保证。重点是我的。

第 11.3 节:

如果 WC 缓冲区被部分填满,写入可能会延迟到下一次序列化事件发生;例如SFENCE 或 MFENCE 指令、CPUID 或其他序列化指令、对未缓存内存的读取或写入、中断发生或 LOCK 指令的执行(包括带有 XACQUIRE 或 XRELEASE 前缀的指令)。

这意味着 UC 访问是相对于早期的 WC 存储进行排序的。将此与 WB 访问进行对比,后者未与早期的 WC 存储一起订购,因为它们的 WB 访问不会导致 WCB 被耗尽。

第 22.34 节:

存储在存储缓冲区中的写入始终按程序顺序写入内存,“快速字符串”存储操作除外(请参见第 8.2.4 节,“快速字符串操作和乱序存储”)。

这意味着存储总是按程序顺序从存储缓冲区提交,这意味着除 WC 之外的所有类型的存储在不同指令中都是按程序顺序观察的。任何类型的商店都不能与早期的 UC 商店重新订购。

英特尔不保证非 UC 加载与早期或晚期 UC 访问(加载或存储)的排序,因此排序在架构上是可能的。

AMD 内存模型针对所有内存类型进行了更准确的描述。它明确指出,非 UC 加载可以使用更早的 UC 存储重新排序,WC/WC+ 加载可以使用更早的 UC 加载重新排序。到目前为止,英特尔和 AMD 模型彼此一致。但是,AMD 模型还指出,UC 负载不能传递任何类型的早期负载。据我所知,英特尔在手册中的任何地方都没有说明这一点。

关于示例 4a 和 4b,英特尔不保证 WB 负载和 WC 负载之间的顺序。AMD 模型允许 WC 负载传递较早的 WB 负载,但反之则不行。


推荐阅读