通常在互联网上我发现LFENCE
在处理器x86中没有任何意义,即它什么都不做,所以相反MFENCE
我们绝对无法使用SFENCE
,因为MFENCE
} = SFENCE
+ LFENCE
= SFENCE
+ NOP
= SFENCE
。
但如果LFENCE
没有意义,那么为什么我们有四种方法可以在x86 / x86_64中实现顺序一致性:
LOAD
(没有围栏)和STORE
+ MFENCE
LOAD
(没有围栏)和LOCK XCHG
MFENCE
+ LOAD
和STORE
(没有围栏)LOCK XADD
(0)和STORE
(没有围栏)从这里采取:http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
除了Herb Sutter在第34页底部的表演:https://skydrive.live.com/view.aspx?resid=4E86B0CF20EF15AD!24884&app=WordPdf&wdo=2&authkey=!AMtj_EflYn2507c
如果LFENCE
没有做任何事情,那么方法(3)将具有以下含义:SFENCE + LOAD and STORE (without fence)
,但在SFENCE
之前做LOAD
没有意义。即如果LFENCE
什么都不做,方法(3)没有意义。
在x86 / x86_64处理器中是否有任何意义上的指令LFENCE
?
解答:
1。 LFENCE
在下面接受的答案中描述的案例中需要。
2. 方法(3)不应独立查看,而应与之前的命令结合使用。例如,方法(3):
MFENCE
MOV reg, [addr1] // LOAD-1
MOV [addr2], reg //STORE-1
MFENCE
MOV reg, [addr1] // LOAD-2
MOV [addr2], reg //STORE-2
我们可以按如下方式重写方法(3)的代码:
SFENCE
MOV reg, [addr1] // LOAD-1
MOV [addr2], reg //STORE-1
SFENCE
MOV reg, [addr1] // LOAD-2
MOV [addr2], reg //STORE-2
此处SFENCE
有助于防止重新排序STORE-1和LOAD-2。为此,STORE-1命令SFENCE
刷新Store-Buffer。
答案 0 :(得分:31)
底线(TL; DR):LFENCE
对于内存排序确实似乎毫无用处,但它并没有使SFENCE
成为MFENCE
的替代品。 "算术"问题中的逻辑不适用。
以下摘自Intel's Software Developers Manual, volume 3,第8.2.2节(2014年9月版325384-052US),与我在another answer中使用的相同
- 读取不会与其他读取重新排序。
- 写入不会与较旧的读取重新排序。
- 写入内存不会与其他写入重新排序,但以下情况除外:
- 使用CLFLUSH指令执行写入;
- 使用非时间移动指令(MOVNTI,MOVNTQ,MOVNTDQ,MOVNTPS和MOVNTPD)执行的流存储(写入);和
- 字符串操作(参见第8.2.4.1节)。
- 可以使用较旧的写入对不同位置进行重新排序,但不能将较旧的写入重新排序到同一位置。
- 读取或写入不能使用I / O指令,锁定指令或序列化指令重新排序。
- 读取不能通过早期的LFENCE和MFENCE指令。
- 写入不能通过早期的LFENCE,SFENCE和MFENCE指令。
- LFENCE指令无法通过早期读取。
- SFENCE指令无法通过更早的写入。
- MFENCE指令无法通过早期的读取或写入。
从这里开始:
MFENCE
是所有内存类型上所有操作的完整内存栅栏,无论是否为非时态。 SFENCE
仅阻止写入的重新排序(在其他术语中,它是StoreStore屏障),并且仅与非临时存储和列为例外的其他指令一起使用。LFENCE
可防止读取与后续读取和写入重新排序(即它结合了LoadLoad和LoadStore障碍)。但是,前两个项目符号表示LoadLoad和LoadStore障碍始终存在,没有例外。因此,仅LFENCE
对内存排序无用。为了支持最后一项声明,我查看了所有3卷英特尔手册中提到LFENCE
的所有地方,并且没有发现任何会说内存需要LFENCE
的地方一致性。即使是MOVNTDQA
- 迄今为止唯一的非时态加载指令 - 提及MFENCE
但未提及LFENCE
。
更新:查看Why is (or isn't?) SFENCE + LFENCE equivalent to MFENCE?上的答案,了解下方猜测的正确答案
MFENCE
是否等同于"总和"其他两个围栏与否是一个棘手的问题。乍一看,在三个fence指令中,只有MFENCE
提供StoreLoad屏障,即防止对先前写入的读取进行重新排序。然而,正确的答案需要知道的不仅仅是上述规则;也就是说,所有围栏指令相对于彼此进行排序是很重要的。这使得SFENCE LFENCE
序列比仅仅单个效果的联合更强大:此序列还会阻止StoreLoad重新排序(因为加载无法通过LFENCE
,而SFENCE
无法通过LFENCE SFENCE
,无法通过商店) ,因此构成一个完整的记忆围栏(但也见下面的注释(*))。但请注意,此处的顺序很重要,MFENCE ~ SFENCE LFENCE
序列不具有相同的协同效应。
但是,虽然可以说LFENCE ~ NOP
和MFENCE ~ SFENCE
,但这并不意味着SFENCE
。我故意使用等价(〜)而不是等于(=)来强调算术规则在这里不适用。 LFENCE
后跟LFENCE
的相互影响有所不同;即使负载没有相互重新排序,也需要SFENCE
来防止MFENCE
重新排序负载。
(*)说CLFLUSH
强于其他两个围栏的组合仍然可能是正确的。特别是,英特尔手册第2卷中的CLFLUSH
指令说明" MFENCE
仅按CLFLUSH
指令排序。不保证可以通过任何其他屏蔽或序列化指令或其他clflush
指令进行排序。"
(更新,mfence
现在被定义为强排序(就像普通商店一样,所以如果你想稍后阻止加载,你只需要clflushopt
),但{ {1}}排序很弱,但sfence
可以围隔。)
答案 1 :(得分:7)
考虑以下场景 - 这是关键情况,推测性加载执行在理论上可能会损害顺序一致性
最初[x] = [y] = 0
CPU0: CPU1:
store [x]<--1 store [y]<--1
load r1<--[y] load r2<--[x]
由于x86允许将早期存储的负载重新排序到不同的地址,因此两个负载都可以返回0。在每个商店之后单独添加一个lfence不会阻止这种情况,因为它们只能防止在同一个上下文中重新排序,但是由于商店在退役后被调度,你可以在执行和观察存储之前同时提交lfences和两个load。
另一方面, mfence 会强制商店执行,然后才允许执行加载,因此您将在至少一个上下文中看到更新的数据。
至于 sfences - 正如评论中指出的那样,从理论上来说,它不足以阻止负载重新排序在它之上,因此它仍然可能会读取陈旧数据。虽然在内存官方排序规则适用的情况下也是如此,但我相信x86 uarch的当前实现会使它稍微强一些(我猜想未来不会这么做)。根据{{3}}:
由于强大的x86排序模型,负载缓冲区被窥探 通过一致性交通。远程存储必须使所有其他副本无效 缓存行。如果加载读取缓存行,然后 由远程存储无效,必须取消加载,因为它 可能会读取无效数据。 x86内存模型不需要 窥探商店缓冲区。
因此,机器中尚未提交的任何负载都应该由来自其他核心的存储进行侦听,从而使提交点的负载的有效观察时间成为可能,而不是执行点(这确实是乱序的,可能早已执行过)。提交是按顺序完成的,因此应该在之前的指令之后观察负载 - 正如我在评论中所说的那样使得lfences几乎无用,因为没有它们可以保持一致性。 这主要是猜测,试图解释在x86中lfences毫无意义的常见概念 - 我不完全确定它起源于何处,以及是否还有其他考虑因素 - 对任何专家来说都很乐意批准/挑战这一理论。 / p>
以上所有内容仅适用于当然的WB mem类型
答案 2 :(得分:-1)
当然它很有意义!
来自英特尔数据表的 LFENCE
:
对所有内存加载指令执行序列化操作 在LFENCE指令之前发布的。这序列化 operation保证每个加载指令都在之前 程序顺序LFENCE指令在任何之前是全局可见的 LFENCE指令后面的加载指令是全局的 可见。
如果正确对齐,像MOV
这样的内存写入指令是原子的。但是这条指令通常在CPU缓存中执行,此时所有其他线程都不会全局可见,因为必须首先执行内存LFENCE/SFENCE or MFENCE
。
典型案例:
如果线程编写器使用内存对齐MOV
之类的写入指令解锁内存区域,那么没有使用LOCK
前缀指令,而是执行的缓存行MOV
可见在很短的时间内所有其他线程。 LFENCE
确保给线程阅读器,线程编写器的所有其他缓存行都是全局访问!