use*_*112 15 optimization x86 assembly atomic memory-barriers
英特尔内存模型保证:
http://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/
我已经看到声称由于Intel内存模型,SFENCE在x86-64上是多余的,但从来没有LFENCE.上述内存模型规则是否使指令冗余?
Pet*_*des 17
对,LFENCE和SFENCE在普通代码中没用,因为除非你使用其他特殊指令或内存类型,否则x86对常规存储的获取/释放语义会使它们变得多余.
对于普通无锁代码而言,唯一重要的围栏是来自locked指令的完整屏障(包括StoreLoad),或者是缓慢的MFENCE.首选+上的xchg顺序一致性存储. 加载和存储是否只有重新排序的指令?因为它更快movmfence
假设没有非时间指令,`xchg`是否包含`mfence`?(是的,即使有 NT指令,只要没有WC内存.)
Jeff Preshing的" 记忆重新排序"中的文章是一篇比较容易阅读的描述Bartosz的帖子谈到的同一案例,你需要像MFENCE这样的StoreLoad屏障. 只有MFENCE会这样做; 你不能用SFENCE + LFENCE构建MFENCE.(为什么(或不是?)SFENCE + LFENCE相当于MFENCE?)
如果您在阅读所发布的链接后遇到问题,请阅读Jeff Preshing的其他博客文章.他们让我对这个主题有了很好的理解.:)尽管我认为我发现关于SFENCE/LFENCE的一些消息通常在Doug Lea的页面中是无操作的.杰夫的帖子没有考虑NT加载/存储.
相关:我什么时候应该使用_mm_sfence _mm_lfence和_mm_mfence(我的答案和@ BeeOnRope的答案都很好.我在很久以前就写了这个答案而不是答案,所以这个答案的部分内容显示了我多年前的经验.我的答案在那里考虑C++内在函数和C++编译时内存顺序,这与x86 asm运行时内存排序完全不同.但你仍然不想要_mm_lfence().)
SFENCE仅在使用movnt(非时间)流式存储时,或者使用类型设置为正常回写之外的内存区域时才相关.或者clflushopt,有点像一个弱有序的商店.NT商店绕过缓存以及虚拟订单. 除了NT存储,WC(写入组合)存储器和ERMSB字符串操作(见下文)之外,x86的正常存储器模型是强有序的.
LFENCE仅对具有弱有序负载的内存排序有用,这种情况非常罕见.(或者在 NT存储之前可以使用常规负载进行LoadStore订购?)
movntdqa来自WB内存的NT load()仍然是强有序的,即使在假设的未来CPU上也不会忽略NT提示; 在x86上执行弱有序加载的唯一方法是从弱有序内存(WC)读取,然后我认为只有movntdqa.这在"普通"程序中不会偶然发生,因此如果您使用视频RAM或其他东西,您只需要担心这一点.
(主要用例lfence不是内存排序,它是用于序列化指令执行,例如用于Spectre缓解,或用于RDTSC.请参阅AMD处理器上的LFENCE序列化?以及该问题的"链接问题"侧栏.)
几个星期前我对此感到好奇,并对最近的一个问题发布了一个相当详细的答案: 原子操作,std :: atomic <>和写入顺序.我包含了大量关于C++内存模型与硬件内存模型的链接.
如果您使用C++编写,使用std::atomic<>是一种很好的方式来告诉编译器您有哪些排序要求,因此它不会在编译时重新排序您的内存操作.您可以而且应该使用较弱的版本或在适当的地方获取语义,而不是默认的顺序一致性,因此编译器不必在x86上发出任何屏障指令.它只需要保持操作顺序.
在像ARM或PPC这样的弱有序体系结构或带有movnt的x86上,在写入缓冲区和设置标志以指示数据准备就绪之间需要一个StoreStore屏障指令.此外,读取器在检查标志和读取缓冲区之间需要一个LoadLoad屏障指令.
不计算movnt,x86已经在每个加载之间存在LoadLoad障碍,并且每个商店之间存在StoreStore障碍.(也保证了LoadStore的订购). MFENCE是4种障碍,包括StoreLoad,这是x86默认不做的唯一障碍.MFENCE确保在其他线程看到您的商店之前,加载不使用旧的预取值,并且可能会使用自己的商店.(以及作为NT商店订购和装载订购的障碍.)
有趣的事实:x86 lock-prefixed指令也是全内存屏障.它们可以用作旧32位代码中MFENCE的替代品,可能在不支持它的CPU上运行. lock add [esp], 0否则是无操作,并且在内存上进行读取/修改/写入循环,该内存很可能在L1高速缓存中很热并且已经处于MESI一致性协议的M状态.
SFENCE是StoreStore的障碍.在NT存储之后为后续存储创建发布语义很有用.
LFENCE几乎总是与内存屏障无关,因为唯一的弱有序负载
一个LoadLoad和一个LoadStore屏障.(loadNT / LFENCE / storeNT防止存储在加载之前变得全局可见.我认为如果加载地址是长依赖链的结果,或者缓存中遗漏的另一个加载的结果,这可能在实践中发生.)
有趣的事实#2(谢谢@EOF):来自ERMSB(增强版rep movsb/ rep stosbIvyBridge及更高版本)的商店排序很弱(但没有缓存绕过).ERMSB建立在常规的Fast-String Ops之上(自PPro rep stos/movsb以来一直存在的微编码实现中的广泛存储).
英特尔记录了ERMSB在其软件开发人员手册第1卷第7.3.9.3节中"可能看起来无序执行"这一事实.他们也说
"依赖于顺序的代码应该在任何字符串操作之后写入离散的信号量变量,以允许所有处理器看到正确排序的数据"
他们没有提到rep movsb商店和商店之间的任何障碍指示data_ready.
我读它的方式,之后有一个隐含的SFENCE rep stosb / rep movsb(至少是字符串数据的栅栏,可能不是其他飞行中弱有序的NT存储).无论如何,措辞意味着在所有字符串移动写入之后对标志/信号量的写入变为全局可见,因此在用快速字符串op填充缓冲区然后写入标志的代码中不需要SFENCE/LFENCE,或者在读取它的代码中.
(总是会发生LoadLoad排序,因此您总是按照其他CPU使其全局可见的顺序查看数据.即使用弱排序存储来写入缓冲区并不会改变其他线程中的负载仍然强烈排序的事实.)
摘要:使用普通存储区写入一个标志,指示缓冲区已准备就绪. 没有读者只需检查用memset/memcpy写的块的最后一个字节.
我还认为ERMSB商店会阻止任何后来的商店传递它们,所以如果你使用的话movNT,你仍然只需要SFENCE.即rep stosb整体上有释放语义.早先的指示.
有一个MSR位可以被清除以禁用ERMSB,以利于需要运行旧二进制文件的新服务器,该二进制文件将"数据就绪"标志写为一个rep stosb或rep movsb多个部分.(在这种情况下,我猜你会得到可能使用高效缓存协议的旧快速字符串微码,但确实会使所有商店按顺序出现在其他内核中).
| 归档时间: |
|
| 查看次数: |
1355 次 |
| 最近记录: |