虚假共享和易失性

False sharing and volatile

美好的一天,我最近发现了 Java 8 中引入的注释,称为 Contended。从这个邮件中 list 我了解到什么是虚假共享以及注释如何允许对象或字段分配整个缓存行。

经过一些研究,我发现如果两个内核存储相同的缓存行,其中一个修改它,那么第二个内核必须从主内存中重新读取整行。 https://en.wikipedia.org/wiki/MESI_protocol。但我仍然不清楚为什么硬件会强制 CPU 重读它。我的意思是这就是为什么我们在 Java 中有一个 volatile 关键字,对吗?如果变量被声明为 volatile 那么线程将从缓存中跳过这个变量并且总是 read/write 它 from/to 主内存。如果硬件强制 cpu 在每次写入后重新读取缓存行,那么在多线程应用程序中数据不一致是如何可能的?
提前致谢

After some research I found that if two cores store the same cache line and 
one of them modify it then the second one has to reread entire line from main memory. https://en.wikipedia.org/wiki/MESI_protocol.

这是不正确的。缓存是事实的来源,因为缓存(至少在 X86 上)总是一致的。所以理论上缓存行永远不需要从主内存中读取;它始终可以从 CPU 个缓存之一提供。如果不同的 CPU 缓存需要一个缓存行,它可以只从其他缓存中读取值。使用 MESI,当缓存行处于修改状态并且不同的 CPU 想要读取它时,可能会发生缓存行被刷新到主内存的情况;但除此之外不需要与主存储器通信。这是因为 MESI 不支持脏共享; MOESI 解决了这个问题。

 But it still unclear for me why hardware forces CPU to reread it. 
 I mean that is why we do have a volatile keyword in Java right ? 

X86 上的缓存总是一致的。这不需要特殊的 CPU 说明;这是开箱即用的行为。所以它不会发生,例如值 A=1 被写入某个缓存行,而稍后的读取仍然看到旧值 A=0.

 If variable is declared as volatile then threads will skip this variable 
 from cache and always read/write it from/to main memory. 
 If hardware forces cpu to reread cache lines after every write then how data inconsistency is possible in multi threaded applications?

这是不正确的。缓存是真相的来源;没有'force reading from main memory'。有一些特殊指令可以绕过称为非临时加载和存储的 CPU 缓存,但它们与本次讨论无关。

volatile 的目的是确保保留对不同地址的其他加载和存储的顺序,并且存储对其他线程可见。

如遇虚假分享;如果 CPU 修改了同一个缓存行的不同部分,一个 CPU 需要写入而另一个 CPU 刚刚写入,第一个 CPU 需要失效另一个 CPU 上的缓存行带有 RFO(所有权请求),一旦写入命中 linefillbuffer,并且在确认此 RFO 之前无法继续写入。但是一旦另一个 CPU 想要写入该缓存行,它就需要发送一个 RFO 并等待确认。

因此,您会在不同 CPU 之间获得大量缓存一致性流量.. 不断争夺同一缓存行。如果你不走运,CPU 不会执行乱序指令,所以即使你有 100% CPU 利用率,CPU 实际上大部分时间都是空闲的.