如果您“放宽” seq_cst 的一些订购要求,就会有mo_acq_rel
(以及纯获取和纯发布)。
比这还轻松mo_relaxed
;没有订购。别的什么,只是原子性1。
在为大多数ISA编译时,seq_cst 加载可以使用与获取加载相同的 asm;我们选择让商店变得昂贵,而不是负载。 C/C++11 映射到适用于 ISA 的处理器,包括 x86、POWER、ARMv7、ARMv8,包括一些 ISA 的 2 个替代方案。为了彼此兼容,同一平台的编译器必须选择相同的策略,否则一个函数中的 seq_cst 存储可能会随着另一个函数中的 seq_cst 加载而重新排序。
在内存模型包括存储缓冲区和一致缓存的典型 CPU 上,如果您在同一个线程中存储然后重新加载,则seq_cst 要求您在存储对所有线程全局可见之前不要让重新加载发生。这意味着在 seq_cst 存储之后或 seq_cst 加载之前的完整屏障(包括 StoreLoad )。由于廉价负载比廉价商店更有价值,例如,通常的映射选择 x86 mov
+mfence
商店。(同样适用于加载任何其他位置;在 store 提交之前不能这样做。这就是 Jeff Preshing 的Memory Reordering Caught in the Act的内容。)
这是在所有线程都可以同意的不同变量上创建全局总操作顺序的实际示例。(x86 asm 为 pure-store 的 pure-load / release 提供了获取,或为lock
-prefixed atomic RMW 指令提供了 seq_cst。因此 Preshing 的 x86 asm 示例完全对应于 C++11mo_release
存储而不是mo_seq_cst
.
ARMv8 / AArch64 很有趣:它有 STLR(顺序发布存储)和 LDAR(获取加载)。与其在存储缓冲区耗尽并将 STLR 提交到 L1d 缓存(全局可见性)之前暂停所有后续加载,实现可以更有效。
等待刷新只需要在 LDAR 执行之前发生;其他加载可以执行,甚至以后的存储也可以提交到 L1d。(顺序释放仍然至少是一个单向障碍)。要做到如此高效/弱,LDAR 必须探测存储缓冲区以检查 STLR 存储。但是,如果你能做到这一点,mo_seq_cst
如果你在那之后不立即执行 seq_cst 加载任何其他内容,那么存储空间可能会比 x86 上的便宜得多。
在大多数其他 ISA 上,恢复顺序一致性的唯一选择是完整的屏障指令(在存储之后)。这会阻止所有以后的加载和存储发生,直到所有先前的存储都提交到 L1d 缓存之后。但这不是ISO C++seq_cst
所暗示或要求的,只是只有 AArch64 有能力与 ISO C++ 要求一样强大,但不会更强。
(为许多其他弱序 ISA 进行编译需要将 acq / release 提升到比需要的强得多,例如 ARMv7 需要一个完整的发布存储屏障。)
脚注 1:(就像你在旧的 C++11 之前的代码中得到的一样,使用自己的原子,volatile
没有任何障碍)。