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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

漫游处理器缓存效应

發(fā)布時(shí)間:2024/4/11 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 漫游处理器缓存效应 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

點(diǎn)擊上方“朱小廝的博客”,選擇“設(shè)為星標(biāo)”

后臺(tái)回復(fù)"書",獲取

后臺(tái)回復(fù)“k8s”,可領(lǐng)取k8s資料

CPU Cache一直是理解計(jì)算機(jī)體系架構(gòu)的重要知識(shí)點(diǎn),也是并發(fā)編程設(shè)計(jì)中的技術(shù)難點(diǎn),而且相關(guān)參考資料如同過江之鯽,浩瀚繁星,閱之如臨深淵,味同嚼蠟,三言兩語難以入門。正好網(wǎng)上有人推薦了微軟大牛Igor Ostrovsky一篇博文《漫游處理器緩存效應(yīng)》?( https://coolshell.cn/articles/10249.html ),文章不僅僅用7個(gè)最簡單的源碼示例就將CPU cache的原理娓娓道來,還附加圖表量化分析做數(shù)學(xué)上的佐證,個(gè)人感覺這種案例教學(xué)的切入方式絕對(duì)是俺的菜,故而忍不住貿(mào)然譯之,以饗列位看官。

大多數(shù)讀者都知道cache是一種快速小型的內(nèi)存,用以存儲(chǔ)最近訪問內(nèi)存位置。這種描述合理而準(zhǔn)確,但是更多地了解一些處理器緩存工作中的“煩人”細(xì)節(jié)對(duì)于理解程序運(yùn)行性能有很大幫助。

在這篇博客中,我將運(yùn)用代碼示例來詳解 cache工作的方方面面,以及對(duì)現(xiàn)實(shí)世界中程序運(yùn)行產(chǎn)生的影響。

下面的例子都是用C#寫的,但語言的選擇同程序運(yùn)行狀況以及得出的結(jié)論幾乎沒什么影響。

示例1:內(nèi)存訪問和運(yùn)行

你認(rèn)為相較于循環(huán)1,循環(huán)2會(huì)運(yùn)行更快嗎?

int[]?arr?=?new?int[64?*?1024?*?1024];//?Loop?1 for?(int?i?=?0;?i?<?arr.Length;?i++)?arr[i]?*=?3;//?Loop?2 for?(int?i?=?0;?i?<?arr.Length;?i?+=?16)?arr[i]?*=?3;

第一個(gè)循環(huán)將數(shù)組的每個(gè)值乘3,第二個(gè)循環(huán)將每16個(gè)值乘3,第二個(gè)循環(huán)只做了第一個(gè)約6%的工作,但在現(xiàn)代機(jī)器上,兩者幾乎運(yùn)行相同時(shí)間:在我機(jī)器上分別是80毫秒和78毫秒。

兩個(gè)循環(huán)花費(fèi)相同時(shí)間的原因跟內(nèi)存有關(guān)。**循環(huán)執(zhí)行時(shí)間長短由數(shù)組的內(nèi)存訪問次數(shù)決定的,而非整型數(shù)的乘法運(yùn)算次數(shù)。**經(jīng)過下面對(duì)第二個(gè)示例的解釋,你會(huì)發(fā)現(xiàn)硬件對(duì)這兩個(gè)循環(huán)的主存訪問次數(shù)是相同的。

示例2:緩存行的影響

讓我們進(jìn)一步探索這個(gè)例子。我們將嘗試不同的循環(huán)步長,而不僅僅是1和16。

for?(int?i?=?0;?i?<?arr.Length;?i?+=?K)?arr[i]?*=?3;

下圖為該循環(huán)在不同步長(K)下的運(yùn)行時(shí)間:

注意當(dāng)步長在1到16范圍內(nèi),循環(huán)運(yùn)行時(shí)間幾乎不變。但從16開始,每次步長加倍,運(yùn)行時(shí)間減半。

背后的原因是今天的CPU不再是按字節(jié)訪問內(nèi)存,而是以64字節(jié)為單位的塊(chunk)拿取,稱為一個(gè)緩存行(cache line)。當(dāng)你讀一個(gè)特定的內(nèi)存地址,整個(gè)緩存行將從主存換入緩存,并且訪問同一個(gè)緩存行內(nèi)的其它值的開銷是很小的。

由于16個(gè)整型數(shù)占用64字節(jié)(一個(gè)緩存行),for循環(huán)步長在1到16之間必定接觸到相同數(shù)目的緩存行:即數(shù)組中所有的緩存行。當(dāng)步長為32,我們只有大約每兩個(gè)緩存行接觸一次,當(dāng)步長為64,只有每四個(gè)接觸一次。

理解緩存行對(duì)某些類型的程序優(yōu)化而言可能很重要。比如,數(shù)據(jù)字節(jié)對(duì)齊可能決定一次操作接觸1個(gè)還是2個(gè)緩存行。那上面的例子來說,很顯然操作不對(duì)齊的數(shù)據(jù)將損失一半性能。

示例3:L1和L2緩存大小

今天的計(jì)算機(jī)具有兩級(jí)或三級(jí)緩存,通常叫做L1、L2以及可能的L3(譯者注:如果你不明白什么叫二級(jí)緩存,可以參考這篇精悍的博文)。如果你想知道不同緩存的大小,你可以使用系統(tǒng)內(nèi)部工具CoreInfo,或者Windows API調(diào)用GetLogicalProcessorInfo。兩者都將告訴你緩存行以及緩存本身的大小。

在我的機(jī)器上,CoreInfo顯示我有一個(gè)32KB的L1數(shù)據(jù)緩存,一個(gè)32KB的L1指令緩存,還有一個(gè)4MB大小的L2數(shù)據(jù)緩存。L1緩存是處理器獨(dú)享的,L2緩存是成對(duì)處理器共享的。

Logical Processor to Cache Map: — Data Cache 0, Level 1, 32 KB, Assoc 8, LineSize 64 — Instruction Cache 0, Level 1, 32 KB, Assoc 8, LineSize 64 -– Data Cache 1, Level 1, 32 KB, Assoc 8, LineSize 64 -– Instruction Cache 1, Level 1, 32 KB, Assoc 8, LineSize 64 – Unified Cache 0, Level 2, 4 MB, Assoc 16, LineSize 64 –- Data Cache 2, Level 1, 32 KB, Assoc 8, LineSize 64 –- Instruction Cache 2, Level 1, 32 KB, Assoc 8, LineSize 64 — Data Cache 3, Level 1, 32 KB, Assoc 8, LineSize 64 — Instruction Cache 3, Level 1, 32 KB, Assoc 8, LineSize 64 –** Unified Cache 1, Level 2, 4 MB, Assoc 16, LineSize 64

(譯者注:作者平臺(tái)是四核機(jī),所以L1編號(hào)為0~3,數(shù)據(jù)/指令各一個(gè),L2只有數(shù)據(jù)緩存,兩個(gè)處理器共享一個(gè),編號(hào)0~1。關(guān)聯(lián)性字段在后面例子說明。)

讓我們通過一個(gè)實(shí)驗(yàn)來驗(yàn)證這些數(shù)字。遍歷一個(gè)整型數(shù)組,每16個(gè)值自增1——一種節(jié)約的方式改變每個(gè)緩存行。當(dāng)遍歷到最后一個(gè)值,就重頭開始。我們將使用不同的數(shù)組大小,可以看到當(dāng)數(shù)組溢出一級(jí)緩存大小,程序運(yùn)行的性能將急劇滑落。

int?steps?=?64?*?1024?*?1024; //?Arbitrary?number?of?steps int?lengthMod?=?arr.Length?-?1; for?(int?i?=?0;?i?<?steps;?i++) {arr[(i?*?16)?&?lengthMod]++;?//?(x?&?lengthMod)?is?equal?to?(x?%?arr.Length) }

下圖是運(yùn)行時(shí)間圖表:

你可以看到在32KB和4MB之后性能明顯滑落——正好是我機(jī)器上L1和L2緩存大小。

示例4:指令級(jí)別并發(fā)

現(xiàn)在讓我們看一看不同的東西。下面兩個(gè)循環(huán)中你以為哪個(gè)較快?

int?steps?=?256?*?1024?*?1024; int[]?a?=?new?int[2];//?Loop?1 for?(int?i=0;?i<steps;?i++)?{?a[0]++;?a[0]++;?}//?Loop?2 for?(int?i=0;?i<steps;?i++)?{?a[0]++;?a[1]++;?}

結(jié)果是第二個(gè)循環(huán)約比第一個(gè)快一倍,至少在我測(cè)試的機(jī)器上。為什么呢?這跟兩個(gè)循環(huán)體內(nèi)的操作指令依賴性有關(guān)。

第一個(gè)循環(huán)體內(nèi),操作做是相互依賴的(譯者注:下一次依賴于前一次):

但第二個(gè)例子中,依賴性就不同了:

現(xiàn)代處理器中對(duì)不同部分指令擁有一點(diǎn)并發(fā)性(譯者注:跟流水線有關(guān),比如Pentium處理器就有U/V兩條流水線,后面說明)。這使得CPU在同一時(shí)刻訪問L1兩處內(nèi)存位置,或者執(zhí)行兩次簡單算術(shù)操作。在第一個(gè)循環(huán)中,處理器無法發(fā)掘這種指令級(jí)別的并發(fā)性,但第二個(gè)循環(huán)中就可以。

[原文更新]:許多人在reddit上詢問有關(guān)編譯器優(yōu)化的問題,像{ a[0]++; a[0]++; }能否優(yōu)化為{ a[0]+=2; }。實(shí)際上,C#編譯器和CLR JIT沒有做優(yōu)化——在數(shù)組訪問方面。我用release模式編譯了所有測(cè)試(使用優(yōu)化選項(xiàng)),但我查詢了JIT匯編語言證實(shí)優(yōu)化并未影響結(jié)果。

示例5:緩存關(guān)聯(lián)性

緩存設(shè)計(jì)的一個(gè)關(guān)鍵決定是確保每個(gè)主存塊(chunk)能夠存儲(chǔ)在任何一個(gè)緩存槽里,或者只是其中一些(譯者注:此處一個(gè)槽位就是一個(gè)緩存行)。

有三種方式將緩存槽映射到主存塊中:

  • 直接映射(Direct mapped cache) 每個(gè)內(nèi)存塊只能映射到一個(gè)特定的緩存槽。一個(gè)簡單的方案是通過塊索引chunk_index映射到對(duì)應(yīng)的槽位(chunk_index % cache_slots)。被映射到同一內(nèi)存槽上的兩個(gè)內(nèi)存塊是不能同時(shí)換入緩存的。(譯者注:chunk_index可以通過物理地址/緩存行字節(jié)計(jì)算得到)

  • N路組關(guān)聯(lián)(N-way set associative cache) 每個(gè)內(nèi)存塊能夠被映射到N路特定緩存槽中的任意一路。比如一個(gè)16路緩存,每個(gè)內(nèi)存塊能夠被映射到16路不同的緩存槽。一般地,具有一定相同低bit位地址的內(nèi)存塊將共享16路緩存槽。(譯者注:相同低位地址表明相距一定單元大小的連續(xù)內(nèi)存)

  • 完全關(guān)聯(lián)(Fully associative cache) 每個(gè)內(nèi)存塊能夠被映射到任意一個(gè)緩存槽。操作效果上相當(dāng)于一個(gè)散列表。

  • 直接映射緩存會(huì)引發(fā)沖突——當(dāng)多個(gè)值競(jìng)爭同一個(gè)緩存槽,它們將相互驅(qū)逐對(duì)方,導(dǎo)致命中率暴跌。另一方面,完全關(guān)聯(lián)緩存過于復(fù)雜,并且硬件實(shí)現(xiàn)上昂貴。N路組關(guān)聯(lián)是處理器緩存的典型方案,它在電路實(shí)現(xiàn)簡化和高命中率之間取得了良好的折中。

    舉個(gè)例子,4MB大小的L2緩存在我機(jī)器上是16路關(guān)聯(lián)。所有64字節(jié)內(nèi)存塊將分割為不同組,映射到同一組的內(nèi)存塊將競(jìng)爭L2緩存里的16路槽位。

    L2緩存有65,536個(gè)緩存行(譯者注:4MB/64),每個(gè)組需要16路緩存行,我們將獲得4096個(gè)集。這樣一來,塊屬于哪個(gè)組取決于塊索引的低12位bit(2^12=4096)。因此緩存行對(duì)應(yīng)的物理地址凡是以262,144字節(jié)(4096*64)的倍數(shù)區(qū)分的,將競(jìng)爭同一個(gè)緩存槽。我機(jī)器上最多維持16個(gè)這樣的緩存槽。(譯者注:請(qǐng)結(jié)合上圖中的2路關(guān)聯(lián)延伸理解,一個(gè)塊索引對(duì)應(yīng)64字節(jié),chunk0對(duì)應(yīng)組0中的任意一路槽位,chunk1對(duì)應(yīng)組1中的任意一路槽位,以此類推chunk4095對(duì)應(yīng)組4095中的任意一路槽位,chunk0和chunk4096地址的低12bit是相同的,所以chunk4096、chunk8192將同chunk0競(jìng)爭組0中的槽位,它們之間的地址相差262,144字節(jié)的倍數(shù),而最多可以進(jìn)行16次競(jìng)爭,否則就要驅(qū)逐一個(gè)chunk)。

    為了使得緩存關(guān)聯(lián)效果更加明了,我需要重復(fù)地訪問同一組中的16個(gè)以上的元素,通過如下方法證明:

    public?static?long?UpdateEveryKthByte(byte[]?arr,?int?K) {Stopwatch?sw?=?Stopwatch.StartNew();const?int?rep?=?1024*1024;?//?Number?of?iterations?–?arbitraryint?p?=?0;for?(int?i?=?0;?i?<?rep;?i++){arr[p]++;p?+=?K;if?(p?>=?arr.Length)?p?=?0;}sw.Stop();return?sw.ElapsedMilliseconds; }

    該方法每次在數(shù)組中迭代K個(gè)值,當(dāng)?shù)竭_(dá)末尾時(shí)從頭開始。循環(huán)在運(yùn)行足夠長(2^20次)之后停止。

    我使用不同的數(shù)組大小(每次增加1MB)和不同的步長傳入U(xiǎn)pdateEveryKthByte()。以下是繪制的圖表,藍(lán)色代表運(yùn)行較長時(shí)間,白色代表較短時(shí)間:

    藍(lán)色區(qū)域(較長時(shí)間)表明當(dāng)我們重復(fù)數(shù)組迭代時(shí),更新的值無法同時(shí)放在緩存中。淺藍(lán)色區(qū)域?qū)?yīng)80毫秒,白色區(qū)域?qū)?yīng)10毫秒。

    讓我們來解釋一下圖表中藍(lán)色部分:

    1.為何有垂直線?垂直線表明步長值過多接觸到同一組中內(nèi)存位置(大于16次)。在這些次數(shù)里,我的機(jī)器無法同時(shí)將接觸過的值放到16路關(guān)聯(lián)緩存中。

    一些糟糕的步長值為2的冪:256和512。舉個(gè)例子,考慮512步長遍歷8MB數(shù)組,存在32個(gè)元素以相距262,144字節(jié)空間分布,所有32個(gè)元素都會(huì)在循環(huán)遍歷中更新到,因?yàn)?12能夠整除262,144(譯者注:此處一個(gè)步長代表一個(gè)字節(jié))。

    由于32大于16,這32個(gè)元素將一直競(jìng)爭緩存里的16路槽位。

    (譯者注:為何512步長的垂直線比256步長顏色更深?在同樣足夠多的步數(shù)下,512比256訪問到存在競(jìng)爭的塊索引次數(shù)多一倍。比如跨越262,144字節(jié)邊界512需要512步,而256需要1024步。那么當(dāng)步數(shù)為2^20時(shí),512訪問了2048次存在競(jìng)爭的塊而256只有1024次。最差情況下步長為262,144的倍數(shù),因?yàn)槊看窝h(huán)都會(huì)引發(fā)一個(gè)緩存行驅(qū)逐。)

    有些不是2的冪的步長運(yùn)行時(shí)間長僅僅是運(yùn)氣不好,最終訪問到的是同一組中不成比例的許多元素,這些步長值同樣顯示為藍(lán)線。

    2.為何垂直線在4MB數(shù)組長度的地方停止?因?yàn)閷?duì)于小于等于4MB的數(shù)組,16路關(guān)聯(lián)緩存相當(dāng)于完全關(guān)聯(lián)緩存。

    一個(gè)16路關(guān)聯(lián)緩存最多能夠維護(hù)16個(gè)以262,144字節(jié)分隔的緩存行,4MB內(nèi)組17或更多的緩存行都沒有對(duì)齊在262,144字節(jié)邊界上,因?yàn)?6*262,144=4,194,304。

    3.為何左上角出現(xiàn)藍(lán)色三角?在三角區(qū)域內(nèi),我們無法在緩存中同時(shí)存放所有必要的數(shù)據(jù),不是出于關(guān)聯(lián)性,而僅僅是因?yàn)長2緩存大小所限。

    舉個(gè)例子,考慮步長128遍歷16MB數(shù)組,數(shù)組中每128字節(jié)更新一次,這意味著我們一次接觸兩個(gè)64字節(jié)內(nèi)存塊。為了存儲(chǔ)16MB數(shù)組中每兩個(gè)緩存行,我們需要8MB大小緩存。但我的機(jī)器中只有4MB緩存(譯者注:這意味著必然存在沖突從而延時(shí))。

    即使我機(jī)器中4MB緩存是全關(guān)聯(lián),仍無法同時(shí)存放8MB數(shù)據(jù)。

    4.為何三角最左邊部分是褪色的?注意左邊0~64字節(jié)部分——正好一個(gè)緩存行!就像上面示例1和2所說,額外訪問相同緩存行的數(shù)據(jù)幾乎沒有開銷。比如說,步長為16字節(jié),它需要4步到達(dá)下一個(gè)緩存行,也就是說4次內(nèi)存訪問只有1次開銷。

    在相同循環(huán)次數(shù)下的所有測(cè)試用例中,采取省力步長的運(yùn)行時(shí)間來得短。

    將圖表延伸后的模型:

    緩存關(guān)聯(lián)性理解起來有趣而且確能被證實(shí),但對(duì)于本文探討的其它問題比起來,它肯定不會(huì)是你編程時(shí)所首先需要考慮的問題。

    示例6:緩存行的偽共享(false-sharing)

    在多核機(jī)器上,緩存遇到了另一個(gè)問題——一致性。不同的處理器擁有完全或部分分離的緩存。在我的機(jī)器上,L1緩存是分離的(這很普遍),而我有兩對(duì)處理器,每一對(duì)共享一個(gè)L2緩存。這隨著具體情況而不同,如果一個(gè)現(xiàn)代多核機(jī)器上擁有多級(jí)緩存,那么快速小型的緩存將被處理器獨(dú)占。

    當(dāng)一個(gè)處理器改變了屬于它自己緩存中的一個(gè)值,其它處理器就再也無法使用它自己原來的值,因?yàn)槠鋵?duì)應(yīng)的內(nèi)存位置將被刷新(invalidate)到所有緩存。而且由于緩存操作是以緩存行而不是字節(jié)為粒度,所有緩存中整個(gè)緩存行將被刷新!

    為證明這個(gè)問題,考慮如下例子:

    private?static?int[]?s_counter?=?new?int[1024]; private?void?UpdateCounter(int?position) {for?(int?j?=?0;?j?<?100000000;?j++){s_counter[position]?=?s_counter[position]?+?3;} }

    在我的四核機(jī)上,如果我通過四個(gè)線程傳入?yún)?shù)0,1,2,3并調(diào)用UpdateCounter,所有線程將花費(fèi)4.3秒。

    另一方面,如果我傳入16,32,48,64,整個(gè)操作進(jìn)花費(fèi)0.28秒!

    為何會(huì)這樣?第一個(gè)例子中的四個(gè)值很可能在同一個(gè)緩存行里,每次一個(gè)處理器增加計(jì)數(shù),這四個(gè)計(jì)數(shù)所在的緩存行將被刷新,而其它處理器在下一次訪問它們各自的計(jì)數(shù)(譯者注:注意數(shù)組是private屬性,每個(gè)線程獨(dú)占)將失去命中(miss)一個(gè)緩存。這種多線程行為有效地禁止了緩存功能,削弱了程序性能。

    示例7:硬件復(fù)雜性

    即使你懂得了緩存的工作基礎(chǔ),有時(shí)候硬件行為仍會(huì)使你驚訝。不用處理器在工作時(shí)有不同的優(yōu)化、探試和微妙的細(xì)節(jié)。

    有些處理器上,L1緩存能夠并發(fā)處理兩路訪問,如果訪問是來自不同的存儲(chǔ)體,而對(duì)同一存儲(chǔ)體的訪問只能串行處理。而且處理器聰明的優(yōu)化策略也會(huì)使你感到驚訝,比如在偽共享的例子中,以前在一些沒有微調(diào)的機(jī)器上運(yùn)行表現(xiàn)并不良好,但我家里的機(jī)器能夠?qū)ψ詈唵蔚睦舆M(jìn)行優(yōu)化來減少緩存刷新。

    下面是一個(gè)“硬件怪事”的奇怪例子:

    private?static?int?A,?B,?C,?D,?E,?F,?G; private?static?void?Weirdness() {for?(int?i?=?0;?i?<?200000000;?i++){//?do?something...} }

    當(dāng)我在循環(huán)體內(nèi)進(jìn)行三種不同操作,我得到如下運(yùn)行時(shí)間:

    操作 時(shí)間 A++; B++; C++; D++; 719 ms A++; C++; E++; G++; 448 ms A++; C++; 518 ms

    增加A,B,C,D字段比增加A,C,E,G字段花費(fèi)更長時(shí)間,更奇怪的是,增加A,C兩個(gè)字段比增加A,C,E,G執(zhí)行更久!

    我無法肯定這些數(shù)字背后的原因,但我懷疑這跟存儲(chǔ)體有關(guān),如果有人能夠解釋這些數(shù)字,我將洗耳恭聽。

    這個(gè)例子的教訓(xùn)是,你很難完全預(yù)測(cè)硬件的行為。你可以預(yù)測(cè)很多事情,但最終,衡量及驗(yàn)證你的假設(shè)非常重要。

    關(guān)于第7個(gè)例子的一個(gè)回帖

    Goz:我詢問Intel的工程師最后的例子,得到以下答復(fù):

    “很顯然這涉及到執(zhí)行單元里指令是怎樣終止的,機(jī)器處理存儲(chǔ)-命中-加載的速度,以及如何快速且優(yōu)雅地處理試探性執(zhí)行的循環(huán)展開(比如是否由于內(nèi)部沖突而多次循環(huán))。但這意味著你需要非常細(xì)致的流水線跟蹤器和模擬器才能弄明白。在紙上預(yù)測(cè)流水線里的亂序指令是無比困難的工作,就算是設(shè)計(jì)芯片的人也一樣。對(duì)于門外漢來說,沒門,抱歉!”

    P.S.個(gè)人感悟——局部性原理和流水線并發(fā)

    程序的運(yùn)行存在時(shí)間和空間上的局部性,前者是指只要內(nèi)存中的值被換入緩存,今后一段時(shí)間內(nèi)會(huì)被多次引用,后者是指該內(nèi)存附近的值也被換入緩存。如果在編程中特別注意運(yùn)用局部性原理,就會(huì)獲得性能上的回報(bào)。

    比如C語言中應(yīng)該盡量減少靜態(tài)變量的引用,這是因?yàn)殪o態(tài)變量存儲(chǔ)在全局?jǐn)?shù)據(jù)段,在一個(gè)被反復(fù)調(diào)用的函數(shù)體內(nèi),引用該變量需要對(duì)緩存多次換入換出,而如果是分配在堆棧上的局部變量,函數(shù)每次調(diào)用CPU只要從緩存中就能找到它了,因?yàn)槎褩5闹貜?fù)利用率高。

    再比如**循環(huán)體內(nèi)的代碼要盡量精簡,**因?yàn)榇a是放在指令緩存里的,而指令緩存都是一級(jí)緩存,只有幾K字節(jié)大小,如果對(duì)某段代碼需要多次讀取,而這段代碼又跨越一個(gè)L1緩存大小,那么緩存優(yōu)勢(shì)將蕩然無存。

    關(guān)于CPU的流水線(pipeline)并發(fā)性簡單說說,Intel Pentium處理器有兩條流水線U和V,每條流水線可各自獨(dú)立地讀寫緩存,所以可以在一個(gè)時(shí)鐘周期內(nèi)同時(shí)執(zhí)行兩條指令。但這兩條流水線不是對(duì)等的,U流水線可以處理所有指令集,V流水線只能處理簡單指令。

    CPU指令通常被分為四類,第一類是常用的簡單指令,像mov, nop, push, pop, add, sub, and, or, xor, inc, dec, cmp, lea,可以在任意一條流水線執(zhí)行,只要相互之間不存在依賴性,完全可以做到指令并發(fā)。

    第二類指令需要同別的流水線配合,像一些進(jìn)位和移位操作,這類指令如果在U流水線中,那么別的指令可以在V流水線并發(fā)運(yùn)行,如果在V流水線中,那么U流水線是暫停的。

    第三類指令是一些跳轉(zhuǎn)指令,如cmp,call以及條件分支,它們同第二類相反,當(dāng)工作在V流水線時(shí)才能通U流水線協(xié)作,否則只能獨(dú)占CPU。

    第四類指令是其它復(fù)雜的指令,一般不常用,因?yàn)樗鼈兌贾荒塥?dú)占CPU。

    如果是匯編級(jí)別編程,要達(dá)到指令級(jí)別并發(fā),必須要注重指令之間的配對(duì)。盡量使用第一類指令,避免第四類,還要在順序上減少上下文依賴。

    想知道更多?掃描下面的二維碼關(guān)注我后臺(tái)回復(fù)"技術(shù)",加入技術(shù)群后臺(tái)回復(fù)“k8s”,可領(lǐng)取k8s資料【精彩推薦】
    • ClickHouse到底是什么?為什么如此牛逼!

    • 原來ElasticSearch還可以這么理解

    • 面試官:InnoDB中一棵B+樹可以存放多少行數(shù)據(jù)?

    • 架構(gòu)之道:分離業(yè)務(wù)邏輯和技術(shù)細(xì)節(jié)

    • 星巴克不使用兩階段提交

    • 面試官:Redis新版本開始引入多線程,談?wù)勀愕目捶?#xff1f;

    • 喜馬拉雅自研網(wǎng)關(guān)架構(gòu)演進(jìn)過程

    • 收藏:存儲(chǔ)知識(shí)全面總結(jié)

    • 微博千萬級(jí)規(guī)模高性能高并發(fā)的網(wǎng)絡(luò)架構(gòu)設(shè)計(jì)

    總結(jié)

    以上是生活随笔為你收集整理的漫游处理器缓存效应的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。