mfence
、lfence
和sfence
。是否只有这三个指令,还是还有其他指令?
mfence
、lfence
和sfence
。sfence
(SSE1)和mfence
/ lfence
(SSE2)是唯一以内存栅栏/障碍功能命名的指令。除非您使用NT存储器和/或WC存储器(以及从WC加载的NT加载),否则只需要mfence
进行内存排序。
(请注意,lfence
在Intel CPU上也是用于乱序执行的屏障,因此它可以序列化rdtsc
,并且对于Spectre缓解非常有用以防止推测执行。在AMD上,有一个必须设置的MSR,否则lfence
基本上是nop
(4个周期吞吐量)。该MSR由Spectre缓解微代码更新引入,并且通常由更新的内核设置。)
lock
ed指令,如lock add [mem], eax
,也是完整的内存屏障。lock xchg与mfence具有相同的行为吗?(虽然可能不像mfence
那样强大,无法对来自WC内存的NT加载进行排序:锁定指令是否在弱序访问之间提供屏障?)。xchg [mem],reg
具有隐式的lock
前缀,因此它也是一个屏障。
在Skylake上测试中, lock
ed指令阻止了NT存储器重排序与常规存储器的重排序,代码如下https://godbolt.org/g/7Q9xgz。
xchg
似乎是一种很好的进行seq-cst存储的方法,特别是在像Skylake这样的英特尔硬件上,mfence
也会阻止纯ALU指令(如lfence
)的乱序执行:请参见本答案底部。
AMD还建议使用xchg
或其他锁定指令代替mfence
。(在AMD手册中,mfence
被记录为在AMD上进行序列化,因此它将始终具有阻止OoO exec的惩罚)。
对于没有SSE的32位目标的顺序一致性存储或全障碍,编译器通常使用lock or [esp], 0
或其他无操作锁定指令仅用于内存屏障效果。这就是g++7.3 -O3 -m32 -mno-sse
为std::atomic_thread_fence(std::memory_order_seq_cst);
所做的。
但无论如何,mfence
或lock
指令在Intel上都不被定义为串行化,而与某些CPU的实现细节无关。
特权序列化指令 — INVD、INVLPG、LGDT、LIDT、LLDT、LTR、MOV(控制寄存器)、MOV(调试寄存器)、WBINVD、WRMSR 和 SWAPGS。
非特权序列化指令 — MFENCE、CPUID、IRET 和 RSM。
在英特尔处理器上,“[完全]序列化指令”的术语与 AMD 处理器上的意思完全相同,除了一个区别:仅通过 AMD 处理器上的 MFENCE
对 CLFLUSH
(但不是 CLFLUSHOPT
)进行缓存行刷新操作,并且该操作会与后续指令有序。
in
/ out
(以及它们的字符串副本ins
和outs
)是完整的内存屏障,也是部分序列化的(类似于lfence
)。文档显示它们推迟下一条指令的执行,直到I/O事务的“数据阶段”之后。
脚注:
(1) 根据BJ137(Sandy Bridge),HSD152(Haswell),BDM103(Broadwell):
问题:通过从嵌套任务返回导致任务切换的IRET指令不会序列化处理器(与软件开发人员手册第3卷中标题为“序列化指令”的部分相反)。
影响:依赖IRET在任务切换期间序列化属性的软件可能无法按预期运行。Intel未观察到此勘误对任何商业软件的操作产生影响。
解决方法:未识别出任何解决方法。如果需要序列化,则软件可以在IRET指令之前立即执行MFENCE指令。
cpuid
或iret
强吗?我并不是试图枚举所有串行化指令,只是列出不同的类别。这个HTML片段来自Intel的vol3手册(在谷歌搜索中出现),列出了完全串行化的指令,供以后参考:https://xem.github.io/minix86/manual/intel-x86-and-64-manual-vol3/o_fe12b1e2a880e0ce-273.html,你说得对,它没有将`in`或`out`列为串行化,但它们是由其他指令排序的。 - Peter Cordes你说得对,x86 CPU 上唯一的三个内存屏障函数是:
LFENCE
SFENCE
MFENCE
mfence
)。因此,为了获得内存屏障效果,您不需要使用上述任何指令。 - BeeOnRope