亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java編程偽共享與緩存行填充

 更新時(shí)間:2021年09月28日 08:46:41   作者:行云創(chuàng)新  
這篇文章主要介紹了Java編程偽共享與緩存行填充,下面文章Disruptor提到的CPU緩存話題,做了一些嘗試和研究,如Disruptor所說,CPU有緩存?zhèn)喂蚕淼膯栴},并且通過緩存行填充能完美的解決這個(gè)問題,需要的朋友可以參考一下

最近在回顧Disruptor的相關(guān)知識(shí),覺得Disruptor在計(jì)算機(jī)底層的領(lǐng)域確實(shí)比一般人厲害不少,以前在寫程序的時(shí)候,基本是從應(yīng)用邏輯的角度考慮,覺得設(shè)計(jì)模式+少量算法+ 優(yōu)美的代碼=理想的結(jié)果,但看完Disruptor的設(shè)計(jì)后,覺得只考慮應(yīng)用本身是有一定的局限性,還需要懂底層,硬件層面的東西,就像Disruptor一樣,通過底層優(yōu)化,讓程序有質(zhì)的飛躍。

下面就Disruptor提到的CPU緩存話題,做了一些嘗試和研究,如Disruptor所說,CPU有緩存?zhèn)喂蚕淼膯栴},并且通過緩存行填充能完美的解決這個(gè)問題。

1、CPU緩存

CPU是機(jī)器的心臟,最終由它來執(zhí)行所有運(yùn)算和程序。主內(nèi)存(RAM)是存放數(shù)據(jù)(包括代碼行)的地方。CPU和主內(nèi)存之間有好幾層緩存,即使直接訪問主內(nèi)存也是非常慢的。如果你正在多次對一塊數(shù)據(jù)做相同的運(yùn)算,那么在執(zhí)行運(yùn)算的時(shí)候把它加載到離CPU很近的地方就有意義了(比如一個(gè)循環(huán)計(jì)數(shù))。下面是CPU的緩存結(jié)構(gòu)圖:

越靠近CPU的核緩存越快但是也越小,所以一級(jí)緩存很小但很快,并且緊靠著在使用它的CPU內(nèi)核。二級(jí)緩存大一些,也慢一些,注意一級(jí)二級(jí)緩存只能被一個(gè)單獨(dú)的CPU的單個(gè)核使用。三級(jí)緩存在現(xiàn)代多核機(jī)器中更普遍,仍然更大,更慢,但是被單個(gè)插槽上的所有CPU核共享。最后,你擁有一塊主存,由全部插槽上的所有CPU核共享。當(dāng)CPU執(zhí)行運(yùn)算的時(shí)候,它先去一級(jí)緩存查找所需的數(shù)據(jù),再去二級(jí)緩存,然后是三級(jí)緩存,最后如果這些緩存中都沒有,所需的數(shù)據(jù)就要去主內(nèi)存拿。走得越遠(yuǎn),運(yùn)算耗費(fèi)的時(shí)間就越長。所以如果你在做一些很頻繁的事,你要確保數(shù)據(jù)在一級(jí)緩存中。

這是在網(wǎng)上找到的一份CPU緩存未命中時(shí)候的CPU時(shí)鐘消耗一級(jí)大概的耗時(shí):

2、CPU緩存行與偽共享

數(shù)據(jù)在緩存中不是以獨(dú)立的項(xiàng)來存儲(chǔ),不是單獨(dú)的變量,也不是單獨(dú)的指針。緩存系統(tǒng)中是以緩存行(cache line)為單位存儲(chǔ),緩存行是2的整數(shù)冪個(gè)連續(xù)字節(jié),一般為32-256個(gè)字節(jié),最常見的緩存行大小是64個(gè)字節(jié)。

下面是CPU緩存行的邏輯圖:

CPU從主內(nèi)存中加載數(shù)據(jù)的時(shí)候,不是只加載某一個(gè)變量的值,而是加載一個(gè)緩存行的值,例如一個(gè)Java的long類型是8字節(jié),因此在一個(gè)緩存行中可以存8個(gè)long類型的變量。如果你訪問一個(gè)long類型的數(shù)組,當(dāng)數(shù)組中的一個(gè)值被加載到緩存中,它會(huì)額外加載另外7個(gè)。如果你數(shù)據(jù)結(jié)構(gòu)中的項(xiàng)在內(nèi)存中不是彼此相鄰的,例如鏈表LinkedList結(jié)構(gòu),你將得不到緩存行加載所帶來的優(yōu)勢,并且在這些數(shù)據(jù)結(jié)構(gòu)中的每一個(gè)項(xiàng)都可能會(huì)出現(xiàn)緩存未命中,這是也是鏈表不適合遍歷的原因之一。

