Assembly 英特尔内存型号是否使SFENCE和LFENCE冗余?

Assembly 英特尔内存型号是否使SFENCE和LFENCE冗余?,assembly,optimization,x86,atomic,memory-barriers,Assembly,Optimization,X86,Atomic,Memory Barriers,英特尔内存型号保证: 门店不会与其他门店一起重新订购 货物不会与其他货物一起重新订购 我曾看到有人声称,由于英特尔内存型号的原因,在x86-64上SFENCE是冗余的,但从来没有看到LFENCE。上述内存模型规则是否使指令冗余?对,LFENCE和SFENCE在普通代码中没有用处,因为x86的常规存储的获取/释放语义使它们冗余,除非您使用其他特殊指令或内存类型 对于正常的无锁代码来说,唯一重要的屏障是来自locked指令的完整屏障(包括StoreLoad)或慢速MFENCE。对于顺序一致性存

英特尔内存型号保证:

  • 门店不会与其他门店一起重新订购
  • 货物不会与其他货物一起重新订购


我曾看到有人声称,由于英特尔内存型号的原因,在x86-64上SFENCE是冗余的,但从来没有看到LFENCE。上述内存模型规则是否使指令冗余?

对,LFENCE和SFENCE在普通代码中没有用处,因为x86的常规存储的获取/释放语义使它们冗余,除非您使用其他特殊指令或内存类型

对于正常的无锁代码来说,唯一重要的屏障是来自
lock
ed指令的完整屏障(包括StoreLoad)或慢速MFENCE。对于顺序一致性存储,首选
xchg
,而不是
mov
+
mfence
。因为它更快

(是的,即使使用NT指令,只要没有WC内存。)


杰夫·普雷辛(Jeff Preshing)的文章描述了巴托斯(Bartosz)在博文中谈到的同一个案例,其中你需要一个像MFENCE这样的存储负载屏障。只有MFENCE可以;不能用SFENCE+LFENCE构造MFENCE。()

如果你在阅读了你发布的链接后有疑问,请阅读杰夫·普雷辛的其他博客文章。他们使我对这个问题有了很好的理解虽然我认为我在Doug Lea的页面上发现了关于SFENCE/LFENCE的小道消息,但通常情况下这是一个不可操作的消息。杰夫的帖子没有考虑NT的加载/存储。


相关的:我的回答和@ BeoO绳le的回答都不错。我在很久以前就写了这个答案,所以部分答案是几年前我的经验不足。我的答案考虑了C++的内部和C++编译时的内存顺序,这与x86 ASM运行时内存排序完全不同。但是你仍然不想<代码>mm_lfence())


SFENCE仅在使用
movnt
时相关(非时态)流式存储
,或使用类型设置为非正常回写的内存区域。或使用
clflushopt
,这有点像弱顺序存储。NT存储绕过缓存,并且是弱顺序存储,而不是NT存储、WC(写合并)内存和ERMSB字符串操作(见下文))

LFENCE仅适用于具有弱有序负载的内存排序,这种情况非常罕见。(或可在NT存储之前使用常规加载进行加载存储订购?)

来自WB内存的NT加载()是,甚至在不忽略NT提示的假设未来CPU上;在x86上执行弱顺序加载的唯一方法是从弱顺序内存(WC)读取,然后我认为只有使用
movntdqa
。在“正常”程序中,这并不是偶然发生的,因此,如果您使用mmap视频RAM或其他方式,您只需担心这一点

lfence的主要使用案例根本不是内存排序,而是用于序列化指令执行,例如用于Spectre缓解,或使用RDTSC。有关该问题,请参阅和“链接问题”侧栏。)


C++中的内存排序及其映射到x86的ASM 几周前,我对此感到好奇,并对最近的一个问题发布了一个相当详细的答案: . 我包括了很多关于C++内存模型和硬件内存模型的链接。

如果你用C++编写,使用<代码> STD::原子是一个极好的方法来告诉编译器你有什么排序要求,所以它不会在编译时重新排序你的内存操作。您可以而且应该在适当的情况下使用较弱的release或acquire语义,而不是默认的顺序一致性,这样编译器就不必在x86上发出任何屏障指令。它只需要保持行动在源头的秩序


在弱有序体系结构(如ARM、PPC或带有movnt的x86)上,在写入缓冲区和设置标志以指示数据就绪之间需要一条StoreStore barrier指令。此外,读取器需要在检查标志和读取缓冲区之间执行加载屏障指令

不算movnt,x86已经在每个负载之间设置了负载屏障,并且在每个存储之间设置了StoreStore屏障。(还保证LoadStore订购)
MFENCE
是所有4种屏障,包括StoreLoad,这是x86默认情况下唯一不使用的屏障。MFENCE确保加载不会使用其他线程看到您的存储之前的旧预取值,也不会使用它们自己的存储。(同时也是NT商店订购和负载订购的障碍。)

有趣的事实:x86
lock
-前缀指令也是完全的内存屏障。它们可以在旧的32位代码中作为MFENCE的替代品,这些代码可能在不支持MFENCE的CPU上运行
lock add[esp],否则0
是不可操作的,并且在内存上执行读/修改/写循环,该内存在一级缓存中很可能很热,并且已经处于MESI一致性协议的M状态

SFENCE是一个商店障碍。在NT存储之后,为后续存储创建发布语义非常有用

LFENCE几乎总是与内存屏障无关,因为只有弱有序的负载

装载物和货物。(
loadNT/LFENCE/storeNT
防止存储在加载之前变得全局可见。我认为,如果加载地址是长依赖链的结果,或者是缓存中丢失的另一个加载的结果,在实践中可能会发生这种情况。)


ERMSB字符串操作 有趣的事实#2(感谢
@EOF
):中的存储顺序很弱(但不是缓存绕过)。ERMSB基于常规的快速字符串操作(自PPro以来一直存在的
rep stos/movsb
的微代码实现的广泛存储)

英特尔在第7.3.9节中记录了ERMSB存储“可能看起来执行不正常”的事实