42、内存一致性与存储原子性解析

内存一致性与存储原子性解析

1. 基本概念

1.1 原子内存系统

在某些协议事务中,所有线程的加载操作在其值被绑定并由内存系统返回后,会自动在全局范围内执行。这类强制访问原子性的内存系统被称为原子内存系统。

1.2 朴素一致性

存储操作具有原子性这一条件限制较大,且可能并非必要。系统中同一数据存在多个副本会引发一致性问题。若一个存在数据多副本的系统能等效为每个数据仅有单一副本的系统,那么该系统就具有一致性,这种宽松形式的一致性被称为朴素一致性,以区别于严格一致性或存储原子性。

1.3 一致性的形式化模型

传统的一致性形式化模型中,线程共享单个内存位置 X。除访问 X 外,线程还会执行其他指令,如算术、浮点运算、分支/跳转以及对其他地址的内存访问,但这些指令并非形式化模型的一部分。该模型的规则如下:
- 每个线程按线程顺序依次对内存位置 X 进行访问。
- 内存每次仅执行并完成对每个位置 X 的一次访问。

根据定义,若一个系统对每个内存位置的内存访问能在具有该内存位置单一副本的系统中按线程顺序正确执行,那么该系统就是一致的。目标系统对特定内存位置的访问的每个正确执行,在形式化模型中也必须是正确执行。

1.4 朴素一致性的定义

一个系统是(朴素)一致的,当且仅当对于每次执行,都能为每个内存位置的所有内存操作构建一个串行顺序,使得:
- 每个线程对该位置的内存操作按线程顺序排列。
- 加载操作返回的值是串行顺序中对同一位置的最新存储操作的值。

存储原子性强制实现严格一致性,因为它为每个内存地址的访问创建了时间顺序,且与对其他地址的访问无关。而在朴素一致性中,串行顺序可以是任何有效顺序(不一定是时间顺序)。因此,基于存储原子性的严格一致性是朴素一致性的一个特殊且重要的情况。

2. 转发存储缓冲区中的朴素一致性

2.1 系统架构

具有可向加载操作转发数据的存储缓冲区的系统是一个具有一致性但不具备存储原子性的硬件示例。在这种架构中,加载操作可以从线程的存储缓冲区获取值并使用,从而绕过缓存和整个内存系统。存储缓冲区是线程私有的,不受缓存一致性协议的约束。

2.2 系统执行示例

以下是一个具有转发存储缓冲区的系统的执行示例:
|时间|T3|T2|T1|C3|C2|C1|注释|
|----|----|----|----|----|----|----|----|
|t0|------------------------L3(A)a0| | |Miss in C3; APT1|SHA|NIC|NIC|----|S|
|t1|1(A)a1| | |SHA|NIC|NIC|S|
|t2|3(A)a2| | |SHA|NIC|NIC|L|
|t3|1(A)a1| | |SHA|NIC|NIC|S|
|t4|2(A)a3| | |SHA|NIC|NIC|L|
|t5|3(A)a2| | |SHA|NIC|NIC|S|
|t6|1(A)a4| | |SHA|NIC|NIC|L|
|t7|2(A)a3| | |SHA|NIC|NIC|L|
|t8|1(A)a4| | |SHA|NIC|NIC|L|
|t9|2(A)a3| | |SHA|NIC|NIC|WB|
|t10|1(A)a4| | |Miss in C1; APT2|INV|MOD|NIC|-------------------------|L|
|t11|1(A)a4| | |Hit in C1|INV|MOD|NIC|--------------------------|S|
|t12|2(A)a5| | |INV|DTY|NIC| |
|t13|------------WB2(A)a5| | |Miss in C2; APT3|INV|DTY|INV|--------------|L|
|t14|1(A)a5| | |Miss in C1; APT4|INV|SHA|SHA|--------------------------|L|
|t15|3(A)a2| | |INV|SHA|SHA| |
|t16|-----------------------WB 3(A)a2| | |Miss in C3; APT5|MOD|INV|INV|–|L|
|t17|1(A)a2| | |Miss in C1; APT6|Hit in C3|SHA|INV|SHA|--------------------------|-----------------------|—|L|
|t18|3(A)a2| | |SHA|INV|SHA| |
|t19|------------L2(A)a2| | |Miss in C2; APT7|SHA|SHA|SHA|---------------|S|
|t20|2(A)a6| | |SHA|SHA|SHA|L|
|t21|2(A)a6| | |SHA|SHA|SHA| |
|t22|------------WB2(A)a6| | |Upgrade in C2; APT8|INV|MOD|INV|--------------|

