日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

伪共享(False Sharing)

發(fā)布時間:2025/3/21 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 伪共享(False Sharing) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

原文地址:http://ifeve.com/false-sharing/

作者:Martin Thompson ?譯者:丁一

緩存系統(tǒng)中是以緩存行(cache line)為單位存儲的。緩存行是2的整數冪個連續(xù)字節(jié),一般為32-256個字節(jié)。最常見的緩存行大小是64個字節(jié)。當多線程修改互相獨立的變量時,如果這些變量共享同一個緩存行,就會無意中影響彼此的性能,這就是偽共享。緩存行上的寫競爭是運行在SMP系統(tǒng)中并行線程實現(xiàn)可伸縮性最重要的限制因素。有人將偽共享描述成無聲的性能殺手,因為從代碼中很難看清楚是否會出現(xiàn)偽共享。

為了讓可伸縮性與線程數呈線性關系,就必須確保不會有兩個線程往同一個變量或緩存行中寫。兩個線程寫同一個變量可以在代碼中發(fā)現(xiàn)。為了確定互相獨立的變量是否共享了同一個緩存行,就需要了解內存布局,或找個工具告訴我們。Intel VTune就是這樣一個分析工具。本文中我將解釋Java對象的內存布局以及我們該如何填充緩存行以避免偽共享。

圖 1.

圖1說明了偽共享的問題。在核心1上運行的線程想更新變量X,同時核心2上的線程想要更新變量Y。不幸的是,這兩個變量在同一個緩存行中。每個線程都要去競爭緩存行的所有權來更新變量。如果核心1獲得了所有權,緩存子系統(tǒng)將會使核心2中對應的緩存行失效。當核心2獲得了所有權然后執(zhí)行更新操作,核心1就要使自己對應的緩存行失效。這會來來回回的經過L3緩存,大大影響了性能。如果互相競爭的核心位于不同的插槽,就要額外橫跨插槽連接,問題可能更加嚴重。

Java內存布局(Java Memory Layout)

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

  • doubles (8) 和 longs (8)
  • ints (4) 和 floats (4)
  • shorts (2) 和 chars (2)
  • booleans (1) 和 bytes (1)
  • references (4/8)
  • <子類字段重復上述順序>
  • (譯注:更多HotSpot虛擬機對象結構相關內容:http://www.infoq.com/cn/articles/jvm-hotspot)

    了解這些之后就可以在任意字段間用7個long來填充緩存行。在Disruptor里我們對RingBuffer的cursor和BatchEventProcessor的序列進行了緩存行填充。

    為了展示其性能影響,我們啟動幾個線程,每個都更新它自己獨立的計數器。計數器是volatile long類型的,所以其它線程能看到它們的進展。

    01public?final?class?FalseSharing
    02????implements?Runnable
    03{
    04????public?final?static?int?NUM_THREADS =?4;?// change
    05????public?final?static?long?ITERATIONS = 500L * 1000L * 1000L;
    06????private?final?int?arrayIndex;
    07??
    08????private?static?VolatileLong[] longs =?new?VolatileLong[NUM_THREADS];
    09????static
    10????{
    11????????for?(int?i =?0; i < longs.length; i++)
    12????????{
    13????????????longs[i] =?new?VolatileLong();
    14????????}
    15????}
    16??
    17????public?FalseSharing(final?int?arrayIndex)
    18????{
    19????????this.arrayIndex = arrayIndex;
    20????}
    21??
    22????public?static?void?main(final?String[] args)?throws?Exception
    23????{
    24????????final?long?start = System.nanoTime();
    25????????runTest();
    26????????System.out.println("duration = "?+ (System.nanoTime() - start));
    27????}
    28??
    29????private?static?void?runTest()?throws?InterruptedException
    30????{
    31????????Thread[] threads =?new?Thread[NUM_THREADS];
    32??
    33????????for?(int?i =?0; i < threads.length; i++)
    34????????{
    35????????????threads[i] =?new?Thread(new?FalseSharing(i));
    36????????}
    37??
    38????????for?(Thread t : threads)
    39????????{
    40????????????t.start();
    41????????}
    42??
    43????????for?(Thread t : threads)
    44????????{
    45????????????t.join();
    46????????}
    47????}
    48??
    49????public?void?run()
    50????{
    51????????long?i = ITERATIONS +?1;
    52????????while?(0?!= --i)
    53????????{
    54????????????longs[arrayIndex].value = i;
    55????????}
    56????}
    57??
    58????public?final?static?class?VolatileLong
    59????{
    60????????public?volatile?long?value = 0L;
    61????????public?long?p1, p2, p3, p4, p5, p6;?// comment out
    62????}
    63}

    結果(Results)

    運行上面的代碼,增加線程數以及添加/移除緩存行的填充,下面的圖2描述了我得到的結果。這是在我4核Nehalem上測得的運行時間。

    圖 2.

    從不斷上升的測試所需時間中能夠明顯看出偽共享的影響。沒有緩存行競爭時,我們幾近達到了隨著線程數的線性擴展。

    這并不是個完美的測試,因為我們不能確定這些VolatileLong會布局在內存的什么位置。它們是獨立的對象。但是經驗告訴我們同一時間分配的對象趨向集中于一塊。

    所以你也看到了,偽共享可能是無聲的性能殺手。

    注意:更多偽共享相關的內容,請閱讀我后續(xù)blog。

    原創(chuàng)文章,轉載請注明:?轉載自并發(fā)編程網 – ifeve.com本文鏈接地址:?偽共享(False Sharing)

    總結

    以上是生活随笔為你收集整理的伪共享(False Sharing)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。