半夜,滴滴司機問我會LRU嗎?
1
宮本走出公司的時候,已經將近半夜。此時天上的月亮仍舊散發著清冷的幽光,無情地審視著大地。
最近公司業務繁忙,大批技術人員都被迫加班到很晚。宮本正是這些技術人員中的一份子,從他開始寫代碼到如今,已經將近五年。
「需求根本做不完,這幾天連續加班要吐了。」宮本小聲嘟囔了一句,掏出手機準備叫個網約車。
宮本所在的公司是互聯網行業,號稱是行業內發展迅猛的獨角獸。但是宮本在其中工作得并不順心;一方面是由于技術工作的壓力太大,二來也是因為公司規模比較大,個人的成就感無法得到充分的滿足。
半夜的網約車基本不用排隊,不到五分鐘,一輛黑色的吉利帝豪便沿著定位開到了公司大門。一上車,宮本就從背包中掏出了筆記本電腦,打算利用路上的時間再補補技術基礎。
打開筆記本,內嵌的鍵盤后背燈和屏幕同時亮了起來,照亮了宮本有點疲倦的臉龐。屏幕上開著一個網頁,顯示的是「CPU緩存」之類的字樣,底下還配有長段的圖文解析。
關于CPU 緩存的相關知識,宮本早在大學期間就已經學過。只不過年代久遠加上也沒有時常回顧,現在也忘得差不多了。這回是打算將計算機組成的基礎知識再溫習一遍,以應對之后可能的跳槽。
「CPU緩存相關的知識,重點應該在于計算機存儲系統的層次結構、地址映像方法和緩存替換算法之類的吧。」宮本低頭喃喃自語道,卻沒注意到前方的司機微微瞟了一眼后視鏡。
說回來,宮本自上車起注意力就沒落在司機上,心思還沒從工作狀態中緩過來。事實上,司機外形跟宮本倒有些相似,只不過看起來年紀更大一些,大概35左右。同時若隱若現的發際線都無意間展露出中年人的危機。
只不過宮本也無暇顧及前方這個與自己有些神似的男人了,一頭撲進了學習中。
2
CPU 緩存「Cache」指的訪問速度比一般內存快得多的高速存儲器,主要是為了解決 CPU 運算速率與內存讀寫速率不匹配的問題。因為 CPU 的運算速率比內存讀寫速率快得多,當 CPU 需要向內存請求數據或者寫入數據時,就需要一直等待內存緩慢的讀寫。在這個過程中,CPU 的高速運算能力就無法得到充分發揮。
「所以緩存的作用就像是 CPU 和內存之間進行數據緩沖的橋梁。」宮本若有所思。
緩存中的數據是內存中的一部分,這一部分數據被認為是 CPU 在短時間內會即將訪問到的數據。當 CPU 在調用數據時,會先從緩存中調用,而不直接通過內存。當在緩存中找到想要的數據時,就會立即讀取并返回給 CPU 處理;如果沒有找到,就以相對較慢的速度從內存中讀取,同時將這個數據所在的數據塊都調入緩存中,方便下次對該數據塊的讀取。
「這樣可行主要還是因為局部性原理吧。」宮本漸漸找回了上大學時的記憶。他記得程序在運行時對內存的訪問會呈現出局部性的特征,這種特征表現在使用了某一個數據時,往往該數據附近的數據會有更高的概率在下次被使用。
「那緩存是如何發揮作用的呢?」宮本有些不太記得緩存的組成,埋頭繼續研究。前座的司機時不時通過后視鏡瞥一瞥鉆心學習的宮本,不經意間眼神里流露出一種夾雜著無奈和釋然的復雜神情。
首先來講講計算機存儲系統的層次結構。一般而言,通用計算機的存儲層級分為四層,分別為 CPU 內部寄存器、高速緩存、主存儲器和輔存儲器。主存可以看作是一般而言的內存,而輔存又可根據是否與計算機相連分為聯機和脫機兩種類型。
計算機存儲系統的層次結構
從層次結構上可以看出,高速緩存處于第二層次,起到承上啟下的作用。而為了能夠與 CPU 進行高速交互,同時與主存進行數據的交換,高速緩存的結構也十分具有個人特色。
高速緩存并不是一中概念上的緩存,而是由特定 SRAM 組成的物理芯片。在現代 CPU 中,大量的空間都已經被 SRAM 占據。下圖是一張Intel CPU的放大圖片,紅色框標出的就是其中一部分高速緩存。
SRAM:SRAM是指靜態隨機存儲器,它具有靜止存取的功能,也就是可以不需要刷新電路就保存內部的數據。性能高,功耗小,速度快,價格高。
Intel CPU,圖源網絡,侵刪
高速緩存一般由兩部分組成,控制部分和存儲器部分。控制部分是用來判斷 CPU 所請求的數據是否保存在存儲器中,如果在,則稱為命中;若不在則沒有命中。
CPU高速緩存組成
當 CPU 命中緩存時,即可直接對高速緩存的存儲器進行尋址;未命中時,則需要根據緩存替換算法將主存中的數據塊替換到高速緩存的存儲器中。
3
「接下來是地址映像方法了吧。」宮本還沒從緩存的組成結構中緩過神來,就聽見前座傳來一聲滄桑的話語。
「誒師傅,行家啊!」宮本驚訝的抬起頭,正好對上后視鏡中那雙炯炯有神的目光。原來是宮本在學習時不自覺地自言自語起來,引起了司機師傅的注意。
「哈哈哈,早年學過,忘得差不多了。」司機師傅靦腆一笑。宮本心里一驚,沒想到真是全民學編程的時代,高手無處不在。
「那您也太厲害了,我正準備了解下高速緩存中的地址映像方法呢。」宮本贊嘆道。
「我之前還在當程序員的時候,就經常被拉去當面試官。關于地址映像方法啊,頁面置換算法啊之類的問題基本上是必問的問題了。這也是對于計算機組成原理的基本考察。」司機師傅仿佛打開了話匣子。
「那您這功力很深厚呀,這么多年了還記得這么基礎的東西。」宮本沒想到這位師傅原來還是一位資深的程序員,時間過去這么久依然記得高速緩存的一些技術重點。
「其實也還好,這種基礎的知識雖然實際工作中不會直接用到,但是對于理解代碼還是有輔助作用的。只不過現在很多年輕人都比較忽視這方面的學習。」司機師傅略帶惋惜的說道。宮本聽完表示贊同的一笑,也沒有接話,繼續沉迷自己學習中去了。
的確也是,現在許多年輕人都是為了編程而編程,反而忽視了許多計算機基本的常識和基本思想。估計很多人可能都還不知道高速緩存中有哪些地址映像方法。
實際上地址映像方法就是為了將主存地址與高速緩存中存儲器的地址對應起來,進行地址的映射,這樣 CPU 在工作才能通過緩存正確地對主存數據進行快速讀寫。
地址映像方法有三種,直接映像,全相聯映像和組相聯映像。
直接映像
直接映像的圖示如下,主存單元中的區塊與緩存中內存塊的關系是固定的,主存中的內存塊只會存放在高速緩存存儲器中的相同塊號。因此,只要主存地址中的主存區號與緩存中的主存區號相同,則表明訪問緩存命中。
這樣一來,根據主存地址中的區內塊號,就可以得到需要訪問的高速緩存存儲器中塊號,從而即可從緩存中訪問需要的數據。
直接映像帶來的好處很明顯, 地址變換很簡單粗暴,主存的內存塊與緩存直接映射,一整塊進行對應。這樣的方式雖然簡單,但是靈活性很差。主存中不同區,但是塊號相同的內存塊不能同時被調入緩存存儲器中。并且,當緩存存儲器中有空著的塊也無法被主存中其它的內存塊替換。
直接映像方式
全相聯映像
為了解決直接映射造成靈活性差,緩存空間無法充分利用的問題,全相聯映像的方式被提出來。全相聯與直接映像就是兩個極端,一個只能整個區塊進行對應,一個就允許任意主存的塊調入高速緩存存儲器中任意的塊。
全相聯映像方式
在進行地址變換時,當主存地址高位的主存塊號與高速緩存中中主存塊號相比時,有相同的塊號即表示命中。一旦命中,CPU 即可通過緩存存儲器中的塊內地址訪問到相應的數據。
全相聯映像能夠提供非常靈活的變換方式,不受主存所在塊的限制。但是同樣也是由于過于靈活,導致實際在變換的時候,無法從主存塊號直接獲得高速緩存存儲器的塊號,變換過程相對比較復雜,速度也比較慢。
組相連映像
既然兩者方法各有利弊,不妨我們就折中一下。因而有了中庸的組相連映像方式。
組相連方式就是將主存和高速緩存存儲器中的塊再分成組,組之間采用直接映像方式,而組內的塊則采用全相聯映像方式。具體的實現可以參考以下圖示。
舉例來說,主存任何區的0組只能存到高速緩存中的0組中,1組只能存到1組。這就是所謂組間的直接映像。而主存相同一組的任意一塊可以存入高速緩存相同組中的任意一塊。這就是所謂組內的全相聯方式。
組相連映像方式
在進行地址變換時,可通過直接映像方式來決定組號,在一組內再用全相聯映像方式決定高速緩存中的塊號。通過比較主存地址高位決定的主存區號與緩存中的區號,即可決定是否命中。
4
司機師傅見宮本專心思考去了,也沒有強行打擾,穩穩地握著方向盤,眼光眺向遠方。
「這地址映像方法還挺有意思,簡單的地址映射也能根據具體的情況進行整體和局部的優化。」不一會,宮本抬起頭,微微感嘆了一聲。
「哈哈哈,說的是啊。計算機領域很多看起來想當然的東西都是經過了不同程度的優化,才能真正運用在實際的系統中。」司機師傅聽見宮本的感嘆,接上了話茬。
「您想必之前是個很純粹的技術人吧。」宮本對這個看起來普普通通的司機師傅充滿了好奇,感覺像是個世外高人,大隱隱于市般。
「噗,這哪談得上。不然也不會出來跑滴滴了。只是之前工作的時候比較看重基礎吧。」司機師傅笑了一聲。
「不過,如果我是你的面試官,我可能會更喜歡問你緩存替換算法。地址變換這些東西沒啥技術含量,對程序員來說也并不是那么透明。相對來說替換算法的思想更值得你們學習哈。比方說 LRU」司機師傅話題一轉,有了那么點面試官的味道。想必應該也是直男一個。
「哈是的,我正準備看呢。我大概記得應該有好幾種吧,除了 LRU ,還有比如隨機替換、FIFO之類的。」宮本之前也經過過幾次面試,對司機師傅的說法表示贊同。
「那你好好看看。」司機師傅建議道,說完二人也都沒有再說話了。
緩存替換算法的目的很明顯,就是為了使得高速緩存獲得盡可能高的命中率,當緩存的存儲器滿了的時候,將不用的數據塊進行刪除,替換新的數據。常用的替換算法有以下幾種:
- 隨機替換算法:顧名思義,就是通過隨機獲得一個需要被替換的塊號,并用新的數據替換該塊。
- 先進先出算法:即FIFO,通過緩存中的塊進入隊列的先后順序進行淘汰和替換,先進入緩存的數據塊最先被替換。
- 最近最少使用算法:即LRU,將近期最少使用的緩存塊替換出去。這種算法在實現的時候將最近使用的的數據塊放置到靠近緩存頂部的位置。每一次替換都從緩存尾部開始進行。
- 最不經常使用算法:即LFU,這個算法需要記錄每一個緩存塊被訪問的頻率,每一次替換都從最低訪問頻率的數據塊開始。
- 最近最常使用算法,即MRU,這個算法會最先移除最近最常使用的數據塊。
替換算法說白了,就是看采用怎么樣的策略將緩存中的數據塊替換出去。在實際的應用中,還會根據程序具體的情況對不同的算法進行優化選擇。
5
看到這,宮本對 CPU 高速緩存的概念已經有了比較清晰的了解。再次抬起頭,發現已經能夠看到小區聳立的高樓。一棟棟樓盤,亮著燈的已經不多。
「師傅,我快到加了,這回感謝你哈。沒想到打車也能遇到同行哈哈。」看完緩存,宮本松了口氣,看來下班回家的時間還是能夠利用起來的。
「不過我還是想問一下,您當初為啥不干這行了呢?」一直懷著這樣的疑問,宮本還是忍不住問了出來。
「我啊,其實也沒為啥吧。之前當程序員的時候,雖然賺得挺多的,但是累是真的累。很長一段時間也找不到自己成長的方向,在公司工作的時間越來越長,卻感覺已經少了那股跟新人一樣的活力和朝氣。怎么說呢,就像是為了工作而工作。」
「所以您就不干啦?」
「倒也沒有那么果斷吧,糾結了挺長時間的。你別看我現在在開滴滴,其實這只不過是我目前的副業。」
「那你現在還敲代碼嗎?」
「現在還是天天敲鍵盤,只不過不是敲代碼啦。我開滴滴也就在你們公司這附近接單,主要是為了找找以前的感覺,同樣也是找找靈感吧。」
「哇,那您應該財富自由了吧哈哈。」
「哈哈哈,還行~」
小區到了,宮本輕松的走下了車,并再次跟司機師傅道了聲謝謝。短短的幾十分鐘的車程,讓他收獲滿滿。
不僅對 CPU 緩存原理進行了快速的復習,還有幸遇到了別樣的人生。他也不清楚自己是否能夠邁過35歲這道坎,但至少情況可能沒那么糟糕。就像那位司機師傅一樣,大膽的嘗試加上勇敢的付出,或許也能夠成就別樣的人生。
大家猜猜司機師傅現在的主業是什么呢?
這篇文章也是一個大膽的嘗試呢,感覺純粹的技術文章可能太過枯燥了。所以給這篇枯燥的技術套上了一個故事的場景,實際上對技術點本身沒啥影響。
本文轉載自微信公眾號「業余碼農」,可以通過以下二維碼關注。轉載本文請聯系 業余碼農公眾號。