2.3 构建全局顺序

为证明系统的一致性,需构建所有内存访问的全局顺序:
1. 首先,以所有全局执行(GP)访问(即在缓存中原子执行的访问)构建顺序:
- L3(A)a0 ≺ WB1(A)a4 ≺ L1(A)a4 ≺ WB2(A)a5 ≺ L1(A)a5 ≺ WB3(A)a2 ≺ L1(A)a2 ≺ L3(A)a2 ≺ L2(A)a2 ≺ WB2(A)a6。
- 其中,WB 并非线程访问,而是标记缓存从存储缓冲区更新的时间点。这些缓存更新会在缓存带宽可用时随机发生,其时间取决于对其他内存位置的存储操作。
2. 然后,将全局顺序中的所有 WB 访问扩展为存储缓冲区中正在退出的条目的所有加载和存储操作:
- L3(A)a0 ≺ S1(A)a1 ≺ L1(A)a1 ≺ S1(A)a4 ≺ L1(A)a4 ≺ L1(A)a4 ≺ S2(A)a3 ≺ L2(A)a3 ≺ L2(A)a3 ≺ S2(A)a5 ≺ L1(A)a5 ≺ S3(A)a2 ≺ L3(A)a2 ≺ L3(A)a2 ≺ L1(A)a2 ≺ L3(A)a2 ≺ L2(A)a2 ≺ S2(A)a6 ≺ L2(A)a6。

在这个全局顺序中,每个线程的访问按线程顺序排列,且加载操作返回的值是顺序中最新存储操作的值。因此,该执行具有一致性。值的顺序与时间顺序不同:
- 时间顺序:a0 ≺ a1 ≺ a2 ≺ a3 ≺ a4 ≺ a5 ≺ a6。
- 一致性顺序:a0 ≺ a1 ≺ a4 ≺ a3 ≺ a5 ≺ a2 ≺ a6。

尽管如此,所有线程都按相同的一致性顺序观察值:
- T1 观察到:a1 ≺ a4 ≺ a5 ≺ a2。
- T2 观察到:a3 ≺ a5 ≺ a2 ≺ a6。
- T3 观察到:a0 ≺ a2。

线程在一致性顺序中跳过某些值是因为它们未通过加载操作观察到这些值,但它们观察到的值遵循相同的一致性顺序。由此可得出结论,具有转发存储缓冲区的系统是一致的。

2.4 一致性流程 mermaid 图

graph LR
    A[开始] --> B[以 GP 访问构建初始顺序]
    B --> C[扩展 WB 访问]
    C --> D[检查线程顺序和最新存储值]
    D --> E{是否满足条件}
    E -- 是 --> F[系统具有一致性]
    E -- 否 --> G[系统不具有一致性]

3. 朴素一致性的推广

3.1 隐私原则

可以定义一致性的“隐私原则”:线程在其最新值传播到其他线程之前加载并使用这些值,不会违反一致性。因为其他线程无法观察到这些局部值,所以一旦其中一个值变为公共值(即变为 GP),就可以轻松地将局部访问插入全局顺序中。

3.2 其他推广情况

