如下圖,每個cpu都有自己可以使用的L1, L2緩存(cachce),多個核可能會共享L3 cache, 通過緩存cpu可以更快的尋址和執行指令

cache是通過cacheline進行尋址的,
cache_line結構,一個cache line大小64B, 包括tag(用于計算內存地址釋放命中緩存), flag(標志緩存,比如緩存是否失效,寫dirty等)
+-------------------------------------------+
| tag | data block(cache line) | flag |
+-------------------------------------------+
內存地址分成3部分來查詢是否命中了緩存
+-------------------------------------------+
| tag | index | offset |
+-------------------------------------------+
index 用于計算在哪個set, 即同一個set的index都是一樣的, tag不一樣(即一個set下有多路,每路的tag不一樣)
offset指定在cache line的偏移, 64B的cacheline需要6位來表示偏移
查看cacheline大小:
cat /sys/devices/system/cpu/cpu1/cache/index0/coherency_line_size
查看緩存級別
cat /sys/devices/system/cpu/cpu1/cache/index0/level
查看緩存set
cat /sys/devices/system/cpu/cpu1/cache/index0/number_of_sets
查看緩存way
cat /sys/devices/system/cpu/cpu1/cache/index0/ways_of_associativity
way * set * 64 = cache總大小
如下64B*64set*8way=32K
獲取cache值可能有這么些組合:
VIVT、VIPT、PIPT。(V虛擬地址, P 物理地址, I為index, T為tag)
VIVT的硬件實現開銷最低,但是軟件維護成本高;PIPT的硬件實現開銷最高,但是軟件維護成本最低;VIPT介于二者之間,但是有些硬件是VIPT,但是behave as PIPT,這樣對軟件而言,維護成本與PIPT一樣。
VIVT的硬件實現效率很高,不需要經過MMU就可以去查cache了。不過,對軟件來說,這是個災難。因為VIVT有嚴重的歧義和別名問題。
歧義:一個虛擬地址先后指向兩個(或者多個)物理地址
別名:兩個(或者多個)虛擬地址同時指向一個物理地址
兩個不同的虛擬地址回命中不同的cachline, 兩個cachline緩存了同一個物理地址的值, 通過虛擬地址1對cacheline1進行修改后,如果使用虛擬地址2對cacheline2訪問,訪問的是舊的值, 出現了不一致的情況,軟件必須寫完虛擬地址1后,對虛擬地址1對應的cache執行clean,對虛擬地址2對應的cache執行invalidate(實現很困難, 容易疏漏)。
PIPT不存在這類問題,虛擬地址最終通過MMU轉換成唯一的物理地址, 進行對cache的訪問。
VIPT呢?當index+offset的位小于等于內存頁位時候VI=PI, 這樣cpu訪問的內存地址映射內存不會有別名,因為假設一頁是4K,那么地址的低12位虛擬地址和物理地址是完全一樣的, VIPT相當于PIPT了
Cache的一致性有這么幾個層面
1.一個CPU的icache和dcache的同步問題
2.多個CPU各自的cache同步問題
- CPU與設備(其實也可能是個異構處理器,不過在Linux運行的CPU眼里,都是設備,都是DMA)的cache同步問題
當一個內存數據在多個cpu cache上使用, 如果有寫的操作, 一般通過硬件了來實現cache同步, 盡管硬件同步性能已經比較好了, 但是大量的cache同步行為還是回影響程序的性能
通常用的cache優化方法:
- cache預取 – prefetch等函數
- 避免false sharing – 使用cacheline對齊
- 常訪問的數據保證在cacheline的開頭,如使用padding[cacheline_size - sizeof(int)];保證前面的int數據在獨立的cacheline中, 對后面的數據讀寫不影響前面的數據的cache命中效率
- 使用____cacheline_aligned標識數據要獨占cacheline, 比如下面的數據定義
|
// /linux/include/net/xdp.h structxdp_rxq_info { structnet_device *dev; u32 queue_index; u32 reg_state; structxdp_mem_infomem; } ____cacheline_aligned; /* perf critical, avoid false-sharing */ |
參考資料: