現在的服務器物理機CPU一般都是多個CPU,核數也是十幾甚至幾十核。內存幾十GB甚至是上百G,也是由許多的內存條組成的。那么我這里思考一下,這么多的CPU和內存它們之間是怎么互相連接的?同一個CPU核訪問不同的內存條延時一樣嗎?
在《內存隨機訪問也比順序慢,帶你深入理解內存IO過程》中我們了解了內存訪問時芯片內部的執行過程,在《實際測試內存在順序IO和隨機IO時的訪問延時差異》中我們又進行了實際的代碼測試。不過這兩文中我們都把精力聚焦在內存內部機制,而回避了上面的問題,那就是CPU和內存的連接方式,也就是總線架構。
回顧CPU與內存的簡單連接:FSB時代
我們先來回顧下在歷史上CPU、內存數量比較少的年代里的總線方案-FSB。FSB的全稱是Front Side Bus,因此也叫前端總線。CPU通過FSB總線連接到北橋芯片,然后再連接到內存。內存控制器是集成在北橋里的,Cpu和內存之間的通信全部都要通過這一條FSB總線來進行。
在這個年代里,當時提高計算機系統整體性能的方式就是不斷地提高CPU、FSB總線、內存條的數據傳輸頻率。
如今多CPU多內存條復雜互聯:NUMA時代
當CPU的主頻提升到了3GHz每秒以后,硬件制造商們發現單個CPU的已經到了物理極限了。所以就改變了性能改進的方法,改成為向多核、甚至是多CPU的方向來發展。在這種情況下,如果仍然采用FSB總線,會導致所有的CPU和內存通信都經過總線,這樣總線就成為了瓶頸,無法充分發揮多核的優勢與性能。所以CPU制造商們把內存控制器從北橋搬到了CPU內部,這樣CPU便可以直接和自己的內存進行通信了。那么,如果CPU想要訪問不和自己直連的內存條怎么辦呢?所以就誕生了新的總線類型,它就叫QPI總線。
圖中CPU1如果想要訪問內存3的話,就需要經過QPS總線才可以。
動手查看Linux下NUMA架構
我們先通過dmidecode命令查看一下內存插槽,單條大小等信息。大家可以試着在linux上執行以下該命令。輸出結果很長,大家可以有空仔細研究。我這里不全部介紹,這里只挑選一些和內存相關的:
# dmidecode|grep -P -A5 "Memory\s+Device"|grep Size
Size: 8192 MB
Size: 8192 MB
Size: No Module Installed
Size: 8192 MB
Size: No Module Installed
Size: 8192 MB
Size: 8192 MB
Size: 8192 MB
Size: No Module Installed
Size: 8192 MB
Size: No Module Installed
Size: 8192 MB
可以看出,我當前使用的機器上共有16個內存插槽,共插了8條8G的內存。所以總共是64GB。如我們前面所述,在NUMA架構里,每一個物理CPU都有不同的內存組,通過numactl
命令可以查看這個分組情況。
# numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 12 13 14 15 16 17
node 0 size: 32756 MB
node 0 free: 19642 MB
node 1 cpus: 6 7 8 9 10 11 18 19 20 21 22 23
node 1 size: 32768 MB
node 1 free: 18652 MB
node distances:
node 0 1
0: 10 21
1: 21 10
通過上述命令可以看到,每一組CPU核分配了32GB(4條)的內存。 node distance
是一個二維矩陣,描述node訪問所有內存條的延時情況。 node 0
里的CPU訪問node 0
里的內存相對距離是10
,因為這時訪問的內存都是和該CPU直連的。而node 0
如果想訪問node 1
節點下的內存的話,就需要走QPI總線了,這時該相對距離就變成了21
。
所以、在NUMA架構下,CPU訪問自己同一個node里的內存要比其它內存要快!
動手測試NUMA架構內存延遲差異
numactl
命令有--cpubind
和--membind
的選項,通過它們我們可以指定我們要用的node節點。還沿用《用代碼讓你來實際感受內存的在不同情況下的訪問延時差異》里的測試代碼
1、讓內存和CPU處於同一個node
# numactl --cpubind=0 --membind=0 ./main
Delay (ns)
2k 8k 32k 128k 512k 2m 8m 32m 128m
s1 1.28 1.28 1.26 1.25 1.26 1.26 1.28 1.43 1.43
s32 1.27 1.26 1.32 1.78 2.67 2.73 3.27 9.95 10.37
s64 1.28 1.26 1.26 1.82 2.43 2.48 3.15 8.82 8.92
andom 2.40 2.40 2.40 2.40 4.80 4.80 19.20 28.80 52.80
2、讓內存和CPU處於不同node
# numactl --cpubind=0 --membind=1 ./main
Delay (ns)
2k 8k 32k 128k 512k 2m 8m 32m 128m
s1 1.29 1.28 1.26 1.26 1.26 1.26 1.31 1.62 1.63
s32 1.29 1.26 1.33 1.77 2.80 2.92 3.95 13.69 13.77
s64 1.30 1.27 1.26 1.82 2.47 2.48 3.96 12.93 12.90
andom 2.40 2.40 2.40 2.40 4.80 4.80 19.20 31.20 52.80
結論
通過上面的各個小節我們可以看到,現代的服務器里,CPU和內存條都有多個,它們之前目前主要采用的是復雜的NUMA架構進行互聯,NUMA把服務器里的CPU和內存分組划分成了不同的node。從上述實驗結果來看,拿8M數組,循環步長為64的case來說,同node耗時3.15納秒,跨node為3.96納秒。所以屬於同一個node里的CPU和內存之間訪問速度會比較快。而如果跨node的話,則需要經過QPI總線,總體來說,速度會略慢一些。
開發內功修煉之內存篇專輯:
- 1.帶你深入理解內存對齊最底層原理
- 2.內存隨機也比順序訪問慢,帶你深入理解內存IO過程
- 3.從DDR到DDR4,內存核心頻率其實基本上就沒太大的進步
- 4.實際測試內存在順序IO和隨機IO時的訪問延時差異
- 5.揭穿內存廠家“謊言”,實測內存帶寬真實表現
- 6.NUMA架構下的內存訪問延遲區別!
- 7.PHP7內存性能優化的思想精髓
- 8.一次內存性能提升的項目實踐
- 9.挑戰Redis單實例內存最大極限,“遭遇”NUMA陷阱!
我的公眾號是「開發內功修煉」,在這里我不是單純介紹技術理論,也不只介紹實踐經驗。而是把理論與實踐結合起來,用實踐加深對理論的理解、用理論提高你的技術實踐能力。歡迎你來關注我的公眾號,也請分享給你的好友~~~