以下情况在逻辑上等同于转发存储缓冲区,满足朴素一致性但不满足存储原子性:
- 无锁定缓存中,在存储缺失挂起时,可分配缓存行。在存储缺失期间,局部存储操作可填充值,局部加载操作可使用这些值。
- 多线程芯片多处理器中,共享同一核心或缓存的线程可读取缓存中未全局传播(非 GP)的彼此的值。可通过在每个缓存级别分层应用隐私原则来实现此结果。
- 分层缓存系统中的核心集群或多核可在共享缓冲区或多级分层的无锁定缓存中共享非 GP 值。
- 在具有失效协议的 cc - NUMAs 中,线程在接收到块时可对其进行修改,并使用自己的值与其他本地线程共享,即使在目录处的一致性事务未关闭也是如此。

一般来说,每个线程和线程组在内存系统中都有一个存储管道,由该线程或线程组的所有挂起存储操作组成。线程和线程组可以从其存储管道中读取值,即使这些值尚未全局传播,也不会违反朴素一致性。与存储原子性相比,所有存储操作的序列化点仍然是一致内存系统的重要组成部分,它构成了任何可能顺序的基础。存储操作的序列化可在芯片多处理器级别甚至多个芯片多处理器集群级别分层进行,但最终存储操作必须全局排序。在一致内存系统中,加载操作无需在 GP 值上执行,只要该值最终能全局排序即可。因此,一致内存系统中的加载操作可以比存储原子内存系统中的加载操作更快地提交其值。在一致内存系统中,加载操作只需将其值绑定到最接近的一致副本,即使该值尚未成为 GP。而在存储原子系统中,加载操作返回的值必须是 GP。

4. 朴素一致性的重要性

4.1 不符合一致性的执行示例

为了展示一致性的主要影响,我们通过几个示例来说明不符合一致性的执行情况:
- 示例 7.3 :一个线程 T2 以无序方式观察另一个线程 T1 的两次存储操作。
|线程 T1|线程 T2|
|----|----|
|S1(A)a1| |
| |L2(A)a2|
|S1(A)a2| |
| |L2(A)a1|
由于在任何有效的一致性顺序中,T1 的两次存储操作必须按线程顺序排列,所以 T2 的第二次加载操作返回 a1 是不可能的,因此该执行不具有一致性。
- 示例 7.4 :两个线程彼此观察对方的存储操作,而不是自己的。
|线程 T1|线程 T2|
|----|----|
|S1(A)a1| |
| |S2(A)a2|
|L1(A)a2| |
| |L2(A)a1|
如果 T1 的加载操作返回 a2,那么在任何一致性顺序中,T2 的存储操作必须在 T1 的存储和加载操作之间执行。这样,T2 的加载操作就不能返回 a1,所以该执行不具有一致性。
- 示例 7.5 :两个线程以不同顺序观察另外两个线程的存储操作。
|线程 T1|线程 T2|线程 T3|线程 T4|
|----|----|----|----|
|S1(A)a1| | | |
| |S2(A)a2| | |
| | |L3(A)a1| |
| | | |L4(A)a2|
| | |L3(A)a2| |
| | | |L4(A)a1|
T3 的加载操作先返回 T1 存储的值,然后返回 T2 存储的值,而 T4 以相反顺序观察这两次存储操作。在强制线程顺序的六个访问的一致性串行顺序中,这种情况无法协调,所以该执行不具有一致性。

4.2 朴素一致性违规示例

在推测式乱序(OoO)处理器的加载/存储队列中,如果不采取特殊措施,可能会违反朴素一致性。例如,再次考虑示例 7.3 的代码,T2 的两个加载操作针对同一地址。在推测式 OoO 处理器的加载/存储队列中,由于这两个加载操作的地址可能来自不同的地址寄存器,可能会出现 T2 的第一个加载操作在加载/存储队列中等待其地址,而第二个(较新的)加载操作准备好的情况。如果在加载/存储队列中不采取特殊措施,第二个加载操作可能会返回 T1 的第一次存储操作的值,然后,稍后,T2 的第一个(较旧的)加载操作可能会返回 T2 的第二次存储操作的值,这违反了朴素一致性。