但是,緩存行加載某一塊內(nèi)存數(shù)據(jù),這個(gè)有好處也有壞處,緩存行不是單個(gè)數(shù)據(jù),而是一組數(shù)據(jù),如上圖所示當(dāng)2個(gè)線程同時(shí)運(yùn)行在2個(gè)core上,同時(shí)加載了同一個(gè)緩存行,Core1修改X數(shù)據(jù),Core2讀Y數(shù)據(jù),Core1修改后提交,Core2發(fā)現(xiàn)X數(shù)據(jù)有變化,緩存未命中,就會(huì)重新加載整個(gè)緩存行,但是Core2并不會(huì)用X數(shù)據(jù),而是讀Y數(shù)據(jù),去重新加載整個(gè)緩存行的數(shù)據(jù),無意中影響彼此的性能。如果兩個(gè)獨(dú)立的線程同時(shí)寫兩個(gè)不同的值會(huì)更糟,因?yàn)槊看尉€程對緩存行進(jìn)行寫操作時(shí),每個(gè)內(nèi)核都要把另一個(gè)內(nèi)核上的緩存塊無效掉并重新讀取里面的數(shù)據(jù)。你基本上是遇到兩個(gè)線程之間的寫沖突了,盡管它們寫入的是不同的變量。每個(gè)線程都要去競爭緩存行的所有權(quán)來更新變量。如果核心1獲得了所有權(quán),緩存子系統(tǒng)將會(huì)使核心2中對應(yīng)的緩存行失效。當(dāng)核心2獲得了所有權(quán)然后執(zhí)行更新操作,核心1就要使自己對應(yīng)的緩存行失效。這會(huì)來來回回的經(jīng)過CPU三級(jí)緩存,大大影響了性能。如果互相競爭的核心位于不同的插槽,就要額外橫跨插槽連接,問題可能更加嚴(yán)重,這就是CPU緩存?zhèn)喂蚕怼?/p>

3、Java處理緩存?zhèn)喂蚕?/h2>

緩存行填充:

因?yàn)槭怯布讓拥倪壿?,幾乎所有程序在跑的時(shí)候都會(huì)遇到這個(gè)問題,那么java是如何處理這個(gè)問題呢?答案就是 緩存行填充 。

對于HotSpot JVM,所有對象都有兩個(gè)字長的對象頭。第一個(gè)字是由24位哈希碼和8位標(biāo)志位(如鎖的狀態(tài)或作為鎖對象)組成的Mark Word。第二個(gè)字是對象所屬類的引用。如果是數(shù)組對象還需要一個(gè)額外的字來存儲(chǔ)數(shù)組的長度。每個(gè)對象的起始地址都對齊于8字節(jié)以提高性能。因此當(dāng)封裝對象的時(shí)候?yàn)榱烁咝剩瑢ο笞侄温暶鞯捻樞驎?huì)被重排序成下列基于字節(jié)大小的順序:

doubles (8) 和 longs (8)
ints (4) 和 floats (4)
shorts (2) 和 chars (2)
booleans (1) 和 bytes (1)
references (4/8)
<子類字段重復(fù)上述順序>


通過對熱點(diǎn)變量周圍進(jìn)行緩存行填充,來規(guī)避緩存?zhèn)喂蚕韼淼膯栴},對于緩存行大小是64字節(jié)或更少的處理器架構(gòu)來說是這樣的,有可能處理器的緩存行是128字節(jié),那么使用64字節(jié)填充還是會(huì)存在偽共享問題,通過增加補(bǔ)全變量的個(gè)數(shù)來確保熱點(diǎn)變量不會(huì)和其他東西同時(shí)存在于一個(gè)緩存行中。下面是Disruptorring buffer的序列號(hào)做的補(bǔ)全代碼:

public long p1, p2, p3, p4, p5, p6, p7; // cache line padding
private volatile long cursor = INITIAL_CURSOR_VALUE; 
public long p8, p9, p10, p11, p12, p13, p14; // cache line padding


當(dāng)CPU緩存加載cursor變量的時(shí)候,會(huì)連帶加載周邊的7個(gè)long類型變量,但是這幾個(gè)long類型變量不會(huì)有任何線程去修改它,因此不會(huì)出現(xiàn)緩存未命中問題,完美規(guī)避了緩存?zhèn)喂蚕淼膯栴}。

4、Java程序代碼驗(yàn)證

官方也給了一個(gè)java的測試demo,那么下面針對各種不同的情景,做一下實(shí)驗(yàn)看看,是不是有緩存?zhèn)喂蚕磉@個(gè)問題,測試代碼如下:

下面針對各個(gè)測試場景,做一下簡單的描述:

場景一:Long變量進(jìn)行寫入,沒有緩存行填充,沒有volatile關(guān)鍵字。

場景二:Long變量進(jìn)行寫入,有緩存行填充,沒有volatile關(guān)鍵字。

場景三:Long變量進(jìn)行寫入,沒有緩存行填充,有volatile關(guān)鍵字。

場景四:Long變量進(jìn)行寫入,有緩存行填充,有volatile關(guān)鍵字。

下面是針對各個(gè)場景的測試結(jié)果(每個(gè)場景測試3次,取平均值):

