最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

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

1、CPU緩存

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

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

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

2、CPU緩存行與偽共享

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

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

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

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

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

緩存行填充:

因為是硬件底層的邏輯,幾乎所有程序在跑的時候都會遇到這個問題,那么java是如何處理這個問題呢?答案就是 緩存行填充 。

對于HotSpot JVM,所有對象都有兩個字長的對象頭。第一個字是由24位哈希碼和8位標志位(如鎖的狀態(tài)或作為鎖對象)組成的Mark Word。第二個字是對象所屬類的引用。如果是數(shù)組對象還需要一個額外的字來存儲數(shù)組的長度。每個對象的起始地址都對齊于8字節(jié)以提高性能。因此當封裝對象的時候為了高效率,對象字段聲明的順序會被重排序成下列基于字節(jié)大小的順序:

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


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

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


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

4、Java程序代碼驗證

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

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

場景一:Long變量進行寫入,沒有緩存行填充,沒有volatile關鍵字。

場景二:Long變量進行寫入,有緩存行填充,沒有volatile關鍵字。

場景三:Long變量進行寫入,沒有緩存行填充,有volatile關鍵字。

場景四:Long變量進行寫入,有緩存行填充,有volatile關鍵字。

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

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

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

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

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

相關文章

  • spring jpa ManyToMany原理及用法詳解

    spring jpa ManyToMany原理及用法詳解

    這篇文章主要介紹了spring jpa ManyToMany原理及用法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-10-10
  • 如何基于java實現(xiàn)Gauss消元法過程解析

    如何基于java實現(xiàn)Gauss消元法過程解析

    這篇文章主要介紹了如何基于java實現(xiàn)Gauss消元法過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-10-10
  • 基于CXF搭建webService的實例講解

    基于CXF搭建webService的實例講解

    下面小編就為大家?guī)硪黄贑XF搭建webService的實例講解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • springboot RESTful以及參數(shù)注解的使用方式

    springboot RESTful以及參數(shù)注解的使用方式

    這篇文章主要介紹了springboot RESTful以及參數(shù)注解的使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Netty分布式高性能工具類同線程下回收對象解析

    Netty分布式高性能工具類同線程下回收對象解析

    這篇文章主要為大家介紹了Netty分布式高性能工具類同線程下回收對象解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-03-03
  • JAVA基于SnakeYAML實現(xiàn)解析與序列化YAML

    JAVA基于SnakeYAML實現(xiàn)解析與序列化YAML

    這篇文章主要介紹了JAVA基于SnakeYAML實現(xiàn)解析與序列化YAML,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-12-12
  • 詳解@ConfigurationProperties實現(xiàn)原理與實戰(zhàn)

    詳解@ConfigurationProperties實現(xiàn)原理與實戰(zhàn)

    這篇文章主要介紹了詳解@ConfigurationProperties實現(xiàn)原理與實戰(zhàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-10-10
  • springboot實現(xiàn)分頁功能的完整代碼

    springboot實現(xiàn)分頁功能的完整代碼

    Spring Boot是一個快速開發(fā)框架,它提供了很多便捷的功能,其中包括分頁查詢,下面這篇文章主要給大家介紹了關于springboot實現(xiàn)分頁功能的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-04-04
  • JAVA記住密碼功能的實現(xiàn)代碼

    JAVA記住密碼功能的實現(xiàn)代碼

    這篇文章主要介紹了JAVA記住密碼功能的實現(xiàn)代碼,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-01-01
  • 更改idea的JDK版本超簡單便捷方法

    更改idea的JDK版本超簡單便捷方法

    idea很多地方都設置了jdk版本,不同模塊的jdk版本也可能不一樣,下面這篇文章主要給大家介紹了關于更改idea的JDK版本的超簡單便捷方法,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2023-11-11

最新評論

海阳市| 饶阳县| 浦城县| 浏阳市| 浏阳市| 丰台区| 龙游县| 平潭县| 泸西县| 德格县| 克东县| 旬邑县| 乌鲁木齐县| 邻水| 上栗县| 成都市| 甘德县| 张北县| 山西省| 营口市| 视频| 正宁县| 图们市| 宁蒗| 芦溪县| 澄城县| 涟源市| 盐城市| 万安县| 泰兴市| 罗甸县| 华宁县| 辽中县| 子长县| 丘北县| 松桃| 策勒县| 镇江市| 正安县| 西乌| 清丰县|