为避免此问题,远程存储操作必须窥探加载队列,并对具有相同地址的条目进行标记。在加载操作可以向缓存发出请求之前,它必须检查较新的加载操作。如果较新的加载操作已被标记并且已经返回其值,则必须回滚执行,因为它违反了朴素一致性。这种对向缓存发出加载操作的额外约束在单处理器中不会导致任何危险,但必须添加到任何支持多处理器的核心中。

4.3 朴素一致性的作用

朴素一致性除了强制对同一地址的所有存储操作进行全局排序外,还具有以下重要作用:
- 促进内存一致性模型的实现 :一致性通过及时、有选择且高效地传播值(由于一致性协议提供的过滤以及转发请求和回复的硬件机制),便于内存一致性模型的实现。
- 确保内存状态的一致性 :一致性确保如果计算突然停止(例如,在上下文切换时),在所有正在执行的指令完成且网络和所有缓冲区清空后,内存状态会收敛到所有数据的一致状态。例如,在转发存储缓冲区的示例中,在允许下一个上下文运行之前,每个存储缓冲区中的值必须全局执行,从而在上下文切换之前在整个系统中对值进行排序并建立全局传播(GP)值。
- 处理线程迁移的内存问题 :出于同样的原因,一致性可以处理由于线程迁移引起的内存问题。

5. 朴素一致性的问题

5.1 不可组合性

朴素一致性不能与对不同内存地址施加的其他可能顺序组合。也就是说,即使一个执行相对于任何两个变量分别是一致的,对这两个变量的访问之间施加的任何其他顺序都可能导致无法排序的执行。推理无法排序的执行比推理可以排序的执行要复杂得多。例如,大多数形式验证方法都依赖于所有访问的一致顺序。

5.2 冲突示例

考虑以下涉及两个地址 A 和 B 的执行:
|初始化|线程 T1|线程 T2|
|----|----|----|
|A = 0; B = 0;|S1(A)1|S2(B)1|
| |L1(A)1| |
| | |L2(B)1|
| |L1(B)0| |
| | |L2(A)0|

根据朴素一致性的形式化模型,此执行对于 A 或 B 分别是一致的,因此是正确的。然而,在以下情况下,一致性顺序将与每个线程中两个加载操作上施加的其他顺序冲突:
- 全局顺序约束 :内存模型的一个常见约束是在同一线程中的两个加载操作之间强制全局顺序,即使它们访问不同的内存位置。
- 数据依赖 :第二个加载操作依赖于第一个加载操作,因为第一个加载操作的结果用于计算第二个加载操作的地址。在第一个加载操作返回其值之前,第二个加载操作甚至无法开始执行或预取。
- 同步屏障 :程序员可能在两个线程的两个加载操作之间插入屏障同步,从而在每个线程的两个加载操作之间强制时间顺序。

在这三种情况下,每个线程中的两个加载操作被迫依次执行,这些顺序与一致性顺序冲突,导致整个执行无法全局排序。在上述代码中,在一致的机器中,必须在两个线程和两个地址上强制执行线程顺序。在 T1 中,A 的加载操作必须跟随 A 的存储操作(由于一致性),并且 B 的加载操作必须跟随 A 的加载操作(由于其他顺序)。

5.3 冲突流程 mermaid 图

graph LR
    A[开始执行] --> B[检查一致性顺序]
    B --> C{是否满足一致性}
    C -- 是 --> D[检查其他顺序约束]
    D --> E{是否存在冲突}
    E -- 是 --> F[执行无法全局排序]
    E -- 否 --> G[执行可以全局排序]
    C -- 否 --> F

综上所述,朴素一致性在内存系统中具有重要作用,但也存在不可组合性等问题。在设计和实现内存系统时,需要充分考虑这些因素,以确保系统的正确性和性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值