首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在x86上,哪个写屏障更好: lock+addl还是xchgl?

在x86上,哪个写屏障更好: lock+addl还是xchgl?
EN

Stack Overflow用户
提问于 2010-11-20 20:15:26
回答 5查看 9K关注 0票数 28

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

x86是否也需要读屏障指令?RE2将其读屏障函数定义为x86上的无操作,而Linux根据SSE2是否可用将其定义为lfence或无操作。什么时候需要lfence

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2010-11-21 05:58:15

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

英特尔数据表中的lfence

对在LFENCE指令之前发出的所有从内存加载指令执行序列化操作。此序列化操作可确保在LFENCE指令之后的任何加载指令全局可见之前,程序顺序中位于LFENCE指令之前的每个加载指令都是全局可见的。

(编者按: lock**ed操作是保证顺序一致性的唯一有用的屏障(在存储之后)**。lfence不会阻止存储缓冲区对StoreLoad进行重新排序。)

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

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

票数 9
EN

Stack Overflow用户

发布于 2010-11-21 06:00:23

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

在单处理器系统中,对于被定义为可回写高速缓存的存储器区域,存储器排序模型遵循以下原则。

  • 读取不会与其他读取一起重新排序
  • 写入不会与较旧的读取重新排序
  • 对内存的写入不会与其他写入一起重新排序,使用非临时移动指令(此处为指令列表)执行的
    • 存储(写入)除外
    • 字符串操作(请参阅8.2.4.1)

一节

对不同位置的较旧写入可能会对

  • 读取进行重新排序,但对同一location.
  • Reads的较旧写入不会对其进行重新排序,或者无法使用I/O指令、锁定指令对写入进行重新排序,或者序列化instructions
  • Reads无法通过LFENCEMFENCE SFENCE无法通过MFENCE and

注意:上面的“在单处理器系统中”有点误导。相同的规则分别适用于每个(逻辑)处理器;然后手册继续描述多个处理器之间的其他排序规则。与这个问题有关的唯一一点是

  • 锁定指令有一个总顺序。

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

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

票数 11
EN

Stack Overflow用户

发布于 2018-10-21 06:32:21

lock addl $0, (%esp)mfence的替代品,而不是lfence

其用例是需要阻塞StoreLoad重新排序(x86的强大内存模型允许的唯一一种),但不需要对共享变量执行原子RMW操作。https://preshing.com/20120515/memory-reordering-caught-in-the-act/

例如,假设std::atomic<int> a,b对齐

代码语言:javascript
运行
复制
movl   $1, a             a = 1;    Atomic for aligned a
# barrier needed here
movl   b, %eax           tmp = b;  Atomic for aligned b

您可以选择:

使用

  • Do xchg,进行顺序一致性存储,例如mov $1, %eax / xchg %eax, a,因此您不需要单独的屏障;它是存储的一部分。我认为在大多数现代硬件上,这是最有效的选择;除了gcc之外,C++11编译器将xchg用于seq_cst存储,而
  • 则使用mfence作为屏障。(gcc将mov + mfence用于seq_cst商店),
  • 使用lock addl $0, (%esp)作为屏障。任何locked指令都是一个完全的障碍。Does lock xchg have the same behavior as mfence?

(或者移到其他位置,但是在L1d中堆栈几乎总是私有的和热门的,所以它是一个比较好的候选者。但是,这可能会使用堆栈底部的数据为某些内容创建一个依赖链。)

您只能通过将xchg折叠到存储中来将其用作屏障,因为它使用不依赖于旧值的值无条件地写入内存位置。

在可能的情况下,将xchg用于seq-cst存储可能是最好的,即使它也从共享位置读取。在最近的英特尔CPU (Are loads and stores the only instructions that gets reordered?)上,mfence的速度比预期的要慢,也会像lfence一样无序地阻止独立的非内存指令的执行。

即使在mfence可用的情况下,使用lock addl $0, (%esp)/(%rsp)而不是mfence也是值得的,但我还没有尝试过它的缺点。使用-64(%rsp)或其他东西可能不太可能延长数据对热门内容(本地或返回地址)的依赖,但这可能会使valgrind等工具感到不快。

lfence 永远不会对内存排序有用,除非你正在使用MOVNTDQA加载从视频内存(或其他WC弱排序区域)读取数据。

串行化乱序执行(而不是存储缓冲区)对于停止StoreLoad重新排序(这是x86的强大内存模型允许正常的WB (回写)内存区域的唯一一种)没有用。

lfence的真实用例是用于阻止rdtsc的无序执行,以便对非常短的代码块进行计时,或者通过条件分支或间接分支阻止推测来缓解频谱。

另请参阅When should I use _mm_sfence _mm_lfence and _mm_mfence (我的答案和@BeeOnRope的答案),了解更多关于为什么lfence没有用处,以及何时使用每个屏障指令的更多信息。(或者在我的例子中,当使用C++而不是asm进行编程时,使用C++内部函数)。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4232660

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档