其中大部分已在其他问答中涵盖(尤其是后来的问答)C++ 如何仅使用 MOV 在 x86 上实现释放和获取? https://stackoverflow.com/questions/60314179/c-how-is-release-and-acquire-achieved-on-x86-only-using-mov),但我会在这里做一个总结。不过,这是个好问题,将所有这些收集到一个地方还是很有用的。
在 x86 上,每个 asm 加载都是获取加载。为了有效地实现这一点,现代 x86 硬件会推测性地早于允许的时间加载,然后检查该推测。 (可能会导致内存顺序错误推测管道核问题。)为了跟踪这一点,英特尔将加载和存储缓冲区的组合称为“内存顺序缓冲区”。
弱顺序 ISA 不必推测,它们可以按任何顺序加载。
x86 商店订购通过仅让存储按程序顺序从存储缓冲区提交到 L1d 来维护。
至少在 Intel CPU 上,存储缓冲区条目是已分配对于商店来说,发行时(从前端进入ROB + RS)。所有微指令都需要为其分配一个 ROB 条目,但某些微指令还需要分配其他资源,例如加载或存储缓冲区条目、它们读/写的寄存器的 RAT 条目等。
所以我认为存储缓冲区本身is ordered。当存储地址或存储数据微指令执行时,它仅将地址或数据写入其已分配的存储缓冲区条目。由于提交(释放 SB 条目)和分配都是按程序顺序进行的,因此我假设它实际上是一个具有头部和尾部的循环缓冲区,就像 ROB 一样。 (与 RS 不同)。
避免 LoadStore 基本上是免费的:加载在执行之前无法退出(从缓存中获取数据)。商店无法承诺,直到after它退休了。按顺序自动退出意味着所有先前的加载都在存储“毕业”并准备好提交之前完成。
在实践中,可以进行加载存储重新排序的弱有序 uarch 可能会记分板加载并在 ROB 中跟踪它们:一旦知道它们没有故障,就让它们退出,即使数据尚未到达。
这似乎更有可能出现在有序核心上,但我不知道。因此,您可能有一个已退役的负载,但如果在数据实际到达之前有任何东西试图读取它,则寄存器目标仍然会停止。我们知道,有序核心实际上以这种方式工作,不需要负载complete在后面的指令可以执行之前。 (这就是为什么使用大量寄存器的软件流水线在此类内核上如此有价值,例如实现 memcpy。立即在有序内核上读取加载结果会破坏内存并行性。)
如何通过按顺序提交实现加载->存储重新排序? https://stackoverflow.com/questions/52215031/how-is-load-store-reordering-possible-with-in-order-commit更深入地讨论有序与无序。
屏障指示
唯一对普通商店有作用的屏障指令是mfence
实际上,这会停止内存操作(或整个管道),直到存储缓冲区耗尽。加载和存储是唯一需要重新排序的指令吗? https://stackoverflow.com/questions/50494658/are-loads-and-stores-the-only-instructions-that-gets-reordered涵盖了 Skylake-with-updated-microcode 的行为,就像lfence
以及。
lfence
主要存在的微架构效应是阻止后续指令的发出,直到所有先前的指令都离开无序后端(退休)。用例lfence
内存排序几乎不存在。
Related:
- C++ 如何仅使用 MOV 在 x86 上实现释放和获取? https://stackoverflow.com/questions/60314179/c-how-is-release-and-acquire-achieved-on-x86-only-using-mov
- 内存屏障的传递性/累积性属性是如何在微架构上实现的? https://stackoverflow.com/questions/58018486/how-is-the-transitivity-cumulativity-property-of-memory-barriers-implemented-mic
- x86 CPU 有多少条内存屏障指令? https://stackoverflow.com/questions/50323347/how-many-memory-barriers-instructions-does-an-x86-cpu-have
- 如何体验“LFENCE或SFENCE无法通过较早的读/写” https://stackoverflow.com/questions/56705436/how-can-i-experience-lfence-or-sfence-can-not-pass-earlier-read-write
- lock xchg 与 mfence 具有相同的行为吗? https://stackoverflow.com/questions/40409297/does-lock-xchg-have-the-same-behavior-as-mfence
- 英特尔内存模型是否使 SFENCE 和 LFENCE 变得冗余? https://stackoverflow.com/questions/32705169/does-the-intel-memory-model-make-sfence-and-lfence-redundant
-
了解 lfence 对具有两个长依赖链的循环的影响,以增加长度 https://stackoverflow.com/questions/51986046/understanding-the-impact-of-lfence-on-a-loop-with-two-long-dependency-chains-fo详细介绍了 LFENCE 如何停止执行后续指令,以及这对性能意味着什么。
-
我什么时候应该使用 _mm_sfence _mm_lfence 和 _mm_mfence https://stackoverflow.com/questions/4537753/when-should-i-use-mm-sfence-mm-lfence-and-mm-mfence/50780314#50780314高级语言的内存模型比 x86 弱,因此有时只需要一个不编译为 asm 指令的屏障。使用
_mm_sfence()
当你没有使用任何 NT 存储时,只会让你的代码无缘无故地变慢atomic_thread_fence(mo_release)
.