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

Assembly 在x86/x86_64处理器上使用LFENCE指令有意义吗?,assembly,x86,x86-64,atomic,memory-barriers,Assembly,X86,X86 64,Atomic,Memory Barriers,在互联网上,我经常发现,LFENCE在x86处理器上毫无意义,也就是说它什么都不做,所以我们可以毫无痛苦地使用SFENCE,因为MFENCE=SFENCE+LFENCE=SFENCENOP=SFENCE 但是如果LFENCE没有意义,那么为什么我们有四种方法来实现x86/x86\u 64中的顺序一致性: 加载(无围栏)和存储+MFENCE 加载(无围栏)和锁定XCHG MFENCE+加载和存储(无围栏) 锁XADD(0)和存储(无围栏) 摘自这里: 以及Herb Sutter在第34页底部的表演

在互联网上,我经常发现,
LFENCE
在x86处理器上毫无意义,也就是说它什么都不做,所以我们可以毫无痛苦地使用
SFENCE
,因为
MFENCE
=
SFENCE
+
LFENCE
=
SFENCE
NOP=
SFENCE

但是如果
LFENCE
没有意义,那么为什么我们有四种方法来实现x86/x86\u 64中的顺序一致性:

  • 加载
    (无围栏)和
    存储
    +
    MFENCE
  • 加载
    (无围栏)和
    锁定XCHG
  • MFENCE
    +
    加载
    存储
    (无围栏)
  • 锁XADD
    (0)和
    存储
    (无围栏)
  • 摘自这里:

    以及Herb Sutter在第34页底部的表演:

    如果
    LFENCE
    没有做任何事情,那么方法(3)将具有以下含义:
    SFENCE+加载和存储(无围栏)
    ,但是在
    加载之前执行
    SFENCE
    没有意义。即如果
    LFENCE
    什么都不做,那么方法(3)就没有意义

    在处理器x86/x86\u 64中,它对指令
    LFENCE
    有意义吗

    回答:

    1.
    LFENCE
    在以下公认答案中所述的情况下是必需的

    2.进近(3)不应单独查看,而应与前面的命令结合使用。例如,方法(3):

    我们可以将方法代码(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进行重新排序。为此,after STORE-1命令
    SFENCE
    刷新存储缓冲区。

    考虑以下场景-这是推测性负载执行理论上会损害顺序一致性的关键情况

    最初[x]=[y]=0

    CPU0:                              CPU1: 
    store [x]<--1                      store [y]<--1
    load  r1<--[y]                     load r2<--[x]
    
    CPU0:CPU1:
    存储[x]底线(TL;DR):
    LFENCE
    单独对内存排序似乎毫无用处,但它并不能使
    SFENCE
    取代
    MFENCE
    。问题中的“算术”逻辑不适用


    以下是第8.2.2节(2014年9月的325384-052US版)的摘录,与我在

    • 读取不会与其他读取一起重新排序
    • 写入不会与旧的读取一起重新排序
    • 对内存的写入不会与其他写入一起重新排序,但以下情况除外:
      • 使用CLFLUSH指令执行的写操作
      • 使用非时态移动指令(MOVTI、MOVTQ、MOVTDQ、MOVTPS和MOVTPD)执行的流式存储(写入);及
      • 串操作(见第8.2.4.1节)
    • 读操作可以通过较旧的写入操作重新排序到不同的位置,但不能通过较旧的写入操作重新排序到同一位置
    • 不能使用I/O指令、锁定指令或序列化指令对读取或写入进行重新排序
    • 读取无法传递早期LFENCE和MFENCE指令
    • 写入无法传递早期的LFENCE、SFENCE和MFENCE指令
    • LFENCE指令无法通过早期读取
    • SFENCE指令无法传递早期写入
    • MFENCE指令无法通过早期读取或写入
    由此可知:

    • MFENCE
      是一个完整的内存围栏,用于所有内存类型上的所有操作,无论是非时态的还是非时态的
    • SFENCE
      仅防止写入的重新排序(在其他术语中,这是一个存储障碍),并且仅与非时态存储和列为例外的其他指令一起使用
    • LFENCE
      防止读取和后续读取和写入的重新排序(即,它结合了Load和LoadStore屏障)。然而,前两项说明Load和LoadStore屏障始终存在,没有例外。因此,
      LFENCE
      单独对内存排序是无用的
    为了支持上一个说法,我查看了英特尔手册所有3卷中提到的
    LFENCE
    的所有地方,没有发现任何地方会说
    LFENCE
    是内存一致性所必需的。即使是迄今为止唯一的非时态加载指令
    MOVNTDQA
    ,也提到了
    MFENCE
    ,但没有提到
    LFENCE


    更新:有关下面猜测的正确答案,请参见上的答案

    MFENCE
    是否等同于其他两个栅栏的“总和”,这是一个棘手的问题。乍一看,在三个围栏指令中,只有
    MFENCE
    提供了存储负载屏障,即防止读取与先前写入的重新排序。然而,正确的答案需要知道的不仅仅是上述规则;也就是说,重要的是,所有的围栏指令都是按照彼此的顺序排列的。这使得
    SFENCE-LFENCE
    序列比单个效果的简单结合更强大:该序列还阻止了StoreLoad重新排序(因为加载无法通过
    LFENCE
    ,而后者无法通过
    SFENCE
    ,后者无法通过存储),因此构成了一个完整的内存围栏(但也请参见下面的注释(*))。但是请注意,这里的顺序很重要,
    LFENCE-SFENCE
    序列没有相同的协同效应

    然而,虽然可以说
    MFENCE~SFENCE-LFENCE
    LFENCE~NOP
    ,但这并不意味着
    MFENCE~SFENCE
    。我故意使用等价(~)和不相等(=)来强调算术规则在这里不适用。
    SFENCE
    LFENCE
    的相互作用产生了差异;即使负载之间没有重新排序,CPU0: CPU1: store [x]<--1 store [y]<--1 load r1<--[y] load r2<--[x]