你不知道的記憶體知識

zmy愛吃炸雞發表於2020-10-21

世界上最快的捷徑,就是腳踏實地,本文已收錄【架構技術專欄】關注這個喜歡分享的地方。

一、CPU與記憶體

先鋪墊幾個概念,以免後面混亂:

  • Socket或Processor: 指一個物理CPU晶片,盒裝還是散裝的。上面有很多針腳,直接安裝在主機板上。

  • Core : 指在Processor裡封裝一個CPU核心,每個Core都是完全獨立的計算單元,我們平時說的4核心CPU,指的就是Processor裡面封裝了4個Core。

  • HT超執行緒: 目前Intel與AMD的Processor大多支援在一個Core裡並行執行兩個執行緒,此時從作業系統看就相當於兩個邏輯CPU(Logical Processor)。大多數情況下,我們程式裡提到的CPU概念就是指的這個Logical Processor。

我們們先來看幾個問題:

1、CPU可以直接操作記憶體嗎?

可能一大部分老鐵肯定會說:肯定的啊,不能操作記憶體怎麼讀取資料呢。

其實如果我們用這聰明的大腦想一想,我們們的臺式主機大家肯定都玩過。上面CPU和記憶體條是兩個完全獨立的硬體啊,而且CPU也沒有任何直接插槽用於掛載記憶體條的。

也就是說,CPU和記憶體條是物理隔離的,CPU並不能直接的訪問記憶體條,而是需要藉助主機板上的其他硬體間接的來實現訪問。

2、CPU的運算速度和記憶體條的訪問速度差距有多大?

呵呵呵,這麼說吧,就是一個鴻溝啊,CPU的運算速度與記憶體訪問速度之間的差距是100倍。

而由於CPU與記憶體之間的速度差存在N個數量級的巨大鴻溝,於是CPU最親密的小夥伴Cache 閃亮登場了。與DRAM 家族的記憶體(Memory)不同,Cache來自SRAM家族。

而DRAM與SRAM的最簡單區別就是後者特別快,容量特別小,電路結構非常複雜,造價特別高。

而Cache與主記憶體之間的巨大效能差距主要還是工作原理與結構不同:

  • DRAM儲存一位資料只需要一個電容加一個電晶體,SRAM則需要6個電晶體。

  • 由於DRAM的資料其實是被儲存在電容裡的,所以每次讀寫過程中的充放電環節也導致了DRAM讀寫資料有一個延時的問題,這個延時通常為十幾到幾十ns。

  • 記憶體可以被看作一個二維陣列,每個儲存單元都有其行地址和列地址。

    由於SRAM的容量很小,所以儲存單元的地址(行與列)比較短,可以被一次性傳輸到SRAM中。DRAM則需要分別傳送行與列的地址。

  • SRAM的頻率基本與CPU的頻率保持一致,而DRAM的頻率直到DDR4以後才開始接近CPU的頻率。

3、Cache 是怎麼使用的?

其實Cache 是被整合到CPU內部的一個儲存單元(平時也被我們稱為快取記憶體),由於其造價昂貴,並且儲存容量遠遠不能滿足CPU大量、高速存取的需求。

所以出於對成本的控制,在現實中往往採用金字塔形的多級Cache體系來實現最佳快取效果。

於是出現了,一級Cache(L1 Cache)、二級Cache(L2 Cache)及三級Cache(L3 Cache)。每一級都犧牲了部分效能指標來換取更大的容量,目的也是儲存更多的熱點資料。

以Intel家族Intel SandyBridge架構的CPU為例:

  • L1 Cache容量為64KB,訪問速度為1ns左右

  • L2Cache容量擴大4倍,達到256KB,訪問速度則降低到3ns左右

  • L3 Cache的容量則擴大512倍,達到32MB,訪問速度也下降到12ns左右(也比訪問主存的105ns(40ns+65ns)快一個數量級)

L3 Cache是被一個Socket上的所有CPU Core共享的,其實最早的L3 Cache被應用在AMD釋出的K6-III處理器上,當時的L3 Cache受限於製造工藝,並沒有被整合到CPU內部,而是被整合在主機板上,如圖:

從上圖我們也能看出來,CPU如果要訪問記憶體中的資料,則需要經過L1、L2、L3三道關卡,就是這三個Cache中都沒有需要的資料,才會從主記憶體中直接進行讀取。

最後我們來看下Intel Sandy Bridge CPU的架構圖:

二、多核CPU與記憶體共享的問題

問題: Cache一致性問題

多核CPU共享記憶體的問題也被稱為Cache一致性問題。

其實就是多個CPU核心看到的Cache資料應該是一致的,在某個資料被某個CPU寫入自己的Cache(L1 Cache)以後,其他CPU都應該能看到相同的Cache資料。

如果在自己的Cache中有舊資料,則拋棄舊資料。

考慮到每個CPU都有自己內部獨佔的Cache,所以這個問題與分散式Cache保持同步的問題是同一類問題

目前業界公認的解決一致性問題的最佳方案就是Intel 的MESI協議了,大多數SMP架構都採用了這一方案。

解決方案:MESI

不知道大家還記得Cache Line 嗎,就是我們常說的快取記憶體中快取條目裡面的那個快取行。

