内存防护是否会阻塞多核 CPU 中的线程?

Does memory fencing blocks threads in multi-core CPUs?

我正在阅读英特尔指令集指南 64-ia-32 guide 了解内存栅栏。我的问题是,对于 SFENCE 的示例,为了确保所有存储操作都是全局可见的,多核 CPU 是否将所有线程甚至 运行 停放在其他内核上,直到缓存一致性实现了?

障碍不会让其他threads/cores等待。它们使当前线程中的一些操作等待,这取决于它是哪种屏障。非内存指令的乱序执行不一定会被阻止。

障碍甚至不会让您的 loads/stores 更快地被其他线程看到; CPU 个内核已经尽快将 中的存储提交(退役)到 L1d 缓存。 (在遵循所有必要的 MESI 一致性规则之后,x86 的强内存模型即使没有障碍也只允许存储按程序顺序提交)。

障碍不一定命令指令执行,它们命令全局可见性,即最远的结果存储缓冲区结束。


mfence (or a like lock add or xchg [mem], reg) 使 current 线程中的所有稍后 loads/stores 等待直到所有先前加载并且存储已完成并且全局可见(即存储缓冲区已刷新)。

mfence 在 Skylake 上的实现方式是暂停整个核心,直到存储缓冲区耗尽。看我的回答 Are loads and stores the only instructions that gets reordered? 详情;这种额外的减速是为了修复一个错误。但是 locked 操作和 xchg 与 Skylake 上的不同;它们是完整的内存屏障,但它们仍然允许乱序执行 imul eax, edx,因此我们有证据表明它们不会拖延整个核心。

对于超线程,我认为这种停滞发生在每个逻辑线程,而不是整个核心。

但请注意,mfence 手动条目并没有说明任何关于停止核心的信息,因此未来的 x86 实现可以自由地使其更高效(如 lock or dword [rsp], 0),并且只会阻止稍后从读取 L1d 缓存加载而不会阻塞后面的非加载指令。


sfence only does anything if there are any NT stores in flight. It doesn't order loads at all, so it doesn't have to stop later instructions from executing. See Why is (or isn't?) SFENCE + LFENCE equivalent to MFENCE?.

它只是在存储缓冲区中放置了一个屏障,阻止 NT 存储在其中重新排序,并强制较早的 NT 存储在 sfence 屏障离开存储缓冲区之前全局可见。 (即写组合缓冲区必须刷新)。但它可能在到达存储缓冲区末尾之前就已经从核心的乱序执行部分(ROB,或 ReOrder Buffer)退出。)

另见


lfence 作为内存屏障几乎毫无用处:它只会阻止 movntdqa 从 WC 内存中的加载与后来的 loads/stores 重新排序。你几乎永远不需要那个。

lfence 的实际用例主要涉及它的 Intel(但不是 AMD)行为,即在它自己退役之前不允许执行后续指令。 (因此 lfence; rdtsc 在 Intel CPUs 上可以避免让 rdtsc 过早读取时钟,作为 cpuid; rdtsc 更便宜的替代方案)

lfence 最近的另一个重要用例是阻止推测执行(例如,在条件或间接分支之前),以缓解 Spectre。这完全是基于其 Intel 保证的部分序列化副作用,与其 LoadLoad + LoadStore 屏障效应无关。

lfence not 必须等待存储缓冲区耗尽才能从 ROB 中退出,因此 LFENCE + SFENCE 的组合没有像MFENCE。 Why is (or isn't?) SFENCE + LFENCE equivalent to MFENCE?


相关:When should I use _mm_sfence _mm_lfence and _mm_mfence(当用 C++ 而不是 asm 编写时)。

请注意,像 _mm_sfence 这样的 C++ 内部函数也会阻止编译时内存排序。这通常是必要的,即使 asm 指令本身不是必需的,因为 C++ 编译时重新排序是基于 C++ 非常弱的内存模型,而不是适用于编译器生成的 asm 的强 x86 内存模型。

所以 _mm_sfence 可能会使您的代码正常工作,但除非您使用的是 NT 商店,否则它就太过分了。一个更有效的选项是 std::atomic_thread_fence(std::memory_order_release)(它变成零指令,只是一个编译器障碍。)参见 http://preshing.com/20120625/memory-ordering-at-compile-time/