#事情其實是這樣的,當時領導者交給我一個perf硬體效能監視的任務,在使用perf的過程中,輸入指令perf list,我看到了以下資訊:
我的任務就要讓這些cache事件能夠正常計數,但關鍵是,我根本不知道這些misses
、loads
是什麼意思。
我只知道它們都是cache,但這幾個名字十分類似,又有什麼差別?
出於此,當時我覺得我有必要去學一下cache的知識了,我對cache、效能等的了解也因此開始。
以下是我當時學習cache總結的一些基本概念知識,對於不了解底層或不了解cache的人,相信都會有幫助。
基本上是以問答的方式引導大家,因為我曾經也是一堆疑問走過來的。
首先我們要知道,cpu存取內存,不是直接存取的,而是需要先經過Cache,為什麼呢?
原因:cpu內的資料是儲存在暫存器中,存取暫存器的速度很快,但是暫存器容量小。而記憶體容量大,但是速度慢。 為了解決cpu和記憶體之間速度和容量的問題,引進了快取Cache。
Cache位於CPU與主記憶體之間,CPU存取主記憶體時,先去存取Cache,看Cache中有沒有這個數據,如果有,就從Cache中拿資料回傳給CPU;如果Cache裡沒有數據,再去存取主記憶體。
通常來說,Cache不只一個,而是有多個,也就是多層Cache,為什麼呢?
原因:cpu存取cache速度也是很快的。但是我們做不到速度和容量完全相容,如果cpu存取cache的速度跟cpu存取暫存器的速度差不多,那麼就代表這個cache速度很快,但是容量很小,這麼小的cache容量還不足夠滿足我們的需求,因此引入了多層Cache。
多層Cache將Cache分成多個等級L1、L2、L3等。
依速度快慢,依序為L1>L2>L3。
依照儲存容量大小,依序為L3>L2>L1。
L1最靠近CPU,L3最靠近主記憶體。
通常L1又分成instruction cache(ICache
)和data cache(DCache
),並且L1 cache是cpu私有的,每個cpu都有一個L1 cache。
命中:CPU要存取的資料在cache中有緩存,稱為“命中”,即cache hit
缺少:CPU要存取的資料在cache中沒有緩存,稱為“缺失”,即cache miss
cache line
:快取行,將cache平均分成相等的許多區塊,每個區塊大小稱為cache line
。
cache line也是cache和主記憶體之間資料傳輸的最小單位.
當CPU試圖load一個位元組資料的時候,如果cache缺失,那麼cache控制器就會從主記憶體中一次性的load cache line大小的資料到cache。例如,cache line大小是8位元組。 CPU即使讀取一個byte,在cache缺失後,cache會從主記憶體load 8位元組填入整個cache line。
CPU存取cache時的位址編碼,通常由tag、index和offset三部分組成:
(標記域):用來判斷cache line快取的資料的位址是否和處理器尋址位址一致。 index(索引網域):用於索引並尋找位址在快取中的哪一行
(偏移量):快取行中的偏移量。可以按字或位元組來尋址高速緩存行的內容我們知道,CPU存取記憶體不是直接存取的,而是CPU發出虛擬位址,然後經過MMU轉換為物理位址後,根據物理位址從內存取數據。 那麼cache存取的是虛擬位址還是實體位址?
答:不一定。既可以是虛擬位址,也可以是實體位址,也可以是虛擬位址和實體位址的組合。
因為cache在硬體設計上有多種組織方式:
#VIVT
虛擬快取:虛擬地址的index,虛擬地址的tag。 PIPT
物理快取:物理位址的index,物理位址的tag。 VIPT
實體標記的虛擬快取:虛擬位址的index,實體位址的tag。 歧義(homonyms
):相同的虛擬位址對應不同的實體位址
別名(alias
):多個虛擬位址對應到了相同的實體位址(多個虛擬位址稱為別名)。
例如上述VIVT方式就會有別名問題,那VIVT、PIPT和VIPT那個方式比較好呢?
PIPT
其實是比較理想的,因為index和tag都使用了實體位址,軟體層面不需要任何維護就能避免歧義和別名問題。
VIPT
的tag使用了實體位址,所以不存在歧義問題,但index是虛擬位址,所以可能也存在別名問題。
而VIVT
的方式,歧義和別名問題都存在。
實際上,現在硬體中使用的基本是PIPT或VIPT。 VIVT問題太多,已經成為歷史了,不會有人用。另外PIVT的方式是不存在的,因為它只有缺點沒有優點,不只速度慢,歧義和別名問題也都存在。
cache的組織方式,以及歧義和別名問題,是比較大塊的內容。這裡只需要知道cache存取的位址可以是虛擬位址,也可以是實體位址,也可以是虛擬位址和實體位址的組合。而不同的組織方式會有歧義和別名問題。
指的是發生cache miss時,cache如何分配。
讀取分配:當CPU
讀取資料時,發生cache
缺失,這種情況會分配一個cache line
緩存從主存讀取的資料。 預設情況下,cache
都支援讀取分配。
寫入分配:當CPU寫入資料發生cache
#缺失時,才會考慮寫入分配策略。當我們不支援寫入分配的情況下,寫指令只會更新主存數據,然後就結束了。當支援寫入分配的時候,我們先從主記憶體載入資料到cache line
中(相當於先做個讀分配動作),然後會更新cache line
中的數據。
指的是cache命中時,寫入操作應該如何更新資料。
寫直通:當CPU執行store指令並在cache命中時,我們更新cache中的資料並且更新主記憶體中的資料。 cache和主記憶體的資料總是保持一致。
寫回:當CPU
執行store
指令並在cache
命中時,我們只更新cache
中的數據。而每個cache line
中會有一個bit
位元記錄資料是否被修改過,稱為dirty bit
。我們會將dirty bit
置位。主記憶體中的資料只會在cache line
被取代或顯示的clean
作業時更新。因此,主記憶體中的數據可能是未修改的數據,而修改的數據躺在cache中。 cache和主記憶體的資料可能不一致。
#關於cache的內容,還有TLB、MESI、記憶體一致性模型等等等,是一個需要沉澱和總結才能真正掌握的東西。
但可能很多人都用不上,只有牽涉到效能問題,當你需要提高cache命中率時,才知道這些知識的重要性。
關於本文所講的知識,總結了一份cache基礎知識的心智圖:
以上是入職後,我才明白什麼叫Cache的詳細內容。更多資訊請關注PHP中文網其他相關文章!