其實仔細想想,在進行I/O操作從來不以位元組為單位,而是以塊為單位,有兩個原因:

  • I/O 操作比較慢,所以讀一個位元組與讀連續N個位元組的花費時間基本相同
  • 資料訪問一般都具有空間連續的特徵

所以CPU針對Memory的讀寫也採用了類似於I/O塊的方式

實際上,CPU Cache(快取記憶體)裡最小的儲存單元就是Cache line(快取行),Intel CPU 的一個Cache Line儲存64個位元組。

每一級Cache都被劃分為很多組Cache Line,典型的情況就是4條Cache Line為一組。

當Cache從Memory中載入資料時,一次載入一條Cache Line的資料

如圖我們可以看到,每個Cache Line 頭部都有兩個Bit來標識自身狀態,總共四種:

  • M(Modified):修改狀態,在其他CPU上沒有資料的副本,並且在本CPU上被修改過,與儲存器中的資料不一致,最終必然會引發系統匯流排的寫指令,將Cache Line中的資料寫回Memory中。

  • E(Exclusive):獨佔狀態,表示當前Cache Line中的資料與Memory中的資料一致,此外,在其他CPU上沒有資料的副本。

  • S(Shared):共享狀態,表示Cache Line中的資料與Memory中的資料一致,而且當前CPU至少在其他某個CPU中有副本。

  • I(Invalid):無效狀態,在當前Cache Line中沒有有效資料或者該Cache Line資料已經失效,不能再用;當Cache要載入新資料時,優先選擇此狀態的Cache Line,此外,Cache Line的初始狀態也是I狀態

在對Cache(快取記憶體)的讀寫操作引發了Cache Line(快取行)的狀態變化,因而可以將其理解為一種狀態機模型。

但MESI的複雜和獨特之處在於狀態有兩種視角:

  • 一種是當前讀寫操作(Local Read/Write)所在CPU看到的自身的Cache Line狀態及其他CPU上對應的Cache Line狀態
  • 另一種是一個CPU上的Cache Line狀態的變遷會導致其他CPU上對應的Cache Line狀態變遷。

如下所示為MESI協議的狀態圖:

具體MESI的實現過程可以看我另一篇文章: 看懂這篇,才能說了解併發底層技術

深入理解不一致性記憶體

MESI協議解決了多核CPU下的Cache一致性問題,因而成為SMP架構的唯一選擇,而SMP架構近幾年迅速在PC領域(X86)發展。

SMP架構是一種平行的架構,所有CPU Core都被連線到一個記憶體匯流排上,它們平等訪問記憶體,同時整個記憶體是統一結構、統一定址的。

如下所示給出了SMP架構的示意圖:

隨著CPU核心數量的不斷增加,SMP架構也暴露出天生的短板,其根本瓶頸是共享記憶體匯流排的頻寬無法滿足CPU數量的增加,同時,在一條“馬路”上通行的“車”多了,難免會陷入“擁堵模式”。

不知道你是否聽說過匯流排風暴,可以看下:匯流排風暴

在這種情況下,分散式解決方案應運而生,系統的記憶體與CPU進行分割並捆綁在一起,形成多個獨立的子系統,這些子系統之間高速互聯,這就是NUMA(None Uniform Memory Architecture)架構,如下圖所示。

可以看出,NUMA架構中的記憶體被分割為獨立的幾塊,被不同CPU私有化了。

因此在CPU訪問自家記憶體的時候會非常快,在訪問其他CPU控制的記憶體資料時,則需要通過內部互聯通道訪問。

NUMA架構的優點就是其伸縮性,就算擴充套件到幾百個CPU也不會導致性嚴重的下降。

NUMA技術的特點

在NUMA架構中引入了一個重要的新名詞——Node

一個Node由一個或者多個Socket Socket組成,即物理上的一個或多個CPU晶片組成一個邏輯上的Node

我們來看一個Dell PowerEdge系列伺服器的NUMA的架構圖:

從上圖可以看出其特點:

  • 4個處理器形成4個獨立的NUMA Node由於每個Node都為8 Core,支援雙執行緒

  • 每個Node裡的Logic CPU數量都為16個,佔每個Node分配系統總記憶體的1/4

  • 每個Node之間都通過Intel QPI(QuickPath Interconnect)技術形成了點到點的全互聯處理器系統

NUMA這種基於點到點的全互聯處理器系統與傳統的基於共享匯流排的處理器系統的SMP還是有巨大差異的。

在這種情況下無法通過嗅探匯流排的方式來實現Cache一致性,因此為了實現NUMA架構下的Cache一致性,Intel引入了MESI協議的一個擴充套件協議——MESIF

針對NUMA的支援

NUMA架構打破了傳統的“全域性記憶體”概念,目前還沒有任意一種程式語言從記憶體模型上支援它,當前也很難開發適應NUMA的軟體。

Java在支援NUMA的系統裡,可以開啟基於NUMA的記憶體分配方案,使得當前執行緒所需的記憶體從對應的Node上分配,從而大大加快物件的建立過程

在大資料領域,NUMA系統正發揮著越來越強大的作用,SAP的高階大資料系統HANA被SGI在其UV NUMA Systems上實現了良好的水平擴充套件

在雲端計算與虛擬化方面,OpenStack與VMware已經支援基於NUMA技術的虛機分配能力,使得不同的虛機執行在不同的Core上,同時虛機的記憶體不會跨越多個NUMA Node

相關文章