英特尔内存模型保证:
http://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/
我已经看到声称由于英特尔内存模型,SFENCE在x86-64上是多余的,但从来没有LFENCE。上述内存模型规则是否使指令冗余?
答案 0 :(得分:16)
对,LFENCE和SFENCE在普通代码中没用,因为除非你使用其他特殊指令或内存类型,否则x86对常规存储的获取/释放语义会使它们变得多余。
对于普通无锁代码而言,唯一重要的围栏是来自lock
ed指令的完整屏障(包括StoreLoad),或者是缓慢的MFENCE。优先于xchg
+ mov
的顺序一致性商店mfence
。 Are loads and stores the only instructions that gets reordered?,因为它更快。
Does `xchg` encompass `mfence` assuming no non-temporal instructions?(是的,即使带有 NT指令,只要没有WC内存。)
Jeff Preshing的Memory Reordering Caught in the Act文章是一篇比较容易阅读的描述Bartosz的帖子谈到的相同案例,你需要像MFENCE这样的StoreLoad屏障。强>只有MFENCE会这样做;你不能用SFENCE + LFENCE构建MFENCE。 (Why is (or isn't?) SFENCE + LFENCE equivalent to MFENCE?)
如果您在阅读发布的链接后遇到问题,请阅读Jeff Preshing的其他博文。他们让我对这个主题有了很好的理解。 :)虽然我认为我发现关于SFENCE / LFENCE的一些消息通常在Doug Lea的页面中是无操作的。杰夫的帖子并没有考虑NT加载/存储。
相关:When should I use _mm_sfence _mm_lfence and _mm_mfence(我的回答和@ BeeOnRope的答案都很好。我在很久以前写过这个答案而不是那个答案,所以这个答案的部分内容显示我多年前缺乏经验。我的回答它考虑了C ++内在函数和C ++编译时内存顺序,这与x86 asm运行时内存排序完全不同。但是你仍然不想要_mm_lfence()
。)
SFENCE仅在使用movnt
(非临时)流媒体商店时使用,或者使用类型设置为正常回写以外的内存区域。或者使用clflushopt
,这有点像一个弱有序的商店。 NT商店绕过缓存以及虚拟订单。 x86's normal memory model is strongly ordered,除了NT存储,WC(写入组合)内存和ERMSB字符串操作(见下文))。
LFENCE仅适用于弱排序负载的内存排序,非常罕见。 (或者在 NT存储之前使用常规负载进行LoadStore订购可能吗?)
来自WB内存的NT加载(movntdqa
)为still strongly ordered,即使是假设的未来CPU也不会忽略NT提示;在x86上执行弱有序加载的唯一方法是从弱有序内存(WC)读取,然后我认为只有movntdqa
。这并非偶然发生在" normal"程序,所以你只需要担心这个,如果你mmap视频RAM或其他什么。
( lfence
的主要用例根本不是内存排序,它用于序列化指令执行,例如用于Spectre缓解,或用于RDTSC 。请参阅{ {3}}和#34;链接问题"该问题的侧边栏。)
如果您正在使用C ++编写,使用std::atomic<>
是一种很好的方式来告诉编译器您有哪些排序要求,因此它不会在编译时重新排序您的内存操作。您可以而且应该在适当的时候使用较弱的版本或获取语义,而不是默认的顺序一致性,因此编译器根本不必在x86上发出任何屏障指令。它只需要保持操作顺序。
在ARM或PPC这样的弱有序体系结构或带有movnt的x86上,在写缓冲区和设置标志以指示数据准备好之间需要一个StoreStore屏障指令。此外,读者在检查标志和读取缓冲区之间需要一个LoadLoad屏障指令。
不计算movnt,x86已经在每个加载之间存在LoadLoad障碍,并且每个商店之间存在StoreStore障碍。 (也保证了LoadStore的订购)。 MFENCE
是所有4种障碍,包括StoreLoad,这是x86默认不做的唯一障碍。 MFENCE确保负载不会在其他线程看到您的商店之前使用旧的预取值,并且可能会使用自己的商店。 (以及作为NT商店订购和装载订购的障碍。)
有趣的事实:x86 lock
- 前缀指令也是全内存屏障。它们可以用作旧32位代码中MFENCE的替代品,可能在不支持它的CPU上运行。 lock add [esp], 0
在其他情况下是无操作,并且在内存上执行读取/修改/写入循环,这在L1缓存中很可能很热并且已经处于MESI一致性协议的M状态。
SFENCE是StoreStore的障碍。在NT存储之后,它可用于为后续商店创建发布语义。
LFENCE几乎总是与内存屏障无关,因为唯一的弱有序负载
一个LoadLoad和Atomic operations, std::atomic<> and ordering of writes。 (loadNT / LFENCE / storeNT
阻止存储在加载之前变为全局可见。我认为如果加载地址是长依赖链的结果,或者缓存中遗漏的另一个加载的结果,这可能在实践中发生。)< / p>
有趣的事实#2(感谢@EOF
):来自also a LoadStore barrier的商店排序很弱(但没有缓存绕过)。 ERMSB建立在常规的Fast-String Ops(来自PPro的自rep stos/movsb
的微编码实现的广泛存储中)。
英特尔记录了ERMSB存储&#34;似乎无法执行的事实&#34;在他们的软件开发人员手册第7.3.9.3节中,第1卷。他们也说
&#34;依赖于顺序的代码应该写入离散的信号量变量 在任何字符串操作之后,允许看到正确排序的数据 由所有加工商&#34;
他们没有在rep movsb
和商店之间提及data_ready
旗帜之间的任何障碍说明。
我读它的方式是rep stosb / rep movsb
之后的隐含SFENCE(至少是字符串数据的栅栏,可能不是其他飞行中弱排序的NT存储)。无论如何,措辞意味着在所有字符串移动写入后,对标志/信号量的写入变为全局可见,因此在使用快速字符串操作填充缓冲区的代码中不需要SFENCE / LFENCE然后写一个标志,或写入读取它的代码。
(总是会发生LoadLoad排序,所以你总是按照其他CPU使其全局可见的顺序看到数据。即使用弱排序的存储来写缓冲区并不会改变其他线程中的负载仍然存在的事实强烈要求。)
摘要:使用普通存储编写一个标志,指示缓冲区已准备就绪。 没有读者只需检查用memset / memcpy 编写的块的最后一个字节。
我还认为ERMSB商店会阻止任何以后的商店传递它们,所以如果你使用movNT
,你仍然只需要SFENCE。即rep stosb
作为一个整体具有释放语义wrt。早先的指示。
可以清除MSR位以禁用ERMSB,以便新服务器能够运行旧的二进制文件,这些二进制文件可以写入&#34;数据就绪&#34;标记为rep stosb
或rep movsb
或其他内容的一部分。 (在这种情况下,我猜你会得到可能使用高效缓存协议的旧快速字符串微代码,但确实会使所有商店按顺序显示给其他内核。)