談亂序執(zhí)行和內(nèi)存屏障

10多年前的程序員對(duì)處理器亂序執(zhí)行和內(nèi)存屏障應(yīng)該是很熟悉的,但隨著計(jì)算機(jī)技術(shù)突飛猛進(jìn)的發(fā)展,我們離底層原理越來(lái)越遠(yuǎn),這并不是一件壞事,但在有些情況下了解一些底層原理有助于我們更好的工作,比如現(xiàn)代高級(jí)語(yǔ)言多提供了多線程并發(fā)技術(shù),如果不深入下來(lái),那么有些由多線程造成問(wèn)題就很難排查和理解.

今天準(zhǔn)備來(lái)聊聊亂序執(zhí)行技術(shù)和內(nèi)存屏障.為了能讓大多數(shù)人理解,這里省略了很多不影響理解的旁枝末節(jié),但由于我個(gè)人水平有限,如果不妥之處,希望各位指正.


按順執(zhí)行技術(shù)

在開始說(shuō)亂序執(zhí)行之前,得先把按序執(zhí)行說(shuō)一遍.在早期處理器中,處理器執(zhí)行指令的順序就是按照我們編寫匯編代碼的順序執(zhí)行的,換句話說(shuō)此時(shí)處理器指令執(zhí)行順序和我們代碼順序一致,我們稱之為按序執(zhí)行(In Order Execution).我們以燒水泡茶為例來(lái)說(shuō)明按序執(zhí)行的過(guò)程(熟悉的同學(xué)會(huì)想起華羅庚的統(tǒng)籌學(xué)):

  1. 洗水壺
  2. 燒開水
  3. 洗茶壺
  4. 洗茶杯
  5. 拿茶葉
  6. 泡茶

我們假設(shè)每一步代表一條指令的執(zhí)行,此時(shí)從指令1到指令6執(zhí)行的過(guò)程就是我們所說(shuō)的按序執(zhí)行.整個(gè)過(guò)程可以表示為:


這里寫圖片描述

按序執(zhí)行對(duì)于早期處理器而言是一種行之有效的方案,但隨著對(duì)時(shí)間的要求,我們希望上述過(guò)程能夠在最短的時(shí)間內(nèi)執(zhí)行完成,這就促使人們迫切希望找到一種優(yōu)化指令執(zhí)行過(guò)程的方案.考慮上述執(zhí)行過(guò)程,我們發(fā)現(xiàn)洗茶壺這步完全沒(méi)有必要等待燒開水完成,也就是說(shuō)洗茶壺和洗水杯完全可以和燒開水同時(shí)進(jìn)行,這么一來(lái),優(yōu)化過(guò)的流程如圖:


這里寫圖片描述

這種通過(guò)改變?cè)袌?zhí)行順序而減少時(shí)間的執(zhí)行過(guò)程我們被稱之為亂序執(zhí)行,也稱為重排.到現(xiàn)在為止,我們已經(jīng)弄明白了什么是按序執(zhí)行,什么是亂序.那接下來(lái)就看看處理器中的亂序執(zhí)行技術(shù).


亂序執(zhí)行技術(shù)

處理器亂序執(zhí)行

隨著處理器流水線技術(shù)和多核技術(shù)的發(fā)展,目前的高級(jí)處理器通過(guò)提高內(nèi)部邏輯元件的利用率來(lái)提高運(yùn)行速度,通常會(huì)采用亂序執(zhí)行技術(shù).這里的亂序和上面談到燒水煮茶的道理是一樣的.

先來(lái)看一張?zhí)幚砥鞯暮?jiǎn)要結(jié)構(gòu)圖:


這里寫圖片描述

處理器從L1 Cache中取出一批指令,分析找出那些不存在相互依賴的指令,同時(shí)將其發(fā)射到多個(gè)邏輯單元執(zhí)行,比如現(xiàn)在有以下幾條指令:

LDR   R1, [R0]侧但;
ADD   R2, R1, R1咐扭;
ADD   R4,R3伶椿,R3;

通過(guò)分析發(fā)現(xiàn)第二條指令和第一條指令存在依賴關(guān)系,但是和第3條指令無(wú)關(guān),那么處理器就可能將其發(fā)送到兩個(gè)邏輯單元去執(zhí)行,因此上述的指令執(zhí)行流程可能如下:


