我猜應(yīng)該很多人都有這樣的困惑,就是覺得學(xué)計組有什么用?感覺實際工作過程中用不到,感覺理論學(xué)了個寂寞。這個困惑很正常,因為學(xué)計組這東西主要是為了搞懂計算機是如何工作的,計算機怎么工作的都是被前輩們實現(xiàn)好的,我們一般也不會參與到造計算機這種工作中。但是學(xué)了計組后,你能看到的視角是和別人不一樣的,而這個不一樣的視角就能在一些關(guān)鍵的問題上得到突破。我這里舉個簡單例子,你覺得下面這兩個?for?循環(huán)哪個效率會更高呢?為什么會更高呢?這個問題的關(guān)鍵在于,大家知不知道 CPU Cache 這個東西。我自己曾經(jīng)在還沒有學(xué)計組的時候,只覺得存儲設(shè)備就是我們常見的內(nèi)存和硬盤,學(xué)了后我才發(fā)現(xiàn)還有 CPU Cache 這個東西,而能不能充分利用到這個東西,就決定你程序的性能。CPU Cache 是 CPU 內(nèi)部的一個緩存,它的讀寫速度遠高于內(nèi)存,所以它充當(dāng)內(nèi)存的緩存角色,當(dāng) CPU 要訪問的數(shù)據(jù)命中了 CPU Cache,就不用去從內(nèi)存上找數(shù)據(jù),就像 MySQL 和 Redis 之間的關(guān)系。CPU Cache 一般會分為三層,分別是 L1 Cache、L2 Cache、L3 Cache ,其中 L1 Cache 是各個 CPU 核心獨立的,剩下的 L2 Cache、L3 Cache 是各個核心共享的。CPU Cache 的數(shù)據(jù)是從內(nèi)存中讀取過來的,它是以一小塊一小塊讀取數(shù)據(jù)的,而不是按照單個數(shù)組元素來讀取數(shù)據(jù)的,在 CPU Cache 中的,這樣一小塊一小塊的數(shù)據(jù),稱為 Cache Line(緩存塊)。?如果 L1 Cache Line 大小是 64 字節(jié),也就意味著 L1 Cache 一次載入數(shù)據(jù)的大小是 64 字節(jié)。比如,有一個 int array[100] 的數(shù)組,當(dāng)載入 array[0] 時,由于這個數(shù)組元素的大小在內(nèi)存只占 4 字節(jié),不足 64 字節(jié),CPU 就會順序加載數(shù)組元素到 array[15] ,意味著 array[0]~array[15] 數(shù)組元素都會 被緩存在 CPU Cache 中了,因此當(dāng)下次訪問這些數(shù)組元素時,會直接從 CPU Cache 讀取,而不用再從內(nèi) 存中讀取,大大提高了 CPU 讀取數(shù)據(jù)的性能。CPU Cache 的概念簡單介紹完了,再來說說剛才的 for 循環(huán)問題。我直接說答案:形式一?array[i][j]?執(zhí)行時間比形式二?array[j][i]?快好幾倍。之所以有這么大的差距,是因為二維數(shù)組?array?所占用的內(nèi)存是連續(xù)的,比如長度?N?的指是?2?的話,那么內(nèi)存中的數(shù)組元素的布局順序是這樣的:形式一用?array[i][j]?訪問數(shù)組元素的順序,正是和內(nèi)存中數(shù)組元素存放的順序一致。當(dāng) CPU 訪問?array[0][0]?時,由于該數(shù)據(jù)不在 Cache 中,于是會「順序」把跟隨其后的 3 個元素從內(nèi)存中加載到 CPU Cache,這樣當(dāng) CPU 訪問后面的 3 個數(shù)組元素時,就能在 CPU Cache 中成功地找到數(shù)據(jù),這意味著緩存命中率很高,緩存命中的數(shù)據(jù)不需要訪問內(nèi)存,這便大大提高了代碼的性能。而如果用形式二的?array[j][i]?來訪問,則訪問的順序就是:你可以看到,訪問的方式跳躍式的,而不是順序的,那么如果 N 的數(shù)值很大,那么操作?array[j][i]?時,是沒辦法把?array[j 1][i]?也讀入到 CPU Cache 中的,既然?array[j 1][i]沒有讀取到 CPU Cache,那么就需要從內(nèi)存讀取該數(shù)據(jù)元素了。很明顯,這種不連續(xù)性、跳躍式訪問數(shù)據(jù)元素的方式,可能不能充分利用到了 CPU Cache 的特性,從而代碼的性能不高。那訪問?array[0][0]?元素時,CPU 具體會一次從內(nèi)存中加載多少元素到 CPU Cache 呢?這個問題,在前面我們也提到過,這跟 CPU Cache Line 有關(guān),它表示?CPU Cache 一次性能加載數(shù)據(jù)的大小,可以在 Linux 里通過?coherency_line_size?配置查看 它的大小,通常是 64 個字節(jié)。也就是說,當(dāng) CPU 訪問內(nèi)存數(shù)據(jù)時,如果數(shù)據(jù)不在 CPU Cache 中,則會一次性會連續(xù)加載 64 字節(jié)大小的數(shù)據(jù)到 CPU Cache,那么當(dāng)訪問?array[0][0]?時,由于該元素不足 64 字節(jié),于是就會往后順序讀取?array[0][0]~array[0][15]?到 CPU Cache 中。順序訪問的?array[i][j]?因為利用了這一特點,所以就會比跳躍式訪問的?array[j][i]?要快。因此,遇到這種遍歷數(shù)組的情況時,按照內(nèi)存布局順序訪問,將可以有效的利用 CPU Cache 帶來的好處,這樣我們代碼的性能就會得到很大的提升,CPU Cache 的內(nèi)容遠不止于我說的這些,還有緩存一致性協(xié)議、偽共享、write through 和 write back 的方式等,這些都是非常重要的知識。
第五章:層次化存儲,講的是計算機的存儲層次結(jié)構(gòu),而且重點講的是 CPU Cahe。
計算機組成原理視頻課程
看書覺得很累,也可以結(jié)合視頻一起看,這里推薦哈工大的《計算機組成原理》視頻,在 b 站就可以直接看,大家自己去搜索就可以。看書和看視頻可以相互結(jié)合的,比如你看視頻看了計算機指令的內(nèi)容,然后你可以不用繼續(xù)往下看,可以回到一本書上,看書上對應(yīng)這個章節(jié)的內(nèi)容,這是個很好的學(xué)習(xí)方法,視頻和書籍相輔相成。你要是覺得哈工大的計組課程太難,你可以看王道考研的計算機組成原理的視頻課程,同樣 b 站就可以看。這個視頻雖然是針對考研的,但是也是可以作為學(xué)習(xí)計組的資料,講的內(nèi)容不會太深,適合你快速建立計算機組成原理體系,和梳理計組知識的脈絡(luò)。
其他重合的計組知識都大同小異。CSAPP 的視頻課程是國外老師錄制的,但是在 b 站已經(jīng)有好人幫我們做了中文字幕,看了這視頻,相當(dāng)于在國外上了一門計算機課的感覺。如果你是在校生,有了一定 C 語言基礎(chǔ)后,非常建議你就開始看這本書,有精力也可以做做 CSAPP 的 lab。越早開始看,你的收益就越大,因為當(dāng)計算機體系搭建起來后,你后面再深入每一個課程的時候,你會發(fā)現(xiàn)學(xué)起來會比較輕松些。對于已經(jīng)工作了,但是計算機系統(tǒng)沒有一個清晰認識的讀者,也可以從這本書開始一點一點學(xué)起來,這本書是很厚,但是并不一定要把書完完看完,每個章節(jié)的知識點還是比較獨立的,有關(guān)硬件的章節(jié)我們可以選擇跳過。這就是我學(xué)計組的心得啦。沒學(xué)過計組的同學(xué),可以找個時間補補了,提高下自己的「內(nèi)功」。干就完啦!