從測試結(jié)果來看,場景一和場景二差不多,有緩存行填充的稍微快那么一點(diǎn)點(diǎn),區(qū)別不大,都是192276000納秒左右。場景三和場景四有volatile關(guān)鍵字的就不一樣了,這里可以看出volatile關(guān)鍵字對一個(gè)變量的讀取和寫入性能影響還是比較大,寫入耗時(shí)是直接寫入的200多倍,因此volatile關(guān)鍵字怎么用很關(guān)鍵,用到哪些地方也很關(guān)鍵,不要在代碼里面隨便加,不會(huì)用反而會(huì)影響程序運(yùn)行效率。場景三有volatile關(guān)鍵字,但是沒有進(jìn)行緩存行填充,耗時(shí)是有緩存行填充的10幾倍,這里就能看出緩存行填充的效果在用到了內(nèi)存屏障的時(shí)候還是很明顯。

CPU緩存?zhèn)喂蚕淼膯栴},確實(shí)打破了很多人對常規(guī)程序執(zhí)行的理解,如何才能應(yīng)用到工作中呢?有以下幾點(diǎn)需要注意:

  • volatile很熟悉,并且代碼里面使用到了緩存屏障,需要看看能否用到這個(gè)緩存填充行。
  • 清楚程序在某個(gè)時(shí)刻會(huì)有緩存?zhèn)喂蚕韱栴},例如某幾個(gè)代碼在一起的變量會(huì)被多個(gè)線程同時(shí)使用并且有寫入操作,需要用緩存填充行把這幾個(gè)變量隔開。
  • 能使用工具分析自己寫的程序,看看有緩存填充行過后,是否真的能提升效率,例如JProfiler分析工具。

到此這篇關(guān)于Java編程偽共享與緩存行填充的文章就介紹到這了,更多相關(guān)Java編程偽共享與緩存行填充內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot啟動(dòng)時(shí)如何獲取端口和項(xiàng)目名

    springboot啟動(dòng)時(shí)如何獲取端口和項(xiàng)目名

    這篇文章主要介紹了springboot啟動(dòng)時(shí)如何獲取端口和項(xiàng)目名,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 徹底搞定堆排序:二叉堆

    徹底搞定堆排序:二叉堆

    二叉堆有兩種:最大堆和最小堆。最大堆:父結(jié)點(diǎn)的鍵值總是大于或等于任何一個(gè)子節(jié)點(diǎn)的鍵值;最小堆:父結(jié)點(diǎn)的鍵值總是小于或等于任何一個(gè)子節(jié)點(diǎn)的鍵值
    2021-07-07
  • 詳解RabbitMq如何做到消息的可靠性投遞

    詳解RabbitMq如何做到消息的可靠性投遞

    這篇文章主要為大家介紹了RabbitMq如何做到消息的可靠性投遞,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • java實(shí)現(xiàn)科研信息管理系統(tǒng)

    java實(shí)現(xiàn)科研信息管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java科研信息管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • Spring使用ThreadPoolTaskExecutor自定義線程池及異步調(diào)用方式

    Spring使用ThreadPoolTaskExecutor自定義線程池及異步調(diào)用方式

    這篇文章主要介紹了Spring使用ThreadPoolTaskExecutor自定義線程池及異步調(diào)用方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • MyBatis-Plus updateById不更新null值的方法解決

    MyBatis-Plus updateById不更新null值的方法解決

    用Mybatis-Plus的updateById()來更新數(shù)據(jù)時(shí),無法將字段設(shè)置為null值,更新后數(shù)據(jù)還是原來的值,本文就來詳細(xì)的介紹一下解決方法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-08-08
  • Java利用深度搜索解決數(shù)獨(dú)游戲詳解

    Java利用深度搜索解決數(shù)獨(dú)游戲詳解

    數(shù)獨(dú)是一項(xiàng)非常簡單的任務(wù)。玩家需要根據(jù)9×9盤面上的已知數(shù)字,推理出所有剩余空格的數(shù)字,并滿足每一行、每一列、每一個(gè)粗線宮(3*3)內(nèi)的數(shù)字均含1-9,不重復(fù)。本文將利用Java編寫一個(gè)程序來解決給定的數(shù)獨(dú)任務(wù),感興趣的可以動(dòng)手嘗試一下
    2022-08-08
  • java對double數(shù)組排序示例分享

    java對double數(shù)組排序示例分享

    這篇文章主要介紹了java對double數(shù)組排序示例,代碼簡單,下面我們直接上代碼,需要的朋友可以參考下
    2014-03-03
  • 詳解BeanUtils.copyProperties()方法如何使用

    詳解BeanUtils.copyProperties()方法如何使用

    這篇文章主要為大家介紹了詳解BeanUtils.copyProperties()方法如何使用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • JVM教程之內(nèi)存管理和垃圾回收(三)

    JVM教程之內(nèi)存管理和垃圾回收(三)

    這篇文章主要介紹了JVM學(xué)習(xí)筆記的第三篇內(nèi)存管理和垃圾回收,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03

最新評(píng)論