我发现这部分不完整:
具有此内存顺序的加载操作执行获取操作,
store 执行释放操作,read-modify-write 执行
一个获取操作和一个释放操作,加上一个总数
存在所有线程观察所有修改的顺序
相同的顺序。
如果这些东西(释放存储、获取加载和总存储顺序)实际上足以提供顺序一致性,这意味着释放和获取操作本身的顺序将比实际更严格。
让我们看看下面的反例:
CPU1:
a = 1 // release store
int r1 = b // acquire load
然后根据上面对 SC 的定义(以及已知的属性顺序一致性必须符合名称),我会假设 a 的存储和 b 的负载不能重新排序:
- 我们有一个发布存储和一个获取加载
- 我们(可以)对所有加载/存储有一个总订单
所以我们已经满足了上述顺序一致性的定义。
但是可以重新排序释放存储,然后是获取加载到不同的地址。典型的例子是 Dekker 算法。因此,上述对 SC 的定义被打破,因为它缺少内存顺序需要保留程序顺序。除了编译器搞砸了事情之外,这种违规的典型原因是大多数现代 CPU 拥有的存储缓冲区可能会导致较旧的存储被重新排序,并将新的加载到不同的地址。
单个总顺序与 CPU 本地指令重新排序不同,您可以使用例如存储缓冲区。它实际上意味着某个操作在内存顺序中生效的时刻,没有人应该能够不同意这一点。对此的标准试金石是独立读取独立写入 (IRIW):
CPU1:
A=1
CPU2:
B=1
CPU3:
r1=A
[LoadLoad]
r2=B
CPU4:
r3=B
[LoadLoad]
r4=A
那么 CPU3 和 CPU4 会不会以不同的顺序看到不同地址的存储?如果答案是肯定的,则不存在加载/存储的总订单。
加载/存储没有总顺序的另一个原因是存储到加载转发 (STLF)。
CPU1:
A=1
r1=A
r2=B
CPU2:
B=1
r3=B
r4=A
r1=1, r2=0, r3=1 和 r4=0 有可能吗?
在 X86 上,由于存储到负载转发,这是可能的。因此,如果 CPU1 先存储 A,然后加载 A,则 CPU 必须在存储缓冲区中查找 A 的值。这导致 A 的存储不是原子的;本地 CPU 可以及早看到存储,结果是不存在加载/存储的总顺序。
因此,不是对所有加载/存储进行总排序,而是将其简化为对存储的总排序,这就是 X86 为其内存模型(总存储排序)命名的方式。
[编辑]
做了一些澄清。我清理了一些文本并清理了原始示例,因为它具有误导性。