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

并發(fā)場(chǎng)景下的底層細(xì)節(jié) - 偽共享問(wèn)題

系統(tǒng) 其他OS
眾所周知,為了緩解內(nèi)存和 CPU 之間速度不匹配的矛盾,引入了高速緩存這個(gè)東西,它的容量比內(nèi)存小很多,但是交換速度卻比內(nèi)存要快得多。

最近看書(shū)看到的偽共享問(wèn)題,直接觸碰到知識(shí)盲區(qū)了,之前確實(shí)沒(méi)聽(tīng)說(shuō)過(guò)這個(gè)東西,打開(kāi)百度就像吃飯一樣自然。

雖然面經(jīng)上出現(xiàn)的次數(shù)不多,不過(guò)我覺(jué)得還是很重要的一個(gè)問(wèn)題,而且不難,花個(gè)五分鐘就能弄清楚~

老規(guī)矩,背誦版在文末。點(diǎn)擊閱讀原文可以直達(dá)我收錄整理的各大廠(chǎng)面試真題

三級(jí)緩存架構(gòu)

眾所周知,為了緩解內(nèi)存和 CPU 之間速度不匹配的矛盾,引入了高速緩存這個(gè)東西,它的容量比內(nèi)存小很多,但是交換速度卻比內(nèi)存要快得多。

之前我們畫(huà)過(guò)這樣的分級(jí)存儲(chǔ)體系結(jié)構(gòu):

?

事實(shí)上,高速緩存仍然存在細(xì)分,也稱(chēng)為三級(jí)緩存結(jié)構(gòu):一級(jí)(L1)緩存、二級(jí)(L2)緩存、三級(jí)(L3)緩存

越靠近 CPU 的緩存,速度越快,容量也越小。所以 L1 緩存容量最小但是速度最快;L3 緩存容量最大同時(shí)速度也最慢

當(dāng) CPU 執(zhí)行運(yùn)算的時(shí)候,它會(huì)先去 L1 緩存查找所需的數(shù)據(jù)、如果沒(méi)有找到的話(huà)就再去 L2 緩存、然后是 L3 緩存,如果最后這三級(jí)緩存中都沒(méi)有命中,那么 CPU 就要去訪(fǎng)問(wèn)內(nèi)存了。

顯然,CPU 走得越遠(yuǎn),運(yùn)算耗費(fèi)的時(shí)間就越長(zhǎng)。所以盡量確保數(shù)據(jù)存在 L1 緩存中能夠提升大計(jì)算量情況下的運(yùn)行速度。

需要注意的是,CPU 和三級(jí)緩存以及內(nèi)存的對(duì)應(yīng)使用關(guān)系:

  • L1 和 L2 都是只能被一個(gè)單獨(dú)的 CPU 核心使用
  • L3 可以被單個(gè)插槽上的所有 CPU 核心共享
  • 內(nèi)存由全部插槽上的所有 CPU 核心共享

如下圖所示:

另外,這三級(jí)緩存空間中的數(shù)據(jù)是如何組織起來(lái)的呢?換句話(huà)說(shuō),數(shù)據(jù)在這三級(jí)緩存中的存儲(chǔ)形式是什么樣的呢?

Cache Line(緩存行)!

緩存中的基本存儲(chǔ)單元就是 Cache Line。

每個(gè) Cache Line 通常是 64 字節(jié),也就是說(shuō),一個(gè) Java 的 long 類(lèi)型變量是 8 字節(jié),一個(gè) Cache Line 中可以存 8 個(gè) long 類(lèi)型的變量。

所以小伙伴們看出來(lái)了嗎~ 緩存中的數(shù)據(jù)并不是按照一個(gè)一個(gè)單獨(dú)的變量這樣存儲(chǔ)組織起來(lái)的,而是多個(gè)變量會(huì)放到一行中。

偽共享問(wèn)題 False Sharing

