J.U.C JMM. pipeline.指令重排序,happen-before(續MESI協議)

緩存(Cache)緩存

      CPU的讀/寫(以及取指令)單元正常狀況下甚至都不能直接訪問內存——這是物理結構決定的;CPU都沒有管腳直接連到內存。相反,CPU和一級緩存(L1 Cache)通信,而一級緩存才能和內存通信。大約二十年前,一級緩存能夠直接和內存傳輸數據。現在,更多級別的緩存加入到設計中,一級緩存已經不能直接和內存通信了,它和二級緩存通信——而二級緩存才能和內存通信。或者還可能有三級緩存。oop

    緩存是分「行」(cache line)的,一個行對應一塊存儲空間,大小是3二、64或128字節。每一個緩存行知道本身對應什麼範圍的物理內存地址(緩存行對應的標誌位與內存虛擬地址的子集對應),根據每組的行數不一樣和組數的不一樣分爲: 1, 直接映射高速緩存(每組只有一個緩存行) 2,組相聯高速緩存(每組有多個緩存行)  3, 全相聯高速緩存(只有一個緩存組) 。不一樣形式的高速緩存對應了不一樣的內存地址映射關係(有效位 - 標誌位 - 行內偏移位)優化

  當CPU看到一條讀內存的指令時,它會把內存地址傳遞給一級數據緩存(或可戲稱爲L1D$)。一級數據緩存會檢查它是否有這個內存地址對應的緩存行。若是沒有,它會把整個緩存段從內存(或者從更低一級的緩存,若是有的話)中加載進來。是的,一次加載整個緩存段,這是基於這樣一個假設:內存訪問傾向於本地化(localized),若是咱們當前須要某個地址的數據,那麼極可能咱們立刻要訪問它的鄰近地址。一旦緩存段被加載到緩存中,讀指令就能夠正常進行讀取。spa

  若是咱們只處理讀操做,那麼事情會很簡單,由於全部級別的緩存都遵照如下規律,我稱之爲:.net

基本定律:在任意時刻,任意級別緩存中的緩存段的內容,等同於它對應的內存中的內容。設計

  一旦咱們容許寫操做,事情就變得複雜一點了。這裏有兩種基本的寫模式:直寫(write-through)和回寫(write-back)。直寫更簡單一點:咱們透過本級緩存,直接把數據寫到下一級緩存(或直接到內存)中,若是對應的段被緩存了,咱們同時更新緩存中的內容(甚至直接丟棄),就這麼簡單。這也遵照前面的定律:緩存中的段永遠和它對應的內存內容匹配。blog

  回寫模式就有點複雜了。緩存不會當即把寫操做傳遞到下一級,而是僅修改本級緩存中的數據,而且把對應的緩存段標記爲「髒」段。髒段會觸發回寫,也就是把裏面的內容寫到對應的內存或下一級緩存中。回寫後,髒段又變「乾淨」了。當一個髒段被丟棄的時候,老是先要進行一次回寫。回寫所遵循的規律有點不一樣。事務

回寫定律:當全部的髒段被回寫後,任意級別緩存中的緩存段的內容,等同於它對應的內存中的內容。ip

  換句話說,回寫模式的定律中,咱們去掉了「在任意時刻」這個修飾語,代之以弱化一點的條件:要麼緩存段的內容和內存一致(若是緩存段是乾淨的話),要麼緩存段中的內容最終要回寫到內存中(對於髒緩存段來講)。內存

  直接模式更簡單,可是回寫模式有它的優點:它能過濾掉對同一地址的反覆寫操做,而且,若是大多數緩存段都在回寫模式下工做,那麼系統常常能夠一會兒寫一大片內存,而不是分紅小塊來寫,前者的效率更高

  有些(大多數是比較老的)CPU只使用直寫模式,有些只使用回寫模式,還有一些,一級緩存使用直寫而二級緩存使用回寫。這樣作雖然在一級和二級緩存之間產生了沒必要要的數據流量,但二級緩存和更低級緩存或內存之間依然保留了回寫的優點。

 

