5

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

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

4

1 回答 1

2
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?

这是不正确的。缓存是事实的来源;没有“从主存储器强制读取”。有一些特殊的指令可以绕过 CPU 缓存,称为非临时加载和存储,但它们与本次讨论无关。

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

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

因此,您会在不同的 CPU 之间获得大量缓存一致性流量……不断争夺相同的缓存线。如果运气不好,CPU 不会执行乱序指令,因此即使 CPU 利用率达到 100%,CPU 也基本上处于空闲状态。

于 2020-11-08T04:30:52.803 回答