五一小長假過完了,在刷了四天劇後又要開啓975養老生活了,收假第一天補一篇關於CPU高速緩存的文章,錯誤之處歡迎你們指正。數組
咱們都知道,程序是由一條條指令和數據組成的,CPU在運行時的工做也是周而復始的執行一條條用途各異的指令。
在一開始,程序加載到主存,在程序執行的過程當中,將指令一條條的從主存中取出並執行,宏觀上來講咱們把主存看作是一個很大的一維字節數組,地址便可看做爲數組的下標。這樣的結構能夠確保程序能夠順利執行。但是存在一個問題,主存的讀取速度太慢,而CPU的運算速度卻很是快,CPU執行完一條指令後又等待十分漫長的時間才能等到下一條指令的到來,CPU的資源被嚴重浪費。緩存
其實CPU一開始執行指令的速度也並非特別快的,經過不斷的優化將其速度進行提高。過了一段時間,CPU執行指令的速度愈來愈快了,這時發現影響程序執行快慢的瓶頸不在於CPU了,而在於CPU執行指令與主存讀取指令的速度差距上,因此必需要想辦法讓讀取指令的速度快起來,這便出現了咱們的高速緩存。性能優化
引入高速緩存後的存儲器層次結構圖以下所示,固然這只是一個簡化圖,利於咱們理解整個存儲器的結構層次。其中L1,L2就是咱們所說的高速緩存, 這個結構相似於一個金字塔,越往上則越靠近CPU,讀取和寫入的速度越快,造價也越昂貴。性能
這裏咱們假設L1與L2之間的緩存塊大小爲8KB,L2與主存之間的緩存塊大小爲64KB爲例來闡述一下CPU讀取指令整個流程,尋址器首先會到L1高速緩存中去尋找指令,若是沒有CPU則等待尋址器到L2高速緩存中尋找指令,若是L2高速緩存也沒有尋找到,那就從主存中尋找指令。尋找到指令後將命中的緩存塊(64KB)的全部數據移動到L2,並將L2對應的緩存塊(8KB)全部數據移動到L1。最終在L1中將對應的單條指令返回給CPU。優化
爲什麼咱們要提出緩存塊這個概念?
咱們的程序指令每每是連續的,程序訪問到某個數據時,那麼它和它周圍的數據會有很大可能在短期內被再次訪問,這被稱之爲局部性原理。因此咱們在訪問到某個數據時,索性將它和它周圍的數據也提到上層的高速緩存中,下一次就能夠直接從高速緩存中命中數據。3d
上節中簡單描述了CPU取指令時數據在存儲器結構中的流動狀況,咱們說若是L1中沒有咱們訪問的數據則會到L2中去尋找,咱們稱這個爲緩存不命中,那麼如何判斷緩存是否命中呢?咱們將圖2的高速緩存進行放大,觀摩一下它的結構cdn
若是說你在疑惑t、b、s的含義,先不用管它,繼續往下看天然就知道了blog
若是說CPU如今要訪問某個地址爲Adress的數據,尋址器將地址進行以下劃分 索引
根據組索引位定位到組資源
查看緩存塊有效位是否爲1,若是是那麼比對標記位,如若標記位一致,則表示該緩存命中。成功定位到緩存塊
根據最後b位計算出偏移地址
這裏的t、b、s與圖3的數據一致。假設咱們是64位機器,那麼Adress的長度應當等於64,即t+s+b=64
緩存不命中的狀況存在兩種,一種是緩存爲空,一種是緩存衝突。前一種的發生的狀況在計算機剛啓動時會比較常見,這是計算機的緩存是空的,因此緩存不命中,後一種發生的緣由咱們舉個很明顯的例子,假設如今有一個尋址長度爲16位的計算機,標記位t=3,索引位s=5,偏移位b=8。若是咱們訪問這樣的兩個地址:
A=121 111 00021
B=131 111 00022
會發現A,B雖然地址不一樣,可是根據上述的步驟,它們會映射到同一個高速緩存塊,可是因爲標記位不一致致使緩存不命中,這種狀況稱之爲緩存衝突。
上面的大篇幅中咱們都是讀數據的狀況,那麼若是是寫數據高速緩存改如何工做呢?因爲寫的狀況涉及到數據的修改,因此務必要比讀的狀況更復雜一些, 假如咱們如今要對地址A進行寫入,那咱們存在兩種方案
一、摒棄高速緩存,直接寫主存
二、寫入高速緩存
若是咱們選擇第一種狀況,那麼直接寫入就ok,可是就回到了一開始的問題了,寫入的速度太慢,cpu要漫長的等待。那麼咱們固然是採用第二種狀況。先將數據讀入高速緩存,再對緩存進行寫入。那麼這種狀況咱們須要注意一個問題:不一樣層級的緩存同步問題,也就是說當這個緩存塊發生緩存衝突,在數據覆蓋時須要將這個緩存塊刷新到它的下一級高速緩存中。
經過上面的講述我想高速緩存工做的原理應該在腦海中已經有一個流程圖了。那麼知道了緩存的工做原理在實際工做中有什麼用呢?這個就回到了咱們一開始說的局部性原理了,因爲計算機老是將一段連續的地址進行緩存(緩存塊),因此若是咱們編寫的代碼符合局部性原理,那麼運行效率將會有很大的提高,這爲咱們的程序性能優化提供了一個方面的指引。
舉個例子 若是遍歷一個長度相同數組和一個鏈表,因爲數組在物理存儲上是連續的,遍歷數組時效率會更快