這里寫圖片描述

可以說(shuō)亂序執(zhí)行技術(shù)是處理器為提高運(yùn)算速度而做出違背代碼原有順序的優(yōu)化.在單核時(shí)代,處理器保證做出的優(yōu)化不會(huì)導(dǎo)致執(zhí)行結(jié)果遠(yuǎn)離預(yù)期目標(biāo),但在多核環(huán)境下卻并非如此.

首先多核時(shí)代,同時(shí)會(huì)有多個(gè)核執(zhí)行指令,每個(gè)核的指令都可能被亂序;另外,處理器還引入了L1,L2等緩存機(jī)制,每個(gè)核都有自己的緩存,這就導(dǎo)致邏輯次序上后寫入內(nèi)存的數(shù)據(jù)未必真的最后寫入.最終帶來(lái)了這么一個(gè)問(wèn)題:如果我們不做任何防護(hù)措施,處理器最終得出的結(jié)果和我們邏輯得出的結(jié)果大不相同.比如我們?cè)谝粋€(gè)核上執(zhí)行數(shù)據(jù)的寫入操作,并在最后寫一個(gè)標(biāo)記用來(lái)表示之前的數(shù)據(jù)已經(jīng)準(zhǔn)備好,然后從另一個(gè)核上通過(guò)判斷這個(gè)標(biāo)志來(lái)判定所需要的數(shù)據(jù)已經(jīng)就緒,這種做法存在風(fēng)險(xiǎn):標(biāo)記位先被寫入,但是之前的數(shù)據(jù)操作卻并未完成(可能是未計(jì)算完成,也可能是數(shù)據(jù)沒(méi)有從處理器緩存刷新到主存當(dāng)中),最終導(dǎo)致另一個(gè)核中使用了錯(cuò)誤的數(shù)據(jù).

編譯器指令重排

除了上述由處理器和緩存引起的亂序之外,現(xiàn)代編譯器同樣提供了亂序優(yōu)化.之所以出現(xiàn)編譯器亂序優(yōu)化其根本原因在于處理器每次只能分析一小塊指令,但編譯器卻能在很大范圍內(nèi)進(jìn)行代碼分析,從而做出更優(yōu)的策略,充分利用處理器的亂序執(zhí)行功能.

亂序的分類

現(xiàn)在來(lái)總結(jié)下所有可能發(fā)生亂序執(zhí)行的情況:

  • 現(xiàn)代處理器采用指令并行技術(shù),在不存在數(shù)據(jù)依賴性的前提下,處理器可以改變語(yǔ)句對(duì)應(yīng)的機(jī)器指令的執(zhí)行順序來(lái)提高處理器執(zhí)行速度
  • 現(xiàn)代處理器采用內(nèi)部緩存技術(shù),導(dǎo)致數(shù)據(jù)的變化不能及時(shí)反映在主存所帶來(lái)的亂序.
  • 現(xiàn)代編譯器為優(yōu)化而重新安排語(yǔ)句的執(zhí)行順序

小結(jié)

盡管我們看到亂序執(zhí)行初始目的是為了提高效率,但是它看來(lái)其好像在這多核時(shí)代不盡人意,其中的某些"自作聰明"的優(yōu)化導(dǎo)致多線程程序產(chǎn)生各種各樣的意外.因此有必要存在一種機(jī)制來(lái)消除亂序執(zhí)行帶來(lái)的壞影響,也就是說(shuō)應(yīng)該允許程序員顯式的告訴處理器對(duì)某些地方禁止亂序執(zhí)行.這種機(jī)制就是所謂內(nèi)存屏障.不同架構(gòu)的處理器在其指令集中提供了不同的指令來(lái)發(fā)起內(nèi)存屏障,對(duì)應(yīng)在編程語(yǔ)言當(dāng)中就是提供特殊的關(guān)鍵字來(lái)調(diào)用處理器相關(guān)的指令.


內(nèi)存屏障

處理器亂序規(guī)則

上面我們說(shuō)了處理器會(huì)發(fā)生指令重排,現(xiàn)在來(lái)簡(jiǎn)單的看看常見處理器允許的重排規(guī)則,換言之就是處理器可以對(duì)那些指令進(jìn)行順序調(diào)整:

