psa*_*ora 5 x86 multithreading multicore cpu-architecture memory-barriers
我正在阅读英特尔指令集指南 64-ia-32指南 以了解内存栅栏。我的问题是,以 SFENCE 为例,为了确保所有存储操作都是全局可见的,多核 CPU 是否会停放所有线程甚至在其他内核上运行,直到实现缓存一致性?
障碍不会让其他线程/核心等待。它们使当前线程中的一些操作等待,这取决于它是什么类型的屏障。非内存指令的乱序执行不一定会被阻止。
Barriers 甚至不会更快地使您的加载/存储对其他线程可见;CPU 内核已经尽快将存储缓冲区中的存储提交(退役)到 L1d 缓存。(在遵循所有必要的 MESI 一致性规则之后,即使没有障碍,x86 的强内存模型也只允许存储按程序顺序提交)。
障碍不一定对指令执行进行排序,它们对全局可见性进行排序,即从存储缓冲区的远端出来的内容。
mfence(或lock编辑操作类似lock add或xchg [mem], reg)使所有后来加载/存储在当前线程等待,直到所有先前的加载和存储完成和全局可见(即存储缓冲区被刷新)。
mfence在 Skylake 上的实现方式是暂停整个核心,直到存储缓冲区耗尽。请参阅我对加载和存储是否是唯一重新排序的指令的回答
?详情;这种额外的减速是为了修复一个错误。但是locked 操作和xchgSkylake 上的不一样;它们是完整的内存屏障,但它们仍然允许乱序执行imul eax, edx,因此我们有证据表明它们不会停止整个内核。
对于超线程,我认为这种停顿发生在每个逻辑线程,而不是整个核心。
但请注意,mfence手动输入并没有说明停止内核的任何内容,因此未来的 x86 实现可以自由地使其更高效(如 a lock or dword [rsp], 0),并且仅防止以后加载读取 L1d 缓存,而不会阻止以后的非加载指令。
sfence只有在有任何 NT 商店在飞行时才会做任何事情。它根本不排序加载,因此它不必停止执行后面的指令。请参阅为什么(或不是?)SFENCE + LFENCE 等价于 MFENCE?.
它只是在存储缓冲区中放置一个屏障,阻止 NT 存储跨它重新排序,并在sfence屏障离开存储缓冲区之前强制较早的 NT 存储全局可见。(即写入组合缓冲区必须刷新)。但在到达存储缓冲区的末尾之前,它可能已经从核心的乱序执行部分(ROB 或 ReOrder 缓冲区)退出。)
lfence因为内存屏障几乎是无用的:它只会阻止movntdqa来自 WC 内存的加载在以后的加载/存储中重新排序。你几乎不需要那个。
其实际用例lfence主要涉及其英特尔(但不是 AMD)的行为,即在其自身退役之前不允许执行后续指令。(因此lfence; rdtsc在英特尔 CPU 上,您可以避免rdtsc过早读取时钟,作为更便宜的替代品cpuid; rdtsc)
最近另一个重要的用例lfence是阻止推测执行(例如在条件或间接分支之前),以缓解 Spectre。这完全是基于其 Intel 保证的部分序列化的副作用,与其 LoadLoad + LoadStore 屏障效果无关。
lfence也没有必须等待存储缓冲器漏,才可以从ROB退役,所以没有LFENCE + SFENCE的组合是强如MFENCE。 为什么(或不是?)SFENCE + LFENCE 等价于 MFENCE?
相关:我什么时候应该使用 _mm_sfence _mm_lfence 和 _mm_mfence(用 C++ 而不是 asm 编写时)。
请注意,C++ 内在函数_mm_sfence也阻止了编译时内存排序。即使 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/。
| 归档时间: |
|
| 查看次数: |
845 次 |
| 最近记录: |