一致性協議(Coherency protocols)

  只要系統只有一個CPU核在工做,一切都沒問題。若是有多個核,每一個核又都有本身的緩存,那麼咱們就遇到問題了:若是某個CPU緩存段中對應的內存內容被另一個CPU偷偷改了,會發生什麼?

  好吧,答案很簡單:什麼也不會發生。這很糟糕。由於若是一個CPU緩存了某塊內存,那麼在其餘CPU修改這塊內存的時候,咱們但願獲得通知。咱們擁有多組緩存的時候,真的須要它們保持同步。或者說,系統的內存在各個CPU之間沒法作到與生俱來的同步,咱們其實是須要一個你們都能遵照的方法來達到同步的目的。

  注意,這個問題的根源是咱們擁有多組緩存,而不是多個CPU核。咱們也能夠這樣解決問題,讓多個CPU核共用一組緩存:也就是說只有一塊一級緩存,全部處理器都必須共用它。在每個指令週期,只有一個幸運的CPU能經過一級緩存作內存操做,運行它的指令。

  這自己沒問題。惟一的問題就是太慢了,由於這下處理器的時間都花在排隊等待使用一級緩存了(而且處理器會作大量的這種操做,至少每一個讀寫指令都要作一次)。指出這一點是由於它代表了問題不是由多核引發的,而是由多緩存引發的。咱們知道了只有一組緩存也能工做,只是太慢了,接下來最好就是能作到:使用多組緩存,但使它們的行爲看起來就像只有一組緩存那樣。緩存一致性協議就是爲了作到這一點而設計的。就像名稱所暗示的那樣,這類協議就是要使多組緩存的內容保持一致。

  緩存一致性協議有多種,可是你平常處理的大多數計算機設備使用的都屬於「窺探(snooping)」協議,這也是這裏要講的。

  「窺探」背後的基本思想是,全部內存傳輸都發生在一條共享的總線上,而全部的處理器都能看到這條總線:緩存自己是獨立的,可是內存是共享資源,全部的內存訪問都要通過仲裁(arbitrate):同一個指令週期中,只有一個緩存能夠讀寫內存。窺探協議的思想是,緩存不只僅在作內存傳輸的時候才和總線打交道,而是不停地在窺探總線上發生的數據交換,跟蹤其餘緩存在作什麼。因此當一個緩存表明它所屬的處理器去讀寫內存時,其餘處理器都會獲得通知,它們以此來使本身的緩存保持同步。只要某個處理器一寫內存,其餘處理器立刻就知道這塊內存在它們本身的緩存中對應的段已經失效

  在直寫模式下,這是很直接的,由於寫操做一旦發生,它的效果立刻會被「公佈」出去。可是若是混着回寫模式,就有問題了。由於有可能在寫指令執行事後好久,數據纔會被真正回寫到物理內存中——在這段時間內,其餘處理器的緩存也可能會傻乎乎地去寫同一塊內存地址,致使衝突。在回寫模型中,簡單把內存寫操做的信息廣播給其餘處理器是不夠的,咱們須要作的是,在修改本地緩存以前,就要告知其餘處理器。咱們一般叫作MESI協議(譯者注:MESI是Modified、Exclusive、Shared、Invalid的首字母縮寫,表明四種緩存狀態,下面的譯文中可能會以單個字母指代相應的狀態)。

  MESI以及衍生協議

  本節叫作「MESI以及衍生協議」,是由於MESI衍生了一系列緊密相關的一致性協議。咱們先從原生的MESI協議開始:MESI是四種緩存段狀態的首字母縮寫,任何多核系統中的緩存段都處於這四種狀態之一。

      CPU中每一個緩存行(caceh line)使用4種狀態進行標記(使用額外的兩位(bit)表示):

      M: 被修改(Modified)

      該緩存行只被緩存在該CPU的緩存中,而且是被修改過的(dirty), 即與主存中的數據不一致,該緩存行中的內存須要在將來的某個時間點(容許其它CPU讀取請主存中相應內存以前)寫回(write back)主存。當被寫回主存以後,該緩存行的狀態會變成獨享(exclusive)狀態。

      E: 獨享的(Exclusive)

      該緩存行只被緩存在該CPU的緩存中,它是未被修改過的(clean),與主存中數據一致。該狀態能夠在任什麼時候刻當有其它CPU讀取該內存時變成共享狀態(shared)。一樣地,當CPU修改該緩存行中內容時,該狀態能夠變成Modified狀態。

      S: 共享的(Shared)

      該狀態意味着該緩存行可能被多個CPU緩存,而且各個緩存中的數據與主存數據一致(clean),當有一個CPU修改該緩存行中,其它CPU中該緩存行能夠被做廢(變成無效狀態(Invalid))。

      I: 無效的(Invalid)

      該緩存是無效的(可能有其它CPU修改了該緩存行)。

    

      在一個典型系統中,可能會有幾個緩存(在多核系統中,每一個核心都會有本身的緩存)共享主存總線,每一個相應的CPU會發出讀寫請求,而緩存的目的是爲了減小CPU讀寫共享主存的次數。一個緩存除在Invalid狀態外均可以知足 CPU的讀請求,一個Invalid的緩存行必須從主存中讀取(變成S或者 E狀態)來知足該CPU的讀請求。

     一個寫請求只有在該緩存行是M或者E狀態時才能被執行,若是緩存行處於S狀態,必須先將其它緩存中該緩存行變成Invalid狀態(也既是不容許不一樣CPU同時修改同一緩存行即便修改該緩存行中不一樣位置的數據也不容許)。該操做常常做用廣播的方式來完成,例如:Request For Ownership (RFO)

     緩存能夠隨時將一個非M狀態的緩存行做廢,或者變成Invalid狀態,而一個M狀態的緩存行必須先被寫回主存。

     一個處於M狀態的緩存行必須時刻監聽全部試圖讀該緩存行相對就主存的操做,這種操做必須在緩存將該緩存行寫回主存並將狀態變成E狀態以前被延遲執行

     一個處於S狀態的緩存行也必須監聽其它緩存使該緩存行無效或者獨享該緩存行的請求,並將該緩存行變成無效(Invalid)。

      一個處於E狀態的緩存行也必須監聽其它緩存讀主存中該緩存行的操做,一旦有這種操做,該緩存行須要變成S狀態

      對於M和E狀態而言老是精確的,他們在和該緩存行的真正狀態是一致的。而S狀態多是非一致的,若是一個緩存將處於S狀態的緩存行做廢了,而另外一個緩存實際上可能已經獨享了該緩存行,可是該緩存卻不會將該緩存行升遷爲E狀態,這是由於其它緩存不會廣播他們做廢掉該緩存行的通知,一樣因爲緩存並無保存該緩存行的copy的數量,所以(即便有這種通知)也沒有辦法肯定本身是否已經獨享了該緩存行。

     從上面的意義看來E狀態是一種投機性的優化:若是一個CPU想修改一個處於S狀態的緩存行,總線事務須要將全部該緩存行的copy變成invalid狀態而修改E狀態的緩存不須要使用總線事務

  

MESI定律:在全部的髒緩存段(M狀態)被回寫後,任意緩存級別的全部緩存段中的內容,和它們對應的內存中的內容一致。此外,在任意時刻,當某個位置的內存被一個處理器加載入獨佔緩存段時(E狀態),那它就不會再出如今其餘任何處理器的緩存中。

  

參考:

     http://blog.csdn.net/realxie/article/details/7317630

     http://kb.cnblogs.com/page/504824/

相關文章
相關標籤/搜索