說(shuō)了這么多似乎還并未觸及偽共享問(wèn)題,別急,我們離真相已經(jīng)很近~

在程序運(yùn)行的過(guò)程中,由于緩存的基本單元 Cache Line 是 64 字節(jié),所以緩存每次更新都會(huì)從內(nèi)存中加載連續(xù)的 64 個(gè)字節(jié)。

如果訪(fǎng)問(wèn)的是一個(gè) long 類(lèi)型數(shù)組的話(huà),當(dāng)數(shù)組中的一個(gè)值比如 v1 被加載到緩存中時(shí),接下來(lái)地址相鄰的 7 個(gè)元素也會(huì)被加載到緩存中。(這也能解釋為啥我們數(shù)組總是能夠這么快,像鏈表這種離散存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu),就無(wú)法享受到這種紅利)。

But,這波紅利很可能帶來(lái)無(wú)妄之災(zāi)。

舉個(gè)例子,如果我們定義了兩個(gè) long 類(lèi)型的變量 a 和 b,他們?cè)趦?nèi)存中的地址是緊挨著的,會(huì)出現(xiàn)什么問(wèn)題?

如果我們想要訪(fǎng)問(wèn) a 的話(huà),那么 b 也會(huì)被存儲(chǔ)到緩存中來(lái)。

懵了懵了,這有什么問(wèn)題嗎,看起來(lái)似乎沒(méi)有什么毛病,多么 nice 的特性啊

來(lái)來(lái)來(lái),直接上個(gè)例子

回想下上文提到的 CPU 和三級(jí)緩存以及內(nèi)存的對(duì)應(yīng)使用關(guān)系,設(shè)想這種情況,如果一個(gè) CPU 核心的線(xiàn)程 T1 在對(duì) a 進(jìn)行修改,另一個(gè) CPU 核心的線(xiàn)程 T2 卻在對(duì) b 進(jìn)行讀取。

當(dāng) T1 修改 a 的時(shí)候,除了把 a 加載到 Cache Line,還會(huì)享受一波紅利,把 b 同時(shí)也加載到 T1 所處 CPU 核心的 Cache Line 中來(lái),對(duì)吧。

根據(jù) MESI 緩存一致性協(xié)議,修改完 a 后這個(gè) Cache Line 的狀態(tài)就是 M(Modify,已修改),而其它所有包含 a 的 Cache Line 中的 a 就都不是最新值了,所以都將變?yōu)?I 狀態(tài)(Invalid,無(wú)效狀態(tài))

這樣,當(dāng) T2 來(lái)讀取 b 時(shí),誒,發(fā)現(xiàn)他所處的 CPU 核心對(duì)應(yīng)的這個(gè) Cache Line 已經(jīng)失效了,mmp,它就需要花費(fèi)比較長(zhǎng)的時(shí)間從內(nèi)存中重新加載了。

問(wèn)題已經(jīng)顯而易見(jiàn)了,b 和 a 沒(méi)有任何關(guān)系,每次卻要因?yàn)?a 的更新導(dǎo)致他需要從內(nèi)存中重新讀取,拖慢了速度。這就是偽共享

表面上 a 和 b 都是被獨(dú)立線(xiàn)程操作的,而且兩操作之間也沒(méi)有任何關(guān)系。只不過(guò)它們共享了一個(gè)緩存行,但所有競(jìng)爭(zhēng)沖突都是來(lái)源于共享。

用更書(shū)面的解釋來(lái)定義偽共享:當(dāng)多線(xiàn)程修改互相獨(dú)立的變量時(shí),如果這些變量共享同一個(gè)緩存行,就會(huì)無(wú)意中影響彼此的性能,導(dǎo)致無(wú)法充分利用緩存行特性,這就是偽共享。

偽共享解決方案

我們先來(lái)舉個(gè)例子看看一段偽共享代碼的耗時(shí),如下所示,我們定義一個(gè) Test 類(lèi),它包含兩個(gè) long 的變量,分別用兩個(gè)線(xiàn)程對(duì)這兩個(gè)變量進(jìn)行自增 1 億次,這段代碼耗時(shí) 5625ms

