Ale*_*lex 40 x86 assembly x86-64 atomic memory-barriers
通常在互联网上我发现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?status = 4E86B0CF20EF15AD!24884&app = WordPdf&wdo = 2&authkey =!AMtj_EflYn2507c
如果LFENCE
没有做任何事情,那么方法(3)将具有以下含义:SFENCE + LOAD and STORE (without fence)
但是SFENCE
之前没有任何意义LOAD
.即如果LFENCE
什么都不做,方法(3)没有意义.
它LFENCE
在处理器x86/x86_64中是否有任何意义上的指令?
回答:
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
Run Code Online (Sandbox Code Playgroud)
我们可以重写方法代码(3)如下:
SFENCE
MOV reg, [addr1] // LOAD-1
MOV [addr2], reg //STORE-1
SFENCE
MOV reg, [addr1] // LOAD-2
MOV [addr2], reg //STORE-2
Run Code Online (Sandbox Code Playgroud)
这SFENCE
有助于防止重新排序STORE-1和LOAD-2.为此,STORE-1命令SFENCE
刷新Store-Buffer.
Ale*_*nov 31
底线(TL; DR):LFENCE
单独确实对内存排序似乎毫无用处,但它SFENCE
不能替代MFENCE
.问题中的"算术"逻辑不适用.
以下是英特尔软件开发人员手册第3卷 8.2.2节(2014年9月版325384-052US)的摘录,与我在另一个答案中使用的相同
- 读取不会与其他读取重新排序.
- 写入不会与较旧的读取重新排序.
- 写入内存不会与其他写入重新排序,但以下情况除外:
- 使用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
单独对于存储器排序是无用的.为了支持最后一个声明,我查看LFENCE
了所有3卷英特尔手册中提到的所有地方,并且没有发现LFENCE
内存一致性所需的内容.甚至MOVNTDQA
- 到目前为止唯一的非临时负载指令 - 提及MFENCE
但不是LFENCE
.
更新:看看答案为什么(或不是?)SFENCE + LFENCE相当于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
它的排序很弱,但是可以被隔离sfence
.)
请考虑以下情形 - 这是关键情况,推测性负载执行在理论上可能会损害顺序一致性
最初[x] = [y] = 0
CPU0: CPU1:
store [x]<--1 store [y]<--1
load r1<--[y] load r2<--[x]
Run Code Online (Sandbox Code Playgroud)
由于x86允许使用早期存储将负载重新排序到不同的地址,因此两个负载都可以返回0.在每个商店之后单独添加一个lfence不会阻止这种情况,因为它们只能防止在同一个上下文中重新排序,但是由于商店在退役后被调度,你可以在执行和观察存储之前同时提交lfences和两个load.
一个MFENCE,另一方面将迫使存储执行,然后才允许执行的负荷,所以你会看到至少一个上下文更新的数据.
至于sfences - 正如评论中所指出的那样,理论上它不足以阻止负载重新排序在它之上,所以它仍然可能读取陈旧数据.虽然在内存官方排序规则适用的情况下也是如此,但我相信x86 uarch的当前实现会使它稍微强一些(我猜想未来不会这么做).根据这个描述:
由于强大的x86排序模型,加载缓冲区被一致性流量窥探.远程存储必须使高速缓存行的所有其他副本无效.如果加载读取缓存行,然后由远程存储使其无效,则必须取消加载,因为它可能会读取无效数据.x86内存模型不需要窥探存储缓冲区.
因此,机器中尚未提交的任何负载都应该由来自其他核心的存储器窥探,从而使得在提交点处的负载的有效观察时间,而不是执行点(实际上是无序的并且可能已经执行)更早).提交是按顺序完成的,因此应该在之前的指令之后观察负载 - 正如我在评论中所说的那样使得lfences几乎无用,因为没有它们,一致性可以保持相同的方式.这主要是猜测,试图解释在x86中lfences毫无意义的常见概念 - 我不完全确定它起源于何处以及是否还有其他考虑因素 - 对任何专家来说都很乐意批准/挑战这一理论.
以上所有内容仅适用于WB mem类型