處理器 Load-Load Load-Store Store-Store Store-Load 數(shù)據(jù)依賴
x86 N N N Y N
PowerPC Y Y Y Y N
ia64 Y Y Y Y N

表格中的Y表示前后兩個(gè)操作允許重排,N則表示不允許重排.與這些規(guī)則對(duì)應(yīng)是的禁止重排的內(nèi)存屏障.

注意:處理器和編譯都會(huì)遵循數(shù)據(jù)依賴性,不會(huì)改變存在數(shù)據(jù)依賴關(guān)系的兩個(gè)操作的順序.所謂的數(shù)據(jù)依賴性就是如果兩個(gè)操作訪問(wèn)同一個(gè)變量,且這兩個(gè)操作中有一個(gè)是寫操作,那么久可以稱這兩個(gè)操作存在數(shù)據(jù)依賴性.舉個(gè)簡(jiǎn)單例子:

a=100;//write
b=a;//read

或者
a=100;//write
a=2000;//write
或者
a=b;//read
b=12;//write

以上所示的,兩個(gè)操作之間不能發(fā)生重排,這是處理器和編譯所必須遵循的.當(dāng)然這里指的是發(fā)生在單個(gè)處理器或單個(gè)線程中.

內(nèi)存屏障的分類

在開始看一下表格之前,務(wù)必確保自己了解Store和Load指令的含義.簡(jiǎn)單來(lái)說(shuō),Store就是將處理器緩存中的數(shù)據(jù)刷新到內(nèi)存中,而Load則是從內(nèi)存拷貝數(shù)據(jù)到緩存當(dāng)中.

屏障類型 指令示例 說(shuō)明
LoadLoad Barriers Load1;LoadLoad;Load2 該屏障確保Load1數(shù)據(jù)的裝載先于Load2及其后所有裝載指令的的操作
StoreStore Barriers Store1;StoreStore;Store2 該屏障確保Store1立刻刷新數(shù)據(jù)到內(nèi)存(使其對(duì)其他處理器可見)的操作先于Store2及其后所有存儲(chǔ)指令的操作
LoadStore Barriers Load1;LoadStore;Store2 確保Load1的數(shù)據(jù)裝載先于Store2及其后所有的存儲(chǔ)指令刷新數(shù)據(jù)到內(nèi)存的操作
StoreLoad Barriers Store1;StoreLoad;Load1 該屏障確保Store1立刻刷新數(shù)據(jù)到內(nèi)存的操作先于Load2及其后所有裝載裝載指令的操作.它會(huì)使該屏障之前的所有內(nèi)存訪問(wèn)指令(存儲(chǔ)指令和訪問(wèn)指令)完成之后,才執(zhí)行該屏障之后的內(nèi)存訪問(wèn)指令

StoreLoad Barriers同時(shí)具備其他三個(gè)屏障的效果,因此也稱之為全能屏障,是目前大多數(shù)處理器所支持的,但是相對(duì)其他屏障,該屏障的開銷相對(duì)昂貴.在x86架構(gòu)的處理器的指令集中,lock指令可以觸發(fā)StoreLoad Barriers.

現(xiàn)在我們綜合重排規(guī)則和內(nèi)存屏障類型來(lái)說(shuō)明一下.比如x86架構(gòu)的處理器中允許處理器對(duì)Store-Load操作進(jìn)行重排,與之對(duì)應(yīng)有StoreLoad Barriers禁止其重排.


as-if-serial語(yǔ)義

無(wú)論是處理器還是編譯器,不管怎么重排都要保證(單線程)程序的執(zhí)行結(jié)果不能被改變,這就是as-if-serial語(yǔ)義.比如燒水煮茶的最終結(jié)果永遠(yuǎn)是煮茶,而不能變成燒水.為了遵循這種語(yǔ)義,處理器和編譯器不能對(duì)存在數(shù)據(jù)依賴性的操作進(jìn)行重排,因?yàn)檫@種重排會(huì)改變操作結(jié)果,比如對(duì):

a=100;//write
b=a;//read

重排為:

b=a;
a=100;