對(duì)于偽共享,一般有兩種方法,其實(shí)思想都是一樣的:

1)padding:就是增大數(shù)組元素之間的間隔,使得不同線(xiàn)程存取的元素位于不同的緩存行上,以空間換時(shí)間

上面提到過(guò),一個(gè) 64 字節(jié)的 Cache Line 中可以存 8 個(gè) long 類(lèi)型的變量,我們?cè)?a 和 b 這兩個(gè) long 類(lèi)型的變量之間再加 7 個(gè) long 類(lèi)型,使得 a 和 b 處在不同的 Cache Line 上面:

class Pointer {
volatile long a;
long v1, v2, v3, v4, v5, v6, v7;
volatile long b;
}

再次運(yùn)行程序,會(huì)發(fā)現(xiàn)輸出時(shí)間神奇的縮短為了 955ms

2)JDK1.8 提供了 @Contended 注解:就是把我們手動(dòng) padding 的操作封裝到這個(gè)注解里面了,這個(gè)注解可以放在類(lèi)上也可以放在字段上,這里就不多做說(shuō)明了

class Test {
@Contended
volatile long a; // 填充 a
volatile long b;
}

需要注意的是,默認(rèn)使用這個(gè)注解是無(wú)效的,需要在 JVM 啟動(dòng)參數(shù)加上 XX:-RestrictContended 才會(huì)生效

最后放上這道題的背誦版:

 面試官:講一下偽共享問(wèn)題

小牛肉:偽共享問(wèn)題其實(shí)是由于高速緩存的特性引起的,三級(jí)高速緩存中的數(shù)據(jù)并不是一個(gè)變量一個(gè)變量單獨(dú)存放的,它的基本存儲(chǔ)單元是 Cache Line,一般一個(gè) Cache Line 的大小是 64 字節(jié),也就是說(shuō),一個(gè) Cache Line 中可以存下 8 個(gè) 8 字節(jié)的 long 類(lèi)型變量

那由于高速緩存的基本單元是 64 字節(jié)的 Cache Line,所以呢,緩存從內(nèi)存中一次讀取的數(shù)據(jù)就是 64 字節(jié)。換句話(huà)說(shuō),如果 cpu 要讀取一個(gè) long 類(lèi)型的數(shù)組,讀取其中一個(gè)元素的同時(shí)也會(huì)把接下來(lái)的其他相鄰地址的七個(gè)元素也加載到 Cache Line 中來(lái)。

這就會(huì)導(dǎo)致一個(gè)問(wèn)題,舉個(gè)例子,我們定義了兩個(gè) long 類(lèi)型的變量 a 和 b,他們沒(méi)有關(guān)系,但是他們?cè)趦?nèi)存中的地址是緊挨著的,如果一個(gè) CPU 核心的線(xiàn)程 T1 在對(duì) a 進(jìn)行修改,另一個(gè) CPU 核心的線(xiàn)程 T2 卻在對(duì) b 進(jìn)行讀取。

那么當(dāng) T1 修改 a 的時(shí)候,除了把 a 加載到 Cache Line,還會(huì)把 b 同時(shí)也加載到 T1 所處 CPU 核心的 Cache Line 中來(lái),對(duì)吧。

根據(jù) MESI 緩存一致性協(xié)議,修改完 a 后這個(gè) Cache Line 的狀態(tài)就是 M(Modify,已修改),而其它所有包含 a 的 Cache Line 中的 a 就都不是最新值了,所以都將變?yōu)?I 狀態(tài)(Invalid,無(wú)效狀態(tài))

這樣,當(dāng) T2 來(lái)讀取 b 時(shí),他就會(huì)發(fā)現(xiàn),他所處的 CPU 核心對(duì)應(yīng)的這個(gè) Cache Line 已經(jīng)失效了,這樣他就需要花費(fèi)比較長(zhǎng)的時(shí)間從內(nèi)存中重新加載了。

也就是說(shuō),b 和 a 沒(méi)有任何關(guān)系,每次卻要因?yàn)?a 的更新導(dǎo)致他需要從內(nèi)存中重新讀取,拖慢了速度。這就是偽共享

對(duì)于偽共享,一般有兩種方法,其實(shí)思想都是一樣的:

1)padding:就是增大數(shù)組元素之間的間隔,使得不同線(xiàn)程存取的元素位于不同的緩存行上,以空間換時(shí)間。比如在 a 和 b 之間再定義 7 個(gè) long 類(lèi)型的變量,使得 a 和 b 不在一個(gè) Cache Line 上,這樣當(dāng)修改 a 的時(shí)候 b 所處的 Cache Line 就不會(huì)受到影響了

2)JDK 1.8 提供了 @Contended 注解,就是把我們手動(dòng) padding 的操作封裝到這個(gè)注解里面了,把這個(gè)注解加在字段 a 或者 b 的上面就可以了


責(zé)任編輯:武曉燕 來(lái)源: 飛天小牛肉
相關(guān)推薦

2022-12-12 08:39:09

CPUCache偽共享

2018-05-04 15:15:37

數(shù)據(jù)庫(kù)MySQL并發(fā)場(chǎng)景

2018-07-27 10:56:10

2019-07-05 17:40:24

MySQL并發(fā)數(shù)據(jù)庫(kù)

2021-12-01 10:13:48

場(chǎng)景分布式并發(fā)

2022-08-04 18:23:28

屏幕共享卡頓流暢度

2019-01-15 14:44:02

CPU Cache L共享存儲(chǔ)器

2022-05-27 09:25:49

數(shù)據(jù)并發(fā)

2021-11-18 08:55:49

共享CPU內(nèi)存

2023-05-28 13:13:54

高并發(fā)場(chǎng)景JUC

2022-05-11 11:25:49

模型方案

2025-02-26 03:00:00

2025-02-28 00:03:22

高并發(fā)TPS系統(tǒng)

2019-12-17 14:24:11

CPU緩存偽共享

2017-07-13 16:40:16

偽共享緩存行存儲(chǔ)

2024-01-05 08:23:55

HttpClientQPS高并發(fā)

2024-07-25 09:05:35

2025-06-05 01:22:00

SpringGateway高并發(fā)

2021-01-13 05:27:02

服務(wù)器性能高并發(fā)

2023-07-18 09:24:04

MySQL線(xiàn)程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 中文字幕av亚洲精品一部二部 | 国产精品国产三级国产a | 国产黄色在线观看 | 国产精品美女 | 一区二区三区四区在线视频 | 99精品99 | 羞羞视频在线观免费观看 | 国产精品自产拍在线观看蜜 | 噜噜噜色网| 欧美黄 片免费观看 | 免费观看的av毛片的网站 | 亚洲一区二区三区国产 | 亚洲一区在线日韩在线深爱 | 亚洲国产成人精品在线 | 婷婷一级片 | 久久久精品网站 | 国产视频导航 | 欧美一区二区三区精品 | 91精品国产综合久久精品 | 午夜久久久久久久久久一区二区 | 黄色片免费 | 色在线看| av一区二区三区四区 | 在线观看精品 | 午夜精品一区二区三区在线视 | 亚洲精品美女视频 | 在线第一页 | 成人精品鲁一区一区二区 | 一级大黄色片 | 午夜视频在线观看网址 | 一区二区三区av | caoporn国产精品免费公开 | 中文字幕在线观看国产 | 国产专区在线 | 黄网站涩免费蜜桃网站 | 在线视频成人 | 亚洲欧美综合精品久久成人 | 国产精品资源在线观看 | 欧美日韩黄色一级片 | 免费永久av | 国产午夜精品一区二区三区四区 |