Assembly 在x86:lock+;addl还是xchgl?

Assembly 在x86:lock+;addl还是xchgl?,assembly,x86,memory-barriers,Assembly,X86,Memory Barriers,Linux内核使用锁;添加$0,0(%%esp)作为写屏障,而RE2库使用xchgl(%0),%0作为写屏障。有什么区别,哪一个更好 x86是否也需要读屏障说明?RE2将其读屏障函数定义为x86上的no op,而Linux将其定义为lfence或no op,具体取决于SSE2是否可用。什么时候需要锁?锁的重要部分;addl和xchgl是lock前缀。对于xchgl,它是隐式的。两者之间真的没有区别。我会看看他们是如何组装的,并选择一个更短的(以字节为单位),因为对于x86上的等效操作,这通常更快

Linux内核使用
锁;添加$0,0(%%esp)
作为写屏障,而RE2库使用
xchgl(%0),%0
作为写屏障。有什么区别,哪一个更好


x86是否也需要读屏障说明?RE2将其读屏障函数定义为x86上的no op,而Linux将其定义为
lfence
或no op,具体取决于SSE2是否可用。什么时候需要锁?

锁的重要部分;addl和
xchgl
lock
前缀。对于
xchgl
,它是隐式的。两者之间真的没有区别。我会看看他们是如何组装的,并选择一个更短的(以字节为单位),因为对于x86上的等效操作,这通常更快(因此有一些技巧,比如
xorl-eax,eax

SSE2的存在可能只是实际情况的一个代理,而实际情况最终是
cpuid
的一个函数。结果可能是,SSE2意味着存在
lfence
,并且在引导时检查/缓存了SSE2的可用性
lfence
在可用时是必需的。

如果我们在(%%esp)地址测试lock变量的0状态,“lock;addl$0,0(%%esp)”会更快。因为我们将0值添加到lock变量,如果地址(%%esp)处变量的锁值为0,则零标志设置为1


lfence来自英特尔数据表:

在上执行序列化操作 所有从内存加载的指令 都是在选举前发出的 指示这个连载 操作保证了每一个负载 在程序中位于前面的指令 命令LFENCE指令为 在任何加载之前全局可见 遵循规则的指令 指令是全局可见的

编者注:或
锁定
ed操作是唯一有用的顺序一致性围栏(在存储之后)
lfence
不会阻止存储缓冲区对存储加载进行重新排序。)


例如:内存写入指令,如'mov'是原子的(它们不需要锁前缀),如果它们正确对齐的话。但该指令通常在CPU缓存中执行,此时对于所有其他线程都不会全局可见,因为必须首先执行内存限制,以使该线程等待,直到其他线程可以看到以前的存储



因此,这两条指令之间的主要区别在于xchgl指令不会对条件标志产生任何影响。当然,我们可以使用lock cmpxchg指令测试锁变量状态,但这仍然比使用lock add$0指令更复杂。

引用IA32手册(第3A卷,第8.2章:内存排序):

在定义为可回写缓存的内存区域的单处理器系统中,内存排序模型遵循以下原则[…]

  • 读取不会与其他读取一起重新排序
  • 写入不会与旧的读取一起重新排序
  • 对内存的写入不会与其他写入一起重新排序,但
    • 使用
      CLFLUSH
      指令执行的写入
    • 使用非临时移动指令执行的流存储(写入)([此处的指令列表])
    • 串操作(见第8.2.4.1节)
  • 读操作可以通过较旧的写入操作重新排序到不同的位置,但不能通过较旧的写入操作重新排序到同一位置
  • 不能使用I/O指令、锁定指令或序列化指令对读取或写入进行重新排序
  • 读取无法传递
    LFENCE
    MFENCE
    指令
  • 写入无法传递
    SFENCE
    MFENCE
    指令
注:上面的“单处理器系统中”有点误导。相同的规则分别适用于每个(逻辑)处理器;本手册接着描述了多个处理器之间的其他排序规则。关于这个问题的唯一一点是

  • 锁定指令有一个总订单
简言之,只要您在写回内存(只要您不是驱动程序或图形程序员,您就会看到所有内存),大多数x86指令几乎是顺序一致的-x86 CPU可以执行的唯一重新排序是在写入之前重新排序稍后(独立)读取以执行。写屏障的主要特点是它们有一个
lock
前缀(隐式或显式),它禁止所有重新排序,并确保多处理器系统中的所有处理器都以相同的顺序看到操作


此外,在写回内存中,读取永远不会重新排序,因此不需要读屏障。最近的x86处理器对于流存储和写入组合内存(通常用于映射图形内存)具有较弱的内存一致性模型。这就是各种
fence
指令发挥作用的地方;它们对于任何其他内存类型都不是必需的,但是Linux内核中的一些驱动程序确实处理写组合内存,所以它们只是以这种方式定义了它们的读屏障。IA-32手册第3A卷第11.3.1节列出了每种内存类型的订购型号。短版本:直写、回写和写保护允许推测性读取(遵循上面详述的规则),不可缓存和强不可缓存内存具有强排序保证(无处理器重新排序,读取/写入立即执行,用于MMIO),写组合内存具有弱排序(即需要围栏的宽松排序规则)。

除了其他答案之外,HotSpot开发者发现,在某些处理器上,零偏移量的
锁定;添加$0,0(%%esp)
可能不是最优的
movl   $1, a             a = 1;    Atomic for aligned a
# barrier needed here
movl   b, %eax           tmp = b;  Atomic for aligned b