在程序局部性原理介紹(請戳我)一文中介紹了程序的局部性原理,而且用用一個遍歷二維數組的例子證實了:知足良好局部性的代碼具備較高的運行效率,本文就從計算機存儲結構的層面解釋爲何會這樣。算法
在計算機系統中,存儲設備都被組織成了一個存儲器層次結構,以下圖所示:
咱們發現,越往上,存儲器的容量越小、成本越高、速度越快。其實在最開始的時候,計算機存儲器層次只有三層:cpu寄存器、DRAM主存以及磁盤存儲。那爲何後來搞得這麼複雜了?編程
這是由於CPU和主存之間存在巨大速度差別,做爲核心的CPU處理數據的速度極快,內存跟不上,怎麼辦?系統設計者被迫在CPU寄存器和主存之間插入了一個小的SRAM高速緩存存儲器稱爲L1緩存,大約能夠在2--4個時鐘週期內訪問。再後來發現L1高速緩存和主存之間仍是有較大差距,又在L1高速緩存和主存之間插入了速度稍微慢點的L2緩存,大約能夠在10個時鐘週期內訪問。因而,在這樣的模式下,在不斷的演變中造成了如今的存儲體系。數組
存儲器層次結構的主要思想是上一層的存儲器做爲低一層存儲器的高速緩存。所以,寄存器L0就是L1的高速緩存,L1是L2的高速緩存,L2是L3的高速緩存,L3是主存的高速緩存,而主存又是磁盤的高速緩存。緩存
也就是說,對於每一個k ,位於k層的更快更小的存儲器設備做爲第k+1層的更大更慢存儲設備的緩存。就是說,k層存儲了k+1層中常常被訪問的數據。在緩存之間,數據是以塊爲單位傳輸的。固然不一樣層次的緩存,塊的大小會不一樣。通常來講是越往上,塊越小。服務器
下圖就是一個示例:
上圖中,k是k+1的緩存,k中緩存了k+1中塊編號爲 四、九、1四、3的數據。他們之間的數據傳輸是以塊大小爲單位的。當程序須要這些塊中的數據時,可直接衝緩存k中獲得。這比從k+1層讀數據要快數據結構
緩存命中和緩存失效
下面以上圖爲例再來解釋兩個概念:併發
緩存命中:當程序須要第k+1層中的某個數據時d,會首先在它的緩存k層中尋找。若是數據恰好在k層中,就稱爲緩存命中(cache hit),如在上圖中,若程序訪問k+1層的4,先去其緩存層k去找,4剛好k層,發生了緩存命中。ide
緩存失效:緩存失效也稱緩存不命中,當須要的數據對象d不在緩存k中時,稱爲緩存不命中。當發生緩存不命中時,cpu會直接從k+1層取出包含數據對象d的那個塊,而後須要將其再緩存到k層,以便下次再訪問時就能直接從緩存層k中取到。
對於緩存失效的數據被內存獲取後再存入到k層的緩存中時,若是此時k層的緩存已經放滿的話,就會覆蓋其中的一個塊。至於要覆蓋哪個塊,這是有緩存中的替換策略決定的,好比說在LRU緩存(請戳我)一文中介紹的LRU緩存就是一種替換策略。這裏再也不討論。函數
好了,如今咱們能夠說說爲何局部性好的程序能有更好的性能了。性能
利用時間局部性:因爲時間局部性,同一個數據對象會屢次被使用。一旦一個數據對象從k+1層進入到k層的緩存中,就但願它屢次被引用。這樣能節省不少訪問形成的時間開支。
利用空間局部性:假設緩存k能存n個數據塊。在對數組訪問的時候,因爲數組是連續存放的,對第一個元素訪問的時候,會把第一個元素後面的一共n個元素(緩存以塊爲單位傳輸)拷貝到緩存k中,這樣在對第二個元素到第n個元素的訪問時就能夠直接從緩存裏獲取,從而提升性能。
咱們仍是分析一個例子,在程序局部性原理介紹(請戳我)一文中已經知道了下面兩個函數fun_1的效率幾乎比fun_2的效率高了一倍
(不一樣機器運行結果可能不太同樣),如今咱們來分析其緣由。
//先訪問行 void fun_1() { int i,j; for(i=0; i<500; i++) { for(j=0; j<500; j++) { a[i][j]=i; } } } //先訪問列 void fun_2() { int i,j; for(j=0; j<500; j++) { for(i=0; i<500; i++) { a[i][j]=i; } } }
咱們知道,這兩個函數的區別在於fun_1函數是按行訪問,fun_2函數是按列訪問,正是這細微的不一樣致使效率上的差異。
咱們先來看看按行訪問,發生了什麼,爲了分析方便,咱們假設:緩存每次只能緩存一塊,一塊大小隻能存放3個int類型數據,假設對於一個兩行三列的int[2][3]的數組訪問。
按行訪問時,訪問a[0][0],時直接從內存讀取,而後將a[0][0]以及其後的兩個數緩存到其緩存中,這樣再訪問a[0][1],a[0][2]時直接從緩存中讀取,對於第二行的訪問也是如此。所以對於整個數組6個元素的訪問,只訪問了2次內存,緩存命中了4次。
再來看看按列訪問,當訪問a[0][0],時直接從內存讀取,而後將a[0][0]以及其後的兩個數緩存到其緩存中,看起來和按行訪問沒什麼區別,那好,再看下一步,按列訪問的話接下來應該是訪問a[1][0]了,先去緩存中找,確定找不到了,沒辦法,只能再次訪問內存。對於第二、3列狀況同樣,這樣,一樣一個數組,按列訪問訪問了6次內存,效率固然比按行訪問低了。
【福利】本身蒐集的網上精品課程視頻分享(上)
【系統設計】LRU緩存
【數據結構與算法】 通俗易懂講解 二叉搜索樹
【數據結構與算法】 通俗易懂講解 鏈表
【數據結構與算法】 通俗易懂講解 位排序
【C++札記】C++11併發編程(一)開啓線程之旅
【C++札記】C/C++指針使用常見的坑
碼農有道,爲您提供通俗易懂的技術文章,讓技術變的更簡單!