本節內容計劃是講解TLB與高速緩存的關係,可是在涉及高速緩的前提是咱們必需要了解操做系統緩存原理,因此提早先詳細瞭解下緩存原理,咱們依然是採起按部就班的方式來解答緩存原理,如有敘述不當之處,還請批評指正。算法
高速緩存被劃分爲多個塊,其大小可能不一樣,緩存中的塊數一般爲2的冪。以下爲一個具備八個塊的高速緩存,每一個塊包含一個字節。緩存
經過本節對緩存原理的學習咱們可以學習到四點:數據結構
【1】當咱們將數據塊從主存儲器複製到緩存,咱們到底應該放在哪裏?app
【2】如何判斷一個字是否已經在緩存中,或者它是否必須首先從主存儲器中獲取?性能
【3】較小的緩存最終會填滿, 須要至從主存加載新塊,咱們必須替換緩存中現有的哪一個塊?學習
【4】存儲系統如何處理寫操做?spa
緩存最簡單的數據結構是直接映射: 其中每一個存儲器地址僅僅對應到緩存中的一個位置。例如以下,16個字節的主存和4個字節的緩存(每一個塊一個字節),內存地址爲0、四、八、12分別映射到緩存中爲0的塊,而地址一、五、九、13被映射到塊1操作系統
等等,咱們是否是講解的太快了,上述地址怎麼就劃分到好比塊0或塊1了呢?要找出緩存所在塊採起取模法:(塊地址)mod (緩存中的塊數),若是緩存包含2k塊,則內存地址i處的數據將進入緩存塊索引爲i mod 2k。仍是不懂?咱們來舉個例子,以下緩存有4個塊,那麼地址爲14將映射到塊2即(14 mod 4 = 2)。設計
爲便於你們理解如上爲10進製表示內存地址,將內存地址映射到緩存塊中實際等效的方式是將內存地址中的最低有效k位(二進制)進行映射。正以下面咱們所看到的,內存地址14(1110,二進制)將最低有效位10做爲塊中的索引3d
到目前爲止咱們知道了將地址利用直接映射的結構映射到緩存中,那麼咱們找到數據是否在緩存中呢?若是要讀取內存地址i,則可使用mod技巧來肯定哪一個緩存塊將包含i,如上所述,若其餘地址也可能映射到相同的緩存塊,那麼咱們如何區分它們呢?例如以下內存地址二、六、十、14都在緩存塊2中
爲了解決這個問題,咱們須要向高速緩存中添加標記(tag),經過內存地址的高位來提供標記位,以使咱們可以區分映射到同一高速緩存塊的不一樣存儲位置。例如以下。內存地址6即(0110,二進制),將低位10做爲索引(index),高位01做爲標記(tag)。
咱們經過將高速緩存塊標記(tag)與塊索引(index)組合起來,能夠準確地知道主存儲器的哪些地址存儲在高速緩存中。
當程序加載到內存中時,緩存爲空,不包含有效數據,咱們應該經過爲每一個緩存塊添加一個有效位來解決這個問題,系統初始化時,全部有效位均設置爲0,當數據加載到特定的緩存塊中時,相應的有效位設置爲1。
當CPU嘗試從內存中讀取數據時,該地址將被髮送到緩存控制器,地址的最低k位將在緩存中索引一個塊,若是該塊有效且標籤與m位地址的高(m-k)位匹配,則該數據將被髮送到CPU,以下爲一個32位內存地址和210字節高速緩存的圖。
到這裏咱們會發現一個問題,將每個字節對應一字節緩存塊並無很好的利用空間局部性,要是訪問一個地址後將訪問附近的地址,咱們又該怎麼辦?咱們要作的是將緩存塊的大小要大於1個字節。以下,咱們使用兩個字節的塊,所以咱們能夠用兩個來加載緩存一次讀取一個字節,若是咱們從內存地址12讀取數據,則地址中的數據12和13都將被複制到緩存塊2。
如今,咱們又該如何肯定數據應放在緩存中的位置?如今演變成塊地址,若是緩存塊大小爲2n字節,咱們也能夠在概念上將主內存也劃分紅2n字節塊,要肯定字節地址i的塊地址,能夠進行整數除法(i / 2n),以下示例中有2個字節的緩存塊,所以咱們能夠將16個字節的主存儲器視爲8塊主存儲器,例如,存儲器地址12和13都對應於塊地址6,由於12 / 2 = 6和13 / 2 = 6。
如今咱們知道了塊地址,就能夠像上述同樣將其映射到緩存:找到塊地址除以緩存塊數後的餘數。在以下示例中,內存塊6屬於緩存塊2,由於6 mod 4 =2,這對應於未來自存儲器字節地址12和13的數據都放入高速緩存塊2中。
當咱們訪問內存中的一個字節數據時,咱們會將其整個塊複製到緩存中以達到充分利用空間局部性。在咱們的示例中,若是程序從字節地址12讀取,咱們會將全部存儲塊6(地址12和13)都加載到緩存塊2中(注意:字節地址13對應於相同的存儲塊地址)所以,對地址13的讀取也會致使將存儲塊6(地址12和13)加載到高速緩存塊2中。爲了簡化起見,存儲塊的字節i始終存儲在相應高速緩存塊的字節i中。
假設咱們有一個包含2k塊的緩存,每一個塊包含2n個字節,咱們能夠經過查看其在主內存中的地址來肯定該緩存中一個字節的數據位置,地址的k位將選擇2k個高速緩存塊之一,最低的n位如今是一個塊偏移量,它決定了高速緩存塊中的2n個字節中的哪一個將存儲數據。
咱們來舉個例子加深理解,以下示例使用22塊高速緩存,每一個塊佔21字節,所以,存儲器地址13(1101)將存儲在高速緩存塊2的字節1中。
到這裏爲止,咱們纔算分析清楚了緩存中有效位、標記位、索引、偏移它們的由來以及實際做用。同時對於緩存採用的直接映射(direct mapped)結構:索引和偏移量可使用位運算符或簡單的算術運算,由於每一個內存地址都剛好屬於一個塊。實際上咱們能夠將一個塊放置到緩存中的任何一個位置,這種機制稱爲全相聯(fully associative)。全相聯的高速緩存容許將數據存儲在任何高速緩存塊中,而不是將每一個內存地址強制映射到一個特定的塊中,從內存中獲取數據時,能夠將其放置在高速緩存的任何未使用塊中。 這樣,咱們將永遠不會在映射到單個緩存塊的兩個或多個內存地址之間發生衝突,在上述示例中,咱們可能將內存地址2放在緩存塊2中,並將地址6放在塊3中。而後對2和6的後續重複訪問將所有命中而不是未命中,若是全部塊都已被使用,則使用LRU算法進行替換。可是在全相聯緩存中要查找一個指定的塊,因爲該塊存放在緩存中的任何位置,所以須要檢索緩存中的全部項,爲了是檢索更加有效,它是由一個與緩存中每一個項都相關的比較器並行完成的,這些比較器加大了硬件開銷,於是,全相聯只適合塊數較少的緩存。介於直接映射和全相聯之間的設計是組相聯(set associative)。在組相聯緩存中,每一個塊可被放置的位置數固定,每一個塊有n個位置可放的緩存被稱做n路組相聯,一個n路組相聯緩存由不少組組成,每一個組有n個塊。經過上述對直接映射的講解,最終咱們得出指定內存地址所在存儲的塊號爲:(塊號) mod (緩存中的塊數),而組相聯對於存儲塊號是:(塊號) mod (緩存中的組數)。以下爲8塊高速緩存的組織
組相聯實際上就是將塊進行分組,好比如上第一個圖則是直接映射(咱們大可將其看作是每個塊就是一個組,因此是1路8組相聯),而第二張圖則是每2個塊做爲一組,因此是2路4組相聯,同理第三張圖是4路2組相聯。換句話說,若每組有2n塊,那麼就是2n路相聯。經過對組相聯的講解,咱們再敘內存地址在組相聯緩存中的位置。若是咱們有2s組而且每塊有2n字節,那麼內存地址映射在緩存中的位置則是以下這般
如今咱們運算則是計算緩存中的組索引而非再是塊,上述Block offset(在組中塊偏移)= 內存地址 mod 2n,塊地址 = 內存地址 / 2n,set index(組索引) = 塊地址 mod 2s。咱們仍是經過圖解來進行敘述,假設有一個8塊的高速緩存,而後每一個塊是16個字節,那麼內存地址爲6195的數據存儲在緩存哪裏呢?首先咱們將6195轉換爲二進制 = 110000 011 0011,因每一個塊是16字節即24,因此塊偏移量爲4位即0011,若採用1路8組相聯(直接映射)那麼其組索引就是(6195 mod 8) = 3,取6195轉換而二進制去除偏移量4位,因此爲011,同理(根據上述給定計算公式)對於2路4組相聯其組索引爲(11),4路2組相聯其組索引爲(1),以下:
到這裏咱們知道將數據進行緩存咱們能夠採起直接映射、組相聯、全相聯的機制,經過增長相聯度一般能夠下降緩存缺失率,可是增長相聯度也就增長了每組中的塊數,也就是並行查找時同時比較的次數,相聯度每增長兩倍就會使得每組中的塊數加倍而使得組數減半,因此增大了訪問時間的開銷。如何找到一個塊,固然也就依賴於所使用的將塊放置的機制(直接映射、組相聯、全相聯),相較於全相聯而言,它使用複雜的替換策略而下降缺失率且很容易被索引,而不須要額外的硬件,也不須要進行查找。所以虛擬存儲系統一般使用全相聯映射,而組相聯映射一般應用於緩存和TLB。
緩存缺失被分爲如下三類(3C模型,three Cs model),因其三類名稱以字母c開頭而得名
強制缺失(compulsory miss):對從沒有在緩存中出現的塊第一次進行訪問引發的缺失,也稱爲冷啓動缺失(cold-start miss)
容量缺失:(capacity miss):因爲緩存容納不了一個程序執行所須要的全部塊而引發的緩存缺失,當某些塊被替換出去,隨後再被調入時,將發生容量缺失
衝突缺失(conflict miss):在組相聯或者直接映射的緩存中,多個競爭同一個組時而引發的緩存缺失。衝突缺失在直接映射或組相聯緩存中存在,而在一樣大小的全相聯緩存中不存在,這種緩存缺失也稱爲碰撞缺失(collision miss)
改變緩存設計的某一方面就能直接影響這些缺失的緣由。衝突缺失是由於爭用同一個緩存塊而引發的,所以提升相聯度能夠減小衝突缺失,而後提升相聯度會延長訪問時間,致使整個性能的下降,容量缺失能夠簡單地經過增大緩存容量來減小,固然緩存容量增大的同時必然致使訪問時間的增長,也將致使總體性能的下降。
在相聯的緩存中發生缺失時,咱們必須決定替換哪一塊,如如果全相聯,那麼全部的塊都是被替換的候選者,如如果組相聯,咱們必須在某一組的塊中進行選擇,固然,直接映射的緩存替換很簡單,由於只有一個能夠替換的候選者。所以在全相聯或組相聯緩存中 ,有兩種主要的替換策略
隨機法:隨機選擇候選塊,可能使用一些硬件來協助實現,例如TLB缺失、MIPS支持隨機替換
LRU(最近最少使用算法):被替換的塊是最久沒有被使用過的塊 (在大多虛擬存儲器中,對於LRU都是經過提供引用位來近似實現(好比TLB))
指令緩存缺失(數據缺失也相似如此)處理步驟以下:
【1】將程序計數器(PC)的原始值送到寄存器
【2】通知主存執行一次讀操做,並等待主存訪問完成
【3】寫緩存項,將從主存取回的數據寫入緩存中存放數據的部分,並將高位(從ALU中獲得)寫入標記域,設置有效位
【4】重啓指令執行第一步,從新取指,此次該指令發生在緩存中
數據訪問是對緩存的控制基本相同:發生缺失時,處理器發生阻塞,直到從存儲器中取回數據後才響應。在執行寫操做時,若是有一個存儲指令,咱們只將數據寫入緩存而不改變主存中的內容,那麼在寫入緩存後將致使緩存和主存被認爲不一致,保持主存和緩存一致性最簡單的方法是將數據同時寫入主存和緩存中,這種方法稱爲【寫直達】法。可是這種方法沒法提供良好的性能,由於每次寫操做都要把數據寫入主存中,這些寫操做將花費大量的時間,可能至少花費100個處理時鐘週期,而且大大下降了機器速度,解決這個問題的方案之一是採用【寫緩衝:一個保存等待寫入主存數據的緩衝隊列】,當一個數據在等待寫入緩存時,先將其寫入緩衝中,當數據寫入緩存和緩衝後,處理器能夠繼續執行,當寫主存操做完成後,寫緩衝裏的數據項也獲得有效釋放。若是寫緩衝已經滿了,那麼當處理器執行到一個寫操做時就必須停下來直到寫緩衝中有一個空位置,固然,若是存儲器完成寫操做的速度比處理器產生寫操做的速度慢,那麼再多的緩衝器也無用,由於產生寫操做比存儲系統接收它們更快。
除了寫直達方法外,另一種可選擇的方法是【寫回】,在寫回機制中,當發生寫操做時,新值僅僅被寫入到緩存塊中,只有當修改過的塊被替換時才須要寫到磁盤上,寫回機制可提升系統性能,尤爲是當處理器寫操做的速度和主存處理寫操做速度同樣快甚至更快時,可是,寫回機制的實現比寫直達要複雜得多。大部分寫回機制的緩衝也是使用寫緩衝,當在發生缺失替換一個被修改的塊時,寫緩衝能夠起到下降缺失代價的做用。在這種狀況下,被修改的數據塊移入與緩存相聯的寫回緩衝器,同時從主存中讀出所須要的數據塊。隨後,寫回緩衝器再將數據寫入主存,若是下一次缺失沒有馬上發生,當髒數據塊必須被替換時,這種方法能夠減小一半的缺失代價。
一個緩存塊能夠放在何處:一個位置(直接映射),一些位置(組相聯),任何位置(全相聯)。
如何找到一個塊:索引(直接映射的緩存中),有限的檢索(組相聯的緩存中),所有檢索(全相聯的緩存中)、專用查找頁表。
緩存缺失時替換哪一塊:隨機選取、LRU
寫操做如何處理:寫直達或寫回策略
本文咱們很是詳細的講解了緩存的基本原理,固然對於如何處理緩存一致性並未涉及(大多采用監聽協議),但願經過我對緩存原理的理解能給閱讀的您能有力所能及的幫助,謝謝。