首页 > 解决方案 > 非读和非写指令在 x86 中重新排序是否重要?

问题描述

mfence 文档说明了以下内容:

对在 MFENCE 指令之前发出的所有从内存加载和存储到内存指令执行序列化操作。这种序列化操作保证了在程序顺序中位于 MFENCE 指令之前的每个加载和存储指令在 MFENCE 指令之后的任何加载或存储指令之前变得全局可见。

据我所知,x86 中没有栅栏指令可以防止非读和非写指令的重新排序。

现在如果我的程序只有一个线程,即使指令被重新排序,它仍然看起来好像指令是按顺序执行的。

但是如果我的程序有多个线程,并且在其中一个线程中对非读和非写指令进行了重新排序,其他线程是否会注意到这种重新排序(我假设答案是否,否则会有一个围栏指令停止非读和非写指令重新排序,或者我错过了什么)?

标签: multithreadingassemblyx86memory-barriers

解决方案


其他线程会注意到这种重新排序吗

不,除了性能(计时或使用硬件性能计数器直接测量)。或微架构侧通道(例如与超线程/SMT 共享物理内核的逻辑内核的 ALU 端口压力):一个线程可以定时自己了解其他硬件线程正在执行的内容。

线程观察彼此的唯一“正常”方式是加载其他线程存储的数据。

甚至加载顺序也只能间接可见(通过它对其他线程决定稍后存储的内容的影响)。


据我所知,x86 中没有栅栏指令可以防止非读和非写指令的重新排序。

在 Intel CPU(但不是 AMD)上,lfence这样做. 英特尔的手册是这么说的,这不仅仅是一个实现细节。对于未来的微架构,它实际上是有保证的。

Intel 的LFENCE 指令集参考手册入口

LFENCE 直到所有先前的指令都在本地完成后才执行,并且在 LFENCE 完成之前没有后面的指令开始执行。

(在本地完成 = 从无序核心退出,即离开 ROB)。

lfence作为实际的负载屏障并不是特别有用,因为 x86 不允许来自 WB 内存(仅来自 WC)的弱排序负载。(甚至不能movntdqaprefetchnta不能从正常的 WB 内存创建弱排序的负载。)所以不像sfence,lfence基本上不需要内存排序,只是因为它的特殊效果,比如 lfence; rdtsc. 或者对于 Spectre 缓解措施,阻止经过它的推测执行。


但作为一个实现细节,在至少包括 Skylake 在内的 Intel CPU 上,mfence 乱序执行的障碍。 请参阅加载和存储是唯一被重新排序的指令吗?为此,还有更多相关的东西。


推荐阅读