在x86/x86_64处理器上使用LFENCE指令是否有意义?

Ale*_*lex 40 x86 assembly x86-64 atomic memory-barriers

通常在互联网上我发现LFENCE在处理器x86中没有任何意义,即它什么都不做,所以相反MFENCE我们可以绝对无痛地使用SFENCE,因为MFENCE= SFENCE+ LFENCE= SFENCE+ NOP= SFENCE.

但是如果LFENCE没有意义,那么为什么我们有四种方法在x86/x86_64中建立顺序一致性:

  1. LOAD(没有围栏)和STORE+MFENCE
  2. LOAD (没有围栏)和 LOCK XCHG
  3. MFENCE+ LOADSTORE(没有围栏)
  4. 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.)


Lee*_*eor 7

请考虑以下情形 - 这是关键情况,推测性负载执行在理论上可能会损害顺序一致性

最初[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类型

  • SFENCE不能用于修复此示例,因为负载可以跨越SFENCE进行迁移.该示例需要MFENCE来修复. (2认同)