理解记忆障碍

我试图在一个对java无锁程序员有用的级别上理解内存障碍。我觉得这个级别介于学习挥发性和从x86手册学习存储/加载缓冲区的工作之间。

我花了一些时间阅读一堆博客/烹饪书,并提出了以下摘要。 有更多知识渊博的人可以查看摘要,看看我是否遗漏了错误或列出的内容。

LFENCE:

Name : LFENCE/Load Barrier/Acquire Fence Barriers : LoadLoad + LoadStore Details : Given sequence {Load1, LFENCE, Load2, Store1}, the barrier ensures that Load1 can't be moved south and Load2 and Store1 can't be moved north of the barrier. Note that Load2 and Store1 can still be reordered. Buffer Effect : Causes the contents of the LoadBuffer (pending loads) to be processed for that CPU.This makes program state exposed from other CPUs visible to this CPU before Load2 and Store1 are executed. Cost on x86 : Either very cheap or a no-op. Java instructions: Reading a volatile variable, Unsafe.loadFence() 

SFENCE

 Name : SFENCE/Store Barrier/Release Fence Barriers : StoreStore + LoadStore Details : Given sequence {Load1, Store1, SFENCE, Store2,Load2} the barrier ensures that Load1 and Store1 can't be moved south and Store2 can't be moved north of the barrier. Note that Load1 and Store1 can still be reordered AND Load2 can be moved north of the barrier. Buffer Effect : Causes the contents of the StoreBuffer flushed to cache for the CPU on which it is issued. This will make program state visible to other CPUs before Store2 and Load1 are executed. Cost on x86 : Either very cheap or a no-op. Java instructions: lazySet(), Unsafe.storeFence(), Unsafe.putOrdered*() 

MFENCE

 Name : MFENCE/Full Barrier/Fence Barriers : StoreLoad Details : Obtains the effects of the other three barrier. Given sequence {Load1, Store1, MFENCE, Store2,Load2}, the barrier ensures that Load1 and Store1 can't be moved south and Store2 and Load2 can't be moved north of the barrier. Note that Load1 and Store1 can still be reordered AND Store2 and Load2 can still be reordered. Buffer Effect : Causes the contents of the LoadBuffer (pending loads) to be processed for that CPU. AND Causes the contents of the StoreBuffer flushed to cache for the CPU on which it is issued. Cost on x86 : The most expensive kind. Java instructions: Writing to a volatile, Unsafe.fullFence(), Locks 

最后,如果SFENCE和MFENCE都消耗了storeBuffer(使cacheline无效并等待来自其他cpus的ack),为什么一个是no-op而另一个是非常昂贵的op?

谢谢

(来自谷歌机械同情论坛的交叉发布)

你正在使用Java,所以最重要的是Java内存模型。 编译时(包括JIT) 优化将在Java内存模型的限制内重新排序内存访问 ,而不是JVM恰好为JIT编译的更强大的x86内存模型。 (请参阅我的回答内存重新排序如何帮助处理器和编译器? )

尽管如此,了解x86可以为您的理解提供一个具体的基础,但不要陷入认为x86上的Java像x86上的程序集一样工作的陷阱。 (或者整个世界都是x86。许多其他架构都是弱有序的,就像Java内存模型一样。)


x86 LFENCESFENCE就内存排序SFENCE都是无操作,除非你使用了movnt弱排序缓存旁路存储。 正常负载是隐式获取负载,而正常存储是隐式释放存储 。


您的表中有错误 :根据英特尔的指令集参考手册, SFENCE “未根据加载指令进行排序” 。 它只是 StoreStore屏障,而不是LoadStore屏障。

(该链接是英特尔pdf的html转换。请参阅x86标签wiki以获取官方版本的链接。)

lfence是一个LoadLoad和LoadStore屏障,所以你的表是正确的。

但CPU并没有提前“缓冲”负载。 他们执行这些操作并在结果可用时立即开始使用无序执行的结果。 (通常在加载结果准备好之前,使用加载结果的指令已被解码并发出,即使在L1缓存命中时也是如此)。 这是加载和存储之间的根本区别。


SFENCE很便宜,因为它实际上不必耗尽存储缓冲区。 这是实现它的一种方法,它以性能为代价保持硬件简单。

MFENCE价格昂贵,因为它是阻止StoreLoad重新排序的唯一障碍。 请参阅Jeff Preshing的内存重新排序法案中的解释,以及实际演示StoreLoad在真实硬件上重新排序的测试程序。

Jeff Preshing的博客文章是理解无锁编程和内存排序语义的黄金。 我通常将他的博客链接到我的SO回答中,以便记忆顺序问题。 如果你有兴趣阅读我写的更多内容(主要是C ++ / asm,而不是Java),你可以使用搜索来找到这些答案。


有趣的事实:x86上的任何primefaces读 – 修改 – 写操作也是一个完整的内存屏障。 lock前缀(隐含在xchg [mem], reg )也是一个完整的障碍。 lock add [esp], 0 ,在mfence可用之前lock add [esp], 0是内存屏障的常用习惯用法,否则就是无操作。 (堆栈内存在L1中几乎总是很热,而不是共享)。

因此,在x86上,无论您请求的内存排序语义如何,递增primefaces计数器都具有相同的性能。 (例如c ++ 11 memory_order_relaxedmemory_order_seq_cst (顺序一致性))。 但是,使用任何适合的内存顺序语义,因为其他体系结构可以在没有完全内存障碍的情况下执行primefaces操作。 当您不需要时强制编译器/ JVM使用内存屏障是一种浪费。