x86 CPU有多少个内存屏障指令?

6
我发现x86 CPU具有以下内存屏障指令:mfencelfencesfence
是否只有这三个指令,还是还有其他指令?

3
以下应该是适当的内存屏障,但也有几个序列化指令。 - Matteo Italia
1
考虑给 https://stackoverflow.com/tags/memory-barriers/synonyms 点赞。 - Hadi Brais
1
@HadiBrais:还有一个[tag:memory-order],我也提议将其作为同义词。尽管排序可以在没有屏障的情况下发生,但这是一个非常晦涩的标签。它与高度相关,因此可能应该在SO上放在一起。 - Peter Cordes
2个回答

11

sfence(SSE1)和mfence / lfence(SSE2)是唯一以内存栅栏/障碍功能命名的指令。除非您使用NT存储器和/或WC存储器(以及从WC加载的NT加载),否则只需要mfence进行内存排序。

(请注意,lfence在Intel CPU上也是用于乱序执行的屏障,因此它可以序列化rdtsc,并且对于Spectre缓解非常有用以防止推测执行。在AMD上,有一个必须设置的MSR,否则lfence基本上是nop(4个周期吞吐量)。该MSR由Spectre缓解微代码更新引入,并且通常由更新的内核设置。)


locked指令,如lock add [mem], eax,也是完整的内存屏障。lock xchg与mfence具有相同的行为吗?(虽然可能不像mfence那样强大,无法对来自WC内存的NT加载进行排序:锁定指令是否在弱序访问之间提供屏障?)。xchg [mem],reg具有隐式的lock前缀,因此它也是一个屏障。

在Skylake上测试中, locked指令阻止了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-ssestd::atomic_thread_fence(std::memory_order_seq_cst);所做的

但无论如何,mfencelock指令在Intel上都不被定义为串行化,而与某些CPU的实现细节无关。


完整的序列化指令(例如cpuid)也是完整的内存屏障,可以清空存储缓冲区并刷新流水线。lock xchg与mfence行为相同吗?有来自英特尔手册的相关引用。
在英特尔处理器上,以下是体系结构序列化指令(来自:https://xem.github.io/minix86/manual/intel-x86-and-64-manual-vol3/o_fe12b1e2a880e0ce-273.html):
- 特权序列化指令 - INVD、INVEPT、INVLPG、INVVPID、LGDT、LIDT、LLDT、LTR、MOV到控制寄存器、MOV(到调试寄存器)、WBINVD和WRMSR。 异常情况:MOV CR8不是序列化的。对IA32_TSC_DEADLINE MSR(MSR索引6E0H)和X2APIC MSRs(MSR索引802H至83FH)的WRMSR不是序列化的。 - 非特权序列化指令 - CPUID、IRET1和RSM
在AMD处理器上,以下是体系结构序列化指令:
  • 特权序列化指令 — INVD、INVLPG、LGDT、LIDT、LLDT、LTR、MOV(控制寄存器)、MOV(调试寄存器)、WBINVD、WRMSR 和 SWAPGS。

  • 非特权序列化指令 — MFENCE、CPUID、IRET 和 RSM。

在英特尔处理器上,“[完全]序列化指令”的术语与 AMD 处理器上的意思完全相同,除了一个区别:仅通过 AMD 处理器上的 MFENCECLFLUSH(但不是 CLFLUSHOPT)进行缓存行刷新操作,并且该操作会与后续指令有序。


in / out(以及它们的字符串副本insouts)是完整的内存屏障,也是部分序列化的(类似于lfence)。文档显示它们推迟下一条指令的执行,直到I/O事务的“数据阶段”之后。


脚注:

(1) 根据BJ137(Sandy Bridge),HSD152(Haswell),BDM103(Broadwell):

问题:通过从嵌套任务返回导致任务切换的IRET指令不会序列化处理器(与软件开发人员手册第3卷中标题为“序列化指令”的部分相反)。

影响:依赖IRET在任务切换期间序列化属性的软件可能无法按预期运行。Intel未观察到此勘误对任何商业软件的操作产生影响。

解决方法:未识别出任何解决方法。如果需要序列化,则软件可以在IRET指令之前立即执行MFENCE指令。


“in”和“out”指令也像“mfence”一样是完整的内存屏障,并具有类似于“lfence”的序列化属性。 - Hadi Brais
@HadiBrais:哦,它们的串行化程度不如cpuidiret强吗?我并不是试图枚举所有串行化指令,只是列出不同的类别。这个HTML片段来自Intel的vol3手册(在谷歌搜索中出现),列出了完全串行化的指令,供以后参考:https://xem.github.io/minix86/manual/intel-x86-and-64-manual-vol3/o_fe12b1e2a880e0ce-273.html,你说得对,它没有将`in`或`out`列为串行化,但它们是由其他指令排序的。 - Peter Cordes

0

你说得对,x86 CPU 上唯一的三个内存屏障函数是:

LFENCE

SFENCE

MFENCE

1
这个答案并不够细致:虽然那些是唯一的“专用”栅栏指令,但其他带有“lock”前缀的指令也可以作为栅栏(它们作为上述栅栏中最强的mfence)。因此,为了获得内存屏障效果,您不需要使用上述任何指令。 - BeeOnRope

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接