1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        深入理解CPU緩存一致性協(xié)議MESI(建議收藏)

        共 4502字,需瀏覽 10分鐘

         ·

        2022-01-10 10:21


        點擊上方卡片關注我

        今天,我們就深入聊聊關于CPU緩存一致性協(xié)議MESI的有關知識,希望能夠為小伙伴們帶來實質性的幫助。好了,不多說了,進入今天的正題。

        CPU高速緩存

        CPU為何要有高速緩存

        CPU在摩爾定律的指導下以每18個月翻一番的速度在發(fā)展,然而內存和硬盤的發(fā)展速度遠遠不及CPU。這就造成了高性能能的內存和硬盤價格及其昂貴。然而CPU的高度運算需要高速的數(shù)據(jù)。為了解決這個問題,CPU廠商在CPU中內置了少量的高速緩存以解決I\O速度和CPU運算速度之間的不匹配問題。

        在CPU訪問存儲設備時,無論是存取數(shù)據(jù)抑或存取指令,都趨于聚集在一片連續(xù)的區(qū)域中,這就被稱為局部性原理。

        時間局部性(Temporal Locality):如果一個信息項正在被訪問,那么在近期它很可能還會被再次訪問。

        比如循環(huán)、遞歸、方法的反復調用等。

        空間局部性(Spatial Locality):如果一個存儲器的位置被引用,那么將來他附近的位置也會被引用。

        比如順序執(zhí)行的代碼、連續(xù)創(chuàng)建的兩個對象、數(shù)組等。

        帶有高速緩存的CPU執(zhí)行計算的流程

        1. 程序以及數(shù)據(jù)被加載到主內存
        2. 指令和數(shù)據(jù)被加載到CPU的高速緩存
        3. CPU執(zhí)行指令,把結果寫到高速緩存
        4. 高速緩存中的數(shù)據(jù)寫回主內存

        目前流行的多級緩存結構

        由于CPU的運算速度超越了1級緩存的數(shù)據(jù)I\O能力,CPU廠商又引入了多級的緩存結構。

        多級緩存結構

        多核CPU多級緩存一致性協(xié)議MESI

        多核CPU的情況下有多個一級緩存,如何保證緩存內部數(shù)據(jù)的一致,不讓系統(tǒng)數(shù)據(jù)混亂。這里就引出了一個一致性的協(xié)議MESI。

        MESI協(xié)議緩存狀態(tài)

        MESI 是指4中狀態(tài)的首字母。每個Cache line有4個狀態(tài),可用2個bit表示,它們分別是:

        緩存行(Cache line):緩存存儲數(shù)據(jù)的單元。

        狀態(tài)描述監(jiān)聽任務
        M 修改 (Modified)該Cache line有效,數(shù)據(jù)被修改了,和內存中的數(shù)據(jù)不一致,數(shù)據(jù)只存在于本Cache中。緩存行必須時刻監(jiān)聽所有試圖讀該緩存行相對就主存的操作,這種操作必須在緩存將該緩存行寫回主存并將狀態(tài)變成S(共享)狀態(tài)之前被延遲執(zhí)行。
        E 獨享、互斥 (Exclusive)該Cache line有效,數(shù)據(jù)和內存中的數(shù)據(jù)一致,數(shù)據(jù)只存在于本Cache中。緩存行也必須監(jiān)聽其它緩存讀主存中該緩存行的操作,一旦有這種操作,該緩存行需要變成S(共享)狀態(tài)。
        S 共享 (Shared)該Cache line有效,數(shù)據(jù)和內存中的數(shù)據(jù)一致,數(shù)據(jù)存在于很多Cache中。緩存行也必須監(jiān)聽其它緩存使該緩存行無效或者獨享該緩存行的請求,并將該緩存行變成無效(Invalid)。
        I 無效 (Invalid)該Cache line無效。

        注意:對于M和E狀態(tài)而言總是精確的,他們在和該緩存行的真正狀態(tài)是一致的,而S狀態(tài)可能是非一致的。如果一個緩存將處于S狀態(tài)的緩存行作廢了,而另一個緩存實際上可能已經獨享了該緩存行,但是該緩存卻不會將該緩存行升遷為E狀態(tài),這是因為其它緩存不會廣播他們作廢掉該緩存行的通知,同樣由于緩存并沒有保存該緩存行的copy的數(shù)量,因此(即使有這種通知)也沒有辦法確定自己是否已經獨享了該緩存行。

        從上面的意義看來E狀態(tài)是一種投機性的優(yōu)化:如果一個CPU想修改一個處于S狀態(tài)的緩存行,總線事務需要將所有該緩存行的copy變成invalid狀態(tài),而修改E狀態(tài)的緩存不需要使用總線事務。

        MESI狀態(tài)轉換

        理解該圖的前置說明:1.觸發(fā)事件

        觸發(fā)事件描述
        本地讀?。↙ocal read)本地cache讀取本地cache數(shù)據(jù)
        本地寫入(Local write)本地cache寫入本地cache數(shù)據(jù)
        遠端讀?。≧emote read)其他cache讀取本地cache數(shù)據(jù)
        遠端寫入(Remote write)其他cache寫入本地cache數(shù)據(jù)

        2.cache分類:前提:所有的cache共同緩存了主內存中的某一條數(shù)據(jù)。

        本地cache:指當前cpu的cache。觸發(fā)cache:觸發(fā)讀寫事件的cache。其他cache:指既除了以上兩種之外的cache。注意:本地的事件觸發(fā) 本地cache和觸發(fā)cache為相同。

        上圖的切換解釋(點擊看大圖):

        點擊上圖看大圖

        下圖示意了,當一個cache line的調整的狀態(tài)的時候,另外一個cache line 需要調整的狀態(tài)。


        MESI
        M×××
        E×××
        S××
        I

        舉個栗子來說:

        假設cache 1 中有一個變量x = 0的cache line 處于S狀態(tài)(共享)。那么其他擁有x變量的cache 2、cache 3等x的cache line調整為S狀態(tài)(共享)或者調整為 I 狀態(tài)(無效)。

        多核緩存協(xié)同操作

        假設有三個CPU A、B、C,對應三個緩存分別是cache a、b、 c。在主內存中定義了x的引用值為0。

        單核讀取

        那么執(zhí)行流程是:CPU A發(fā)出了一條指令,從主內存中讀取x。從主內存通過bus讀取到緩存中(遠端讀取Remote read),這是該Cache line修改為E狀態(tài)(獨享).

        雙核讀取

        那么執(zhí)行流程是:CPU A發(fā)出了一條指令,從主內存中讀取x。CPU A從主內存通過bus讀取到 cache a中并將該cache line 設置為E狀態(tài)。CPU B發(fā)出了一條指令,從主內存中讀取x。CPU B試圖從主內存中讀取x時,CPU A檢測到了地址沖突。這時CPU A對相關數(shù)據(jù)做出響應。此時x 存儲于cache a和cache b中,x在chche a和cache b中都被設置為S狀態(tài)(共享)。

        修改數(shù)據(jù)

        那么執(zhí)行流程是:CPU A 計算完成后發(fā)指令需要修改x. CPU A 將x設置為M狀態(tài)(修改)并通知緩存了x的CPU B, CPU B將本地cache ?b中的x設置為I狀態(tài)(無效) CPU A 對x進行賦值。

        同步數(shù)據(jù)

        那么執(zhí)行流程是:

        CPU B 發(fā)出了要讀取x的指令。CPU B 通知CPU A,CPU A將修改后的數(shù)據(jù)同步到主內存時cache a 修改為E(獨享) CPU A同步CPU B的x,將cache a和同步后cache b中的x設置為S狀態(tài)(共享)。

        MESI優(yōu)化和他們引入的問題

        緩存的一致性消息傳遞是要時間的,這就使其切換時會產生延遲。當一個緩存被切換狀態(tài)時其他緩存收到消息完成各自的切換并且發(fā)出回應消息這么一長串的時間中CPU都會等待所有緩存響應完成。可能出現(xiàn)的阻塞都會導致各種各樣的性能問題和穩(wěn)定性問題。

        CPU切換狀態(tài)阻塞解決-存儲緩存(Store Bufferes)

        比如你需要修改本地緩存中的一條信息,那么你必須將I(無效)狀態(tài)通知到其他擁有該緩存數(shù)據(jù)的CPU緩存中,并且等待確認。等待確認的過程會阻塞處理器,這會降低處理器的性能。應為這個等待遠遠比一個指令的執(zhí)行時間長的多。

        Store Bufferes

        為了避免這種CPU運算能力的浪費,Store Bufferes被引入使用。處理器把它想要寫入到主存的值寫到緩存,然后繼續(xù)去處理其他事情。當所有失效確認(Invalidate Acknowledge)都接收到時,數(shù)據(jù)才會最終被提交。這么做有兩個風險

        Store Bufferes的風險

        第一、就是處理器會嘗試從存儲緩存(Store buffer)中讀取值,但它還沒有進行提交。這個的解決方案稱為Store Forwarding,它使得加載的時候,如果存儲緩存中存在,則進行返回。第二、保存什么時候會完成,這個并沒有任何保證。

        value?=?3;

        void?exeToCPUA(){
        ??value?=?10;
        ??isFinsh?=?true;
        }
        void?exeToCPUB(){
        ??if(isFinsh){
        ????//value一定等于10?!
        ????assert?value?==?10;
        ??}
        }

        試想一下開始執(zhí)行時,CPU ?A保存著finished在E(獨享)狀態(tài),而value并沒有保存在它的緩存中。(例如,Invalid)。在這種情況下,value會比finished更遲地拋棄存儲緩存。完全有可能CPU B讀取finished的值為true,而value的值不等于10。

        即isFinsh的賦值在value賦值之前。

        這種在可識別的行為中發(fā)生的變化稱為重排序(reordings)。注意,這不意味著你的指令的位置被惡意(或者好意)地更改。

        它只是意味著其他的CPU會讀到跟程序中寫入的順序不一樣的結果。

        順便提一下NIO的設計和Store Bufferes的設計是非常相像的。

        硬件內存模型

        執(zhí)行失效也不是一個簡單的操作,它需要處理器去處理。另外,存儲緩存(Store Buffers)并不是無窮大的,所以處理器有時需要等待失效確認的返回。這兩個操作都會使得性能大幅降低。為了應付這種情況,引入了失效隊列。它們的約定如下:

        • 對于所有的收到的Invalidate請求,Invalidate Acknowlege消息必須立刻發(fā)送
        • Invalidate并不真正執(zhí)行,而是被放在一個特殊的隊列中,在方便的時候才會去執(zhí)行。
        • 處理器不會發(fā)送任何消息給所處理的緩存條目,直到它處理Invalidate。

        即便是這樣處理器已然不知道什么時候優(yōu)化是允許的,而什么時候并不允許。干脆處理器將這個任務丟給了寫代碼的人。這就是內存屏障(Memory Barriers)。

        寫屏障

        寫屏障 Store Memory Barrier(a.k.a. ST, SMB, smp_wmb)是一條告訴處理器在執(zhí)行這之后的指令之前,應用所有已經在存儲緩存(store buffer)中的保存的指令。

        讀屏障

        讀屏障Load Memory Barrier (a.k.a. LD, RMB, smp_rmb)是一條告訴處理器在執(zhí)行任何的加載前,先應用所有已經在失效隊列中的失效操作的指令。

        void?executedOnCpu0()?{
        ????value?=?10;
        ????//在更新數(shù)據(jù)之前必須將所有存儲緩存(store buffer)中的指令執(zhí)行完畢。
        ????storeMemoryBarrier();
        ????finished?=?true;
        }
        void?executedOnCpu1()?{
        ????while(!finished);
        ????//在讀取之前將所有失效隊列中關于該數(shù)據(jù)的指令執(zhí)行完畢。
        ????loadMemoryBarrier();
        ????assert?value?==?10;
        }

        現(xiàn)在確實安全了。完美無暇!

        轉自:cnblogs.com/yanlong300/p/8986041.html



        終于有人Docker講清楚了,網(wǎng)工別再說你不懂了!


        【硬核】教你幾招,如何快速把一個團隊搞垮?


        一看就懂| 如何用 Kubernetes 實現(xiàn) CI/CD 發(fā)布流程?


        瀏覽 165
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产91探花在线观看 | 女人自己扒开荫道口视频鼓包 | 婷婷在线成人视频精品 | 国产女人高潮视频 | 成人91网站午夜视频 | 女人18片免费视频网站 | 91亚洲狠狠色综合久久久久久久 | 中文字幕无限2021 | 91无码视频 | 国产一级一级片 |