java并发 cpu高_java高并发核心要点|系列5|CPU内存伪共享
上節提到的:偽共享,今天我們來說說。
那什么是偽共享呢?
這得從CPU的緩存結構說起。以下如圖,CPU一般來說是有三級緩存,1 級,2級,3級,越上面的,越靠近CPU的,速度越快,成本也越高。也就是說速度方面:1級>2級>3級。
圖1
圖2
如上圖2我們來看看不同級別的緩存的時延:
到CPU的延遲CPU時鐘耗時
主內存
很多(Multiple)
~60-80 ns
L3 緩存
~40-45 周期
~15 ns
L2 緩存
~10 周期
~3 ns
L1 緩存
~3-4 周期
~1 ns
寄存器
一周期
小于1ns,飛快
更多CPU架構信息:https://blog.csdn.net/karamos/article/details/80126704
說到這里,我們要理解一個很重要的概念:緩存行。什么是緩存行?
首先我們來看這幾級緩存,其中,1,2級緩存是CPU核心私有的,也就是說每個核,之間不會共享1,2級緩存,那它們之間怎么通信或共享數據呢?
答案是:3級緩存,如下圖:
那core1,和core2之間,是通過什么方式共享緩存呢?
答案是:緩存行!
什么是緩存行?簡單來說,就是CPU內核之間共享數據的最小單位。如下圖:x,y是在同一個緩存行,那每次CPU內核之間通信時交換x,y值,可以同時共享兩個值。是不是很高效?
是的,一般情況下,如果x,y是屬于數組內的數據 ,是可以達到高效共享數據的功能,但問題又來了:如果,x,y并不屬于同一數組,x屬于core1,而y屬于core2,這個時候,如果core1更新了x,會導致y值失效了。為什么失效了,因為他們在同一緩存行。這時,只有把緩存行 flush到主存后, 其他內核中的相應的緩存行才會被置為過期數據,而緩存行什么時候flush到memory, 這個是有一定延時的 ,在這個延時當中, 其他CPU core是無法得知你的更新的 。那么內核core2再去讀取Y的值時,由于L1的緩存里的數據已失效,那么就需要從L3獲取,然后放入L2,再放入L1。 這樣核心2讀取Y值就需要從L3級的緩存讀了。但是明明是內核core1修改的X的值,卻影響到內核2去讀取Y值了。同理,如果是內核2去修改Y的值,也會影響內核1去讀取X的值。
簡單來說,x,y同放在緩存行,而且它們又屬于不同CPU內核的數據值(事實上CPU內核也就是代碼中的:線程)。那就會因為各自更新其中一個值,而導致緩存失效。
這就是著名的偽共享問題。
有沒有什么解決方案呢?
有的。
方案是:緩存行填充。
還是回到上面的例子,如果x,y同放到同一個緩存行,會造成偽共享。很簡單,那就不要放在一起好了!
比如:x有8byte(字節),而一般緩存行總共有64byte。那其他剩下的位置,我們就用預定的空變量填充就行了,代碼如下 (java6版本):
public final static classVolatileLong {
public volatile long x= 0L;
public long p1, p2, p3, p4, p5, p6,p7;//緩存行填充}
這個時候,core1更新x值,也就不會影響y值,從而造成偽共享問題。
上面的代碼是java6的解決方案。
JAVA 8下的方案
在JAVA 8中,緩存行填充終于被JAVA原生支持了。JAVA 8中添加了一個@Contended的注解,添加這個的注解,將會在自動進行緩存行填充,如下代碼:
import sun.misc.Contended;
@Contended
public class VolatileLong {
public volatile long value = 0L;
}
執行時,必須加上虛擬機參數-XX:-RestrictContended,@Contended注釋才會生效。很多文章把這個漏掉了,那樣的話實際上就沒有起作用。
這就是偽共享的解決方案,多么簡單!
本系列完畢!
如果各位讀者,還有什么意見或建議,歡迎拍磚吐槽!
總結
以上是生活随笔為你收集整理的java并发 cpu高_java高并发核心要点|系列5|CPU内存伪共享的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql blgg__MySQL_ex
- 下一篇: 高性能计算机存储部件有磁盘阵列,信息存储