为什么原子 RMW 指令的加载部分不能将较早的存储传递到 TSO(x86) 内存一致性模型中的不相关位置?
Why cannot the load part of the atomic RMW instruction pass the earlier store to unrelated location in TSO(x86) memory consistency model?
众所周知,x86 体系结构由于使用了写入缓冲区而未实现顺序一致性内存模型,因此可能会发生存储-> 加载重新排序(可以提交较晚的加载,而较早的存储仍驻留在写入缓冲区中等待提交到 L1 缓存)。
在A Primer on Memory Consistency and Coherence中我们可以读到Total Store Order(TSO)内存一致性模型(应该与x86非常相似)中的Read-Modify-Write(RMW)操作:
... we consider
the RMW as a load immediately followed by a store. The load part of
the RMW cannot pass earlier loads due to TSO’s ordering rules. It
might at first appear that the load part of the RMW could
pass earlier stores in the write buffer, but this is not legal. If
the load part of the RMW passes an earlier store, then the store
part of the RMW would also have to pass the earlier store
because the RMW is an atomic pair. But because stores are not
allowed to pass each other in TSO, the load part of the RMW cannot
pass an earlier store either.
好的,原子操作必须是原子的,即RMW访问的内存位置在RMW操作期间不能被另一个threads/cores访问,但是如果前面的存储通过加载部分原子操作与RMW访问的内存位置无关?假设我们有以下几条指令(伪代码):
store int32 value in 0x00000000 location
atomic increment int32 value in 0x10000000 location
第一个存储已添加到写入缓冲区,正在等待轮到它。同时,原子操作从另一个位置(甚至在另一个缓存行)加载值,传递第一个存储,并在第一个之后将存储添加到写缓冲区中。在全局内存顺序中,我们将看到以下顺序:
加载(原子的一部分)-> 存储(顺序)-> 存储(原子的一部分)
是的,从性能的角度来看,这可能不是最佳解决方案,因为我们需要将原子操作的缓存行保持在读写状态,直到写入缓冲区中的所有先前存储都已提交,但是, 撇开性能方面的考虑,是否存在任何违反 TSO 内存一致性模型的情况,我们允许 RMW 操作的加载部分将较早的存储传递到不相关的位置?
您可以针对不同地址的任何存储 + 加载对提出相同的问题:由于乱序执行,加载可能比旧存储在内部更早执行。在 X86 中这是允许的,因为:
Loads may be reordered with older stores to different locations but not with older stores to the same location
(来源:Intel 64 Architecture Memory Ordering White Paper)
但是,在您的示例中,锁定前缀会阻止这种情况,因为(来自同一组规则):
Locked instructions have a total order
这意味着锁将强制执行内存屏障,如 mfence(实际上一些编译器使用锁定操作作为栅栏)。这通常会使 CPU 停止加载的执行,直到存储缓冲区耗尽,强制存储首先执行。
since we need to hold the cache line for the atomic operation in
read-write state until all preceding stores from the write buffer are
committed, but, performance considerations aside
如果你在操作S的时候持有锁L,而你做的操作S与L阻止的操作具有相同的性质,那就是存在S'可以被L阻塞(延迟)并且S可以被阻塞(延迟)通过 L',然后 你有死锁的秘诀,除非你保证是唯一一个这样做的演员(这会使整个原子事情变得毫无意义)。
众所周知,x86 体系结构由于使用了写入缓冲区而未实现顺序一致性内存模型,因此可能会发生存储-> 加载重新排序(可以提交较晚的加载,而较早的存储仍驻留在写入缓冲区中等待提交到 L1 缓存)。
在A Primer on Memory Consistency and Coherence中我们可以读到Total Store Order(TSO)内存一致性模型(应该与x86非常相似)中的Read-Modify-Write(RMW)操作:
... we consider the RMW as a load immediately followed by a store. The load part of the RMW cannot pass earlier loads due to TSO’s ordering rules. It might at first appear that the load part of the RMW could pass earlier stores in the write buffer, but this is not legal. If the load part of the RMW passes an earlier store, then the store part of the RMW would also have to pass the earlier store because the RMW is an atomic pair. But because stores are not allowed to pass each other in TSO, the load part of the RMW cannot pass an earlier store either.
好的,原子操作必须是原子的,即RMW访问的内存位置在RMW操作期间不能被另一个threads/cores访问,但是如果前面的存储通过加载部分原子操作与RMW访问的内存位置无关?假设我们有以下几条指令(伪代码):
store int32 value in 0x00000000 location
atomic increment int32 value in 0x10000000 location
第一个存储已添加到写入缓冲区,正在等待轮到它。同时,原子操作从另一个位置(甚至在另一个缓存行)加载值,传递第一个存储,并在第一个之后将存储添加到写缓冲区中。在全局内存顺序中,我们将看到以下顺序:
加载(原子的一部分)-> 存储(顺序)-> 存储(原子的一部分)
是的,从性能的角度来看,这可能不是最佳解决方案,因为我们需要将原子操作的缓存行保持在读写状态,直到写入缓冲区中的所有先前存储都已提交,但是, 撇开性能方面的考虑,是否存在任何违反 TSO 内存一致性模型的情况,我们允许 RMW 操作的加载部分将较早的存储传递到不相关的位置?
您可以针对不同地址的任何存储 + 加载对提出相同的问题:由于乱序执行,加载可能比旧存储在内部更早执行。在 X86 中这是允许的,因为:
Loads may be reordered with older stores to different locations but not with older stores to the same location
(来源:Intel 64 Architecture Memory Ordering White Paper)
但是,在您的示例中,锁定前缀会阻止这种情况,因为(来自同一组规则):
Locked instructions have a total order
这意味着锁将强制执行内存屏障,如 mfence(实际上一些编译器使用锁定操作作为栅栏)。这通常会使 CPU 停止加载的执行,直到存储缓冲区耗尽,强制存储首先执行。
since we need to hold the cache line for the atomic operation in read-write state until all preceding stores from the write buffer are committed, but, performance considerations aside
如果你在操作S的时候持有锁L,而你做的操作S与L阻止的操作具有相同的性质,那就是存在S'可以被L阻塞(延迟)并且S可以被阻塞(延迟)通过 L',然后 你有死锁的秘诀,除非你保证是唯一一个这样做的演员(这会使整个原子事情变得毫无意义)。