此時(shí)b的值就是不正確的.如果不存在操作之間不存在數(shù)據(jù)依賴,那么這些操作就可能被處理器或編譯器進(jìn)行重排,比如:

a=10;
b=200;
result=a*b;

它們之間的依賴關(guān)系如圖:


這里寫圖片描述

由于a=10b=200之間不存在依賴關(guān)系,因此編譯器或處理可以這兩兩個(gè)操作進(jìn)行重排,因此最終執(zhí)行順序可能有以下兩種情況:

這里寫圖片描述

但無(wú)論哪種執(zhí)行順序,最終的結(jié)果都是對(duì)的.

正是因?yàn)閍s-if-serial的存在,我們?cè)诰帉憜尉€程程序時(shí)會(huì)覺(jué)得好像它就是按代碼的順序執(zhí)行的,這讓我們可以不必關(guān)心重排的影響.換句話說(shuō),如果你從來(lái)沒(méi)有編寫多線程程序的需求,那就不需要關(guān)注今天我所說(shuō)的一切.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末账劲,一起剝皮案震驚了整個(gè)濱河市什乙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖腰涧,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異紊浩,居然都是意外死亡窖铡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門坊谁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)费彼,“玉大人,你說(shuō)我怎么就攤上這事口芍」坎” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵鬓椭,是天一觀的道長(zhǎng)颠猴。 經(jīng)常有香客問(wèn)我,道長(zhǎng)小染,這世上最難降的妖魔是什么翘瓮? 我笑而不...
    開封第一講書人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮裤翩,結(jié)果婚禮上资盅,老公的妹妹穿的比我還像新娘。我一直安慰自己踊赠,他們只是感情好呵扛,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筐带,像睡著了一般择份。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上烫堤,一...
    開封第一講書人閱讀 49,842評(píng)論 1 290
  • 那天荣赶,我揣著相機(jī)與錄音,去河邊找鬼鸽斟。 笑死拔创,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的富蓄。 我是一名探鬼主播剩燥,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了灭红?” 一聲冷哼從身側(cè)響起侣滩,我...
    開封第一講書人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎变擒,沒(méi)想到半個(gè)月后君珠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡娇斑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年策添,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片毫缆。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡唯竹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出苦丁,到底是詐尸還是另有隱情浸颓,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布旺拉,位于F島的核電站产上,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏账阻。R本人自食惡果不足惜蒂秘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望淘太。 院中可真熱鬧姻僧,春花似錦、人聲如沸蒲牧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)冰抢。三九已至松嘶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間挎扰,已是汗流浹背翠订。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留遵倦,地道東北人尽超。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像梧躺,于是被迫代替她去往敵國(guó)和親似谁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • 我們一起來(lái)討論討論java內(nèi)存模型。理解內(nèi)存模型對(duì)多線程編程無(wú)疑是有好處的巩踏。 java代碼是如何跑起來(lái)的 java...
    caixiangwang閱讀 572評(píng)論 0 3
  • 從三月份找實(shí)習(xí)到現(xiàn)在秃诵,面了一些公司,掛了不少塞琼,但最終還是拿到小米菠净、百度、阿里屈梁、京東嗤练、新浪榛了、CVTE在讶、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,213評(píng)論 11 349
  • 6:20:起床 6:30-7:30跑步/在家鍛煉腰腹,練氣音霜大,繞口令构哺,朗讀美文 上班時(shí)間可以收聽:音樂(lè),演講战坤,理財(cái)...
    簡(jiǎn)書米閱讀 386評(píng)論 0 0
  • 不管你有多么真誠(chéng)途茫, 遇到懷疑你的人碟嘴, 你就是謊言。 不管你有多么單純囊卜, 遇到復(fù)雜的人娜扇, 你就是有心計(jì)。 不管你多么...
    223了凡閱讀 391評(píng)論 0 0
  • 謝謝今天到場(chǎng)的所有小伙伴栅组,首先要謝謝你們出現(xiàn)在我的生命中雀瓢,再次感謝你們蒞臨活動(dòng)現(xiàn)場(chǎng)。唯有給大家鞠一躬玉掸,才能表達(dá)我的...
    派小欣閱讀 1,816評(píng)論 15 11