成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

填之前的坑,偽共享

開發 前端
我們都知道 CPU 的執行速度遠大于從內存獲取數據的速度,為了減少這個差距科研人員們就不斷的研究,產出了高速緩存,但這個高速緩存由于工藝集成度問題,無法作為主存的介質。

[[435661]]

大家好,我是yes。

之前在寫 FastThreadLocal 的時候,挖了個坑。

咳咳,時間過得有點久了,但是影響不大今天就來補上。

來談談什么是偽共享,并且為什么 Netty 要在這里移除這個優化?

話不多說,發車!

什么是偽共享?

這個名詞聽著有點高級的感覺,實際上很好理解。

我們都知道 CPU 的執行速度遠大于從內存獲取數據的速度,為了減少這個差距科研人員們就不斷的研究,產出了高速緩存,但這個高速緩存由于工藝集成度問題,無法作為主存的介質,所以常見的 CPU 緩存結構如下圖所示:

L1、L2、L3則為 CPU 和主存之間的高速緩沖區,距離 CPU 越近的緩存訪問速度越快,且容量越小。

比如我筆記本的 CPU上:

訪問速度:L1>L2>L3>主存。

L1 和 L2 是單核 CPU 獨享的,當 CPU 訪問數據的時候會先去 L1 上面找,找不到再去 L2,然后是 L3,最后是主存。所以當對一個數據重復計算的時候,應該盡量保證數據在 L1 中,這樣效率才高。

從上面的結構來看,有經驗的同學肯定會發現上面的結構有共享內存多線程的問題。這里就引入了一致性協議 MESI。具體協議內容這里不作展開,這里簡單舉例理解下:

當 cpu1 和 cpu3 共同訪問主存里面的一個數據時,會分別獲取放置到自己高速緩沖區中,當 cpu1 修改了這個數據之后,cpu3 的高速緩沖區中這個數據就失效了,它會讓 cpu1 把這個改動刷新到主存中,然后自己再去主存加載這個數據,這樣數據才會正確。

圖中按序號順序來閱讀,應該不難理解。

然后重點來了,CPU 緩存的單位是緩存行,也就是說 CPU 從主存拿數據不是一個一個拿,是一行一行的拿,這一行的大小一般是 64 字節,那問題就來了。

比如,現在有個 long 數組,大小為 8 ,那剛好這個數組滿足一行的大小。現在 cpu1 頻繁更新long[0]的值,而 cpu3 頻繁更新 long[5] 的值,這就有點麻了。

由于緩存行的機制,每次 cpu1 會把整個數組都加載到緩存中,每次僅修改 long[0] 也會使得這一行都變臟,此時 cpu3 訪問的 long[5] 就失效了,因此 cpu3 需要讓 cpu1 把修改刷新到主存中,然后它從主存重新獲取 long[5] 再進行操作,假設此時 cpu1 又修改了 long[0],則上面的操作就又得來一遍!

明明修改的是不同的變量,但是卻相互影響了,這種情況,就稱之為,偽共享!

如何避免偽共享問題?

解決的方案非常簡單粗暴,填充。

把可能會沖突的數據在內存上隔開來,用什么隔?用無用的數據隔開。

在關鍵數據前后(上圖僅填充了后)填充無用的數據,讓一個緩存行中,僅會存在一個有效的數據,其它都是無效的數據,就避免了一個緩存行里面出現多個有效的數據。這樣一來不同的 CPU 核心修改不同的數據就不會造成其它數據緩存失效,避免了偽共享的問題。

所以 Netty 里 InternalThreadLocalMap 中奇怪的代碼就是起這個作用的。 

但恕我直言,可能是我等級太低,我沒看出來這玩意到底是為了哪個變量而填充的。

果然,最新的版本有個大佬把它標注為廢棄。

我從 github 上看了看,大佬將其廢棄的理由如下:

簡單直白的翻譯下:

我看不出填充有什么切實的好處。

唯一保護的對象可能是 BitSet,但是它的修改并不頻繁

填充用了 long,這并不一定會阻止 JVM 在對齊間隙中匹配上述的對象引用。

簡單來講就是沒發現這填充有啥好用,所以廢棄了,將來版本要咔嚓了它。

所以拿 Netty 來展示偽共享的例子不行(我只是把之前寫 FastThreadLocal 的坑填了)。

現在填完了,我們換個好的例子。

用代碼跑跑看

我寫了個例子,咱們來看看填充和不填充的真實差距。

我用兩個線程分別循環五千萬次修改一個對象里面的兩個變量 a 和 b,這兩個變量大概率會在同一個緩存行中,這樣就制造了偽共享的現場。

在未填充的情況下,耗費的毫秒數是1400。

然后我們再用變量p1-p7填充一下,隔開 a 和 b。

可以看到,結果變成了380毫秒,這么一看,確實生效了!說明填充確實有效!

其實 Java 提供了一個注解 @Contended,可以標記到指定的字段上,減少偽共享的發生,你可以認為這個注解會讓 JVM 自動幫我們填充,而不需要我們手寫填充的變量。不過要注意一點,這個注解需要啟動時添加-XX:-RestrictContended 參數,才會生效。

我們跑一下看下結果:

果然,也提高了效率!

這個注解其實在別的地方也有應用,比如 ConcurrentHashMap 里的 CounterCell

還有 Striped64 里的 Cell

不過要注意,沒有-XX:-RestrictContended 不會生效的!

最后

至此,想必你已經明白了什么是偽共享,并且可以利用填充來避免偽共享的問題。

但填充就代表著空間的浪費,也不是什么情況下都需要填充。

只有在頻繁更新相鄰字段的情況下,才可能需要考慮偽共享的情況,別的情況不需要下操心。

好了,今天就到這了。

 

責任編輯:武曉燕 來源: yes的練級攻略
相關推薦

2015-04-03 09:38:32

程序員前人挖坑后人填坑

2024-06-04 22:20:02

2020-10-29 10:22:44

中臺

2022-12-12 08:39:09

CPUCache偽共享

2017-07-13 16:40:16

偽共享緩存行存儲

2019-12-17 14:24:11

CPU緩存偽共享

2021-03-25 10:14:10

自動化運營人工智能AIOps

2021-03-31 08:33:17

SysTick定時器SysTick定時器

2023-06-12 07:00:40

Rust進度任務

2022-01-17 14:24:09

共享字節面試

2021-04-16 08:11:24

js前端JavaScript

2016-10-19 19:03:18

javascriptes6React Nativ

2017-03-02 13:32:36

Android開發開發者

2022-02-02 21:50:25

底層偽共享CPU

2023-12-26 10:08:57

緩存偽共享修飾結構體

2017-08-23 13:21:31

2017-05-05 08:12:51

Spark共享變量

2018-11-22 16:20:07

RedisMySQL數據庫

2018-02-27 16:01:24

2022-08-17 06:25:19

偽共享多線程
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩精品免费一区二区在线观看 | 人人做人人澡人人爽欧美 | 国产精品美女 | 成人免费视频网站 | 亚洲成人高清 | 国产精品无码永久免费888 | 蜜桃视频一区二区三区 | 亚洲成人毛片 | 国产乱码精品1区2区3区 | 日韩欧美在线播放 | 中文字幕成人av | 91精品国产91久久久久久最新 | 99综合| 黄色免费在线网址 | 视频三区 | 久久精品视频在线观看 | 中国大陆高清aⅴ毛片 | 99热最新网址 | 亚洲视频免费 | 成人精品久久久 | 久久精品亚洲国产 | 国产91视频免费 | 亚洲视频免费在线观看 | 免费一级片 | 国产精品极品美女在线观看免费 | 人人澡视频 | 亚洲成人福利在线观看 | 国产一区二区在线播放 | 草b视频 | 一级片免费在线观看 | h视频免费在线观看 | 久久日韩粉嫩一区二区三区 | 色综合一区二区 | 99re热精品视频 | 久久成人精品 | 91免费在线视频 | 99re6在线 | 欧美色性 | 国产激情自拍视频 | 国产成人精品午夜视频免费 | 国产精品美女久久久久久免费 |