【发布时间】:2021-10-25 06:17:13
【问题描述】:
实现锁的时候,strict consistency和sequential consistency都只需要cas(tas)指令,都不需要barriers。由于cpu上没有物理世界的观察者,观察者视角的一致性没有实际意义。
【问题讨论】:
标签: caching cpu cpu-cache consistency
实现锁的时候,strict consistency和sequential consistency都只需要cas(tas)指令,都不需要barriers。由于cpu上没有物理世界的观察者,观察者视角的一致性没有实际意义。
【问题讨论】:
标签: caching cpu cpu-cache consistency
我对严格一致性的定义不是很熟悉。
下面是一个很长的故事,信息过载的风险很高,但我希望它能够展示一致性如何适应内存一致性模型,或者至少为您提供足够的结构来寻找更多信息。
缓存一致性通常根据每个位置的顺序一致性 (SC) 来定义。 SC不需要尊重请求的实时顺序;因此,只要保留程序顺序 (PO),读取和写入就可以发生偏差。这会阻止 SC 可组合,因此如果您有一个连贯位置的缓存(因此每个位置的 SC),那么整个缓存不需要是 SC(每个位置都有 PO)。结果是不能保证存在可以解释执行的内存顺序的总顺序。所以简单来说,如果每个位置的缓存只有SC,就不能建立SC缓存。
如果将使用每个位置的线性化而不是每个位置的 SC 来实现一致性,则可以制作线性化缓存(每个位置使用 PO)。这也称为“原子内存”。
通过线性化,请求的实时顺序得到尊重。线性化的优点是它是可组合的。因此,如果您有一个可线性化的位置系统,那么缓存作为一个整体将是可线性化的。因此,在解释执行的内存顺序上总是存在至少 1 个总顺序。因此,如果 CPU 会在加载/存储到达缓存之前阻止任何重新排序,结合线性化缓存,您可以创建一个 SC CPU。
缓存一致性的典型协议是 MESI:写入需要等到缓存行在所有 CPU 上都已失效,然后才能将更改写入缓存行。这种方法的结果是基于 MESI 的缓存是可线性化的。
大多数 CPU 都有存储缓冲区,因此较旧的存储可以通过新的加载重新排序到不同的地址,因此内存顺序不会将较旧的存储与较新的加载排序到不同的地址。因此,[StoreLoad] 作为内存顺序的要求被删除。删除 [StoreLoad] 并不会阻止您对所有内存访问进行总排序,它只是意味着内存模型不关心在总排序中可以找到它们的顺序而不是内存顺序。
这里的关键问题是当一个存储之后是加载到同一地址时。有两种可能的解决方案:
1(严格的解决方案):加载需要等待存储提交到缓存才能执行加载。这种方法的优点是加载和存储在内存顺序中正确排序,并且存在内存顺序的总顺序。这是 IBM-370 的内存模型。所以 IBM-370 是 SC + 删除 [StoreLoad]。
2(一个轻松的解决方案)负载在存储缓冲区内查找。如果匹配,它将返回存储的值。这称为存储到加载转发 (STLF)。这里的问题是不可能在内存顺序上创建一个总顺序,因为存储不是原子的。根据定义,加载在它读取的存储之后全局排序,但是由于在全局执行存储(提交到缓存)之前执行加载(从存储缓冲区加载),因此存储和加载到同一地址不是按内存顺序正确排序。以下测试证明了这一点:
A=B=0
CPU1:
A=1
r1=A
r2=B
CPU2:
B=1
r3=B
r4=A
使用 STLF 可以是 r1=1, r2=0, r3=1, r4=0,但使用 IBM-370/SC/Linearizability 则不可能。在上面的示例中,r1=A 的负载在 A=1 之后和 A=1 之前排序(由于 STLF)。因此,不存在所有内存操作的总顺序,因为加载将在存储之前和之后进行排序。相反,内存模型的要求被放宽到所有存储需要存在的总顺序。这就是我们如何获得 Total Store Order,即 X86 的内存模型。所以 TSO 是 SC 的松弛,其中 [StoreLoad] 被删除 + STLF。
我们可以进一步放宽记忆顺序。因此,使用 TSO,我们可以保证所有商店中至少存在 1 个总订单,但这是因为缓存是可线性化的。如果我们放宽这个要求,我们就会得到处理器一致性(PC)。因此,PC 允许使用新负载对旧存储进行重新排序,并且需要一致的缓存,但是可以看到由不同 CPU 写入的不同地址是无序的(因此没有对存储进行总排序)。
这通过独立写入的独立读取 (IRIW) 石蕊测试来演示
A=B=0
CPU1
A=1
CPU2
B=1
CPU3:
r1=A
r2=B
CPU4:
r3=B
r4=A
难道我们看到r=1,r2=0,r3=1,r4=0。那么 CPU3 和 CPU4 会不会以不同的顺序看到对 A、B 的写入?如果存在商店的总订单(例如 TSO/IBM-370/SC/Linearizability),那么这是不可能的。但在 PC 上,这是允许的。
我希望这个例子清楚地表明,“只是”一个一致的缓存仍然是一个非常弱的属性。
Linearizability、SC 和 IBM-370 也被称为 atomic/store-atomic/single-copy store atomic 因为只有一个数据副本。有一个逻辑点,存储对所有 CPU 都可见。 TSO 被称为多副本存储原子,因为存储可以早期对发布 CPU 可见 (STLF)。 像 PC 这样的内存模型被称为非原子(或非存储原子),因为没有逻辑时刻存储对其他 CPU 可见。
CAS 指令不仅仅是顺序一致的;它是可线性化的。并且根据架构,CAS 涉及栅栏。例如。像 X86 上的 CMPXCHG 这样的原子指令有一个隐式锁,它的作用就像一个完整的屏障。因此,它保证保留所有 4 个栅栏,尽管它只需要保留 [StoreLoad],因为其他栅栏是自动提供的。
有关此主题的更多信息,请参阅免费提供的"A primer on memory consistency and cache coherence 2e"。
注 1: 内存模型的一个常见要求是,存在解释执行的内存模型中所有加载和存储的某种总顺序。这可以通过使用拓扑排序来完成。
注2: 只要没有人能够观察到,就可以违反内存顺序中的任何要求。
注 3: 如果存在加载/存储的总顺序(每个位置或所有位置),则加载需要在内存顺序中查看它之前的最新存储。
【讨论】:
当存在隐式写入时,严格一致性与顺序一致性是有区别的。在处理 I/O 设备时,隐式写入并非闻所未闻。
一个明显的例子是时钟;时钟在每个时钟节拍都有隐式写入,与读取无关。
一个也许更有意义的例子是一个缓冲区,它表示为一个单字地址。对缓冲区的写入只有在之前的写入被读取后才会变得可见,因此即使更新该地址时一致性机制可以看到此类写入,写入可见性的顺序也将取决于读取缓冲区的顺序。写入对于一致性机制可能实际上是不可见的,因为它们来自非一致的 I/O 活动,或者因为接口指定了一个不同的地址,用于将值添加到缓冲区,而不是用于从缓冲区获取值的地址(其中一个从写入地址读取可能会提供已填充的缓冲区条目数或空置数)。
共享的伪随机数生成器或访问计数器会产生类似的读取副作用,即推进“缓冲区”中的位置。
C 编程语言的 volatile 关键字通知编译器变量可以在没有显式写入的情况下更改,从而识别出编程语言级别的严格一致性和顺序一致性之间的区别。
【讨论】: