Linux內(nèi)核揭秘:NUMA節(jié)點(diǎn)探測(cè)背后的故事
在當(dāng)今的計(jì)算機(jī)世界里,隨著硬件性能的不斷提升,Linux 內(nèi)核也在持續(xù)進(jìn)化以充分發(fā)揮硬件的潛力。而今天,我們要深入探討的是 Linux 內(nèi)核中一個(gè)至關(guān)重要但又常常被忽視的領(lǐng)域 ——NUMA 節(jié)點(diǎn)探測(cè)。你是否曾好奇,當(dāng)我們的服務(wù)器擁有多個(gè) CPU 和大量?jī)?nèi)存時(shí),系統(tǒng)是如何高效地管理和分配這些資源的呢?這背后的功臣之一就是 NUMA 架構(gòu)。想象一下,在一個(gè)龐大的數(shù)據(jù)中心里,眾多服務(wù)器協(xié)同工作,如果不能精準(zhǔn)地掌握 NUMA 節(jié)點(diǎn)的情況,就如同在一個(gè)巨大的倉(cāng)庫(kù)中盲目地尋找貨物,效率低下且容易出錯(cuò)。
Linux 內(nèi)核中的 NUMA 節(jié)點(diǎn)探測(cè)技術(shù),就像是一把神奇的鑰匙,能夠幫助我們打開(kāi)了解系統(tǒng)內(nèi)存架構(gòu)的大門(mén)。它讓我們清楚地知道每個(gè) CPU 與內(nèi)存之間的親和關(guān)系,以及數(shù)據(jù)在不同節(jié)點(diǎn)之間的流動(dòng)情況。無(wú)論是優(yōu)化服務(wù)器性能,還是解決復(fù)雜的系統(tǒng)故障,NUMA 節(jié)點(diǎn)探測(cè)都能為我們提供關(guān)鍵的線(xiàn)索和方向。接下來(lái),就讓我們一起踏上這場(chǎng)探索 Linux 內(nèi)核 NUMA 節(jié)點(diǎn)探測(cè)的奇妙之旅,揭開(kāi)其神秘的面紗,看看它是如何在幕后默默工作,保障我們系統(tǒng)高效穩(wěn)定運(yùn)行的。
一、什么是NUMA
傳統(tǒng)的SMP對(duì)稱(chēng)多處理器中,所有處理器都共享系統(tǒng)總線(xiàn),因此當(dāng)處理器的數(shù)目增大時(shí),系統(tǒng)總線(xiàn)的競(jìng)爭(zhēng)沖突加大,系統(tǒng)總線(xiàn)將成為瓶頸,所以目前SMP系統(tǒng)的CPU數(shù)目一般只有數(shù)十個(gè),可擴(kuò)展能力受到極大限制;NUMA技術(shù)有效結(jié)合了SMP系統(tǒng)易編程性和MPP(大規(guī)模并行)系統(tǒng)易擴(kuò)展性的特點(diǎn),較好解決了SMP系統(tǒng)的可擴(kuò)展性問(wèn)題,已成為當(dāng)今高性能服務(wù)器的主流體系結(jié)構(gòu)之一。
基于NUMA架構(gòu)的高性能服務(wù)器有HP的Superdome、SGI的Altix 3000、IBM的 x440、NEC的TX7、AMD的Opteron等;NUMA(Non Uniform Memory Access)技術(shù)可以使眾多服務(wù)器像單一系統(tǒng)那樣運(yùn)轉(zhuǎn),同時(shí)保留小系統(tǒng)便于編程和管理的優(yōu)點(diǎn)。
在早期,對(duì)于x86架構(gòu)的計(jì)算機(jī),那時(shí)的內(nèi)存控制器還沒(méi)有整合進(jìn)CPU,所有內(nèi)存的訪(fǎng)問(wèn)都需要通過(guò)北橋芯片來(lái)完成。此時(shí)的內(nèi)存訪(fǎng)問(wèn)如下圖所示,被稱(chēng)為UMA(uniform memory access, 一致性?xún)?nèi)存訪(fǎng)問(wèn))。這樣的訪(fǎng)問(wèn)對(duì)于軟件層面來(lái)說(shuō)非常容易實(shí)現(xiàn):總線(xiàn)模型保證了所有的內(nèi)存訪(fǎng)問(wèn)是一致的,不必考慮由不同內(nèi)存地址之前的差異。
之后的x86平臺(tái)經(jīng)歷了一場(chǎng)從“拼頻率”到“拼核心數(shù)”的轉(zhuǎn)變,越來(lái)越多的核心被盡可能地塞進(jìn)了同一塊芯片上,各個(gè)核心對(duì)于內(nèi)存帶寬的爭(zhēng)搶訪(fǎng)問(wèn)成為了瓶頸;此時(shí)軟件、OS方面對(duì)于SMP多核心CPU的支持也愈發(fā)成熟;再加上各種商業(yè)上的考量,x86平臺(tái)也順?biāo)浦鄣母懔薔UMA(Non-uniform memory access,非一致性?xún)?nèi)存訪(fǎng)問(wèn))。在這種架構(gòu)之下,每個(gè)Socket都會(huì)有一個(gè)獨(dú)立的內(nèi)存控制器IMC(integrated memory controllers,集成內(nèi)存控制器),分屬于不同的socket之內(nèi)的IMC之間通過(guò)QPI link通訊。
然后就是進(jìn)一步的架構(gòu)演進(jìn),由于每個(gè)socket上都會(huì)有多個(gè)core進(jìn)行內(nèi)存訪(fǎng)問(wèn),這就會(huì)在每個(gè)core的內(nèi)部出現(xiàn)一個(gè)類(lèi)似最早SMP架構(gòu)相似的內(nèi)存訪(fǎng)問(wèn)總線(xiàn),這個(gè)總線(xiàn)被稱(chēng)為IMC bus。
于是,很明顯的,在這種架構(gòu)之下,兩個(gè)socket各自管理1/2的內(nèi)存插槽,如果要訪(fǎng)問(wèn)不屬于本socket的內(nèi)存則必須通過(guò)QPI link 。也就是說(shuō)內(nèi)存的訪(fǎng)問(wèn)出現(xiàn)了本地/遠(yuǎn)程(local/remote )的概念,內(nèi)存的延時(shí)是會(huì)有顯著的區(qū)別的。這也是為什么在NUMA架構(gòu)下有些應(yīng)用性能反而更差的原因。
回到當(dāng)前世面上的CPU,工程上的實(shí)現(xiàn)其實(shí)更加復(fù)雜了。以來(lái)看,兩個(gè)Socket之之間通過(guò)各自的一條9.6GT/s的QPI link互訪(fǎng)。而每個(gè)Socket事實(shí)上有2個(gè)內(nèi)存控制器。雙通道的緣故,每個(gè)控制器又有兩個(gè)內(nèi)存通道(channel),每個(gè)通道最多支持3根內(nèi)存條(DIMM)。理論上最大單socket支持76.8GB/s的內(nèi)存帶寬,而兩個(gè)QPI link,每個(gè)QPI link有9.6GT/s的速率(~57.6GB/s)事實(shí)上QPI link已經(jīng)出現(xiàn)瓶頸了。
NUMA in Linux
對(duì)于NUMA系統(tǒng)來(lái)說(shuō),Linux會(huì)為每一個(gè)NUMA節(jié)點(diǎn)創(chuàng)建一套內(nèi)存管理對(duì)象的實(shí)例,每個(gè)節(jié)點(diǎn)包含DMA, DMA32, NORMAL等Zone。當(dāng)某個(gè)節(jié)點(diǎn)下的某個(gè)Zone無(wú)法滿(mǎn)足內(nèi)存分配請(qǐng)求時(shí),系統(tǒng)會(huì)咨詢(xún)zonelist進(jìn)而決定后備Zone的選擇順序。當(dāng)本地Zone NORMAL內(nèi)存不足時(shí),黙認(rèn)順序是從本地的Zone DMA32和DMA嘗試,然后再?lài)L試其它的節(jié)點(diǎn)。此順序可以由numa_zonelist_order參數(shù)更改,比如先去嘗試遠(yuǎn)程節(jié)點(diǎn)的Zone NORMAL以節(jié)省比較稀缺的Zone DMA32和DMA內(nèi)存(當(dāng)然,非黙認(rèn)NUMA policy有可能偏好遠(yuǎn)程節(jié)點(diǎn))。
二、NUMA系統(tǒng)架構(gòu)
2.1內(nèi)存管理的 “進(jìn)化之路”
⑴SMP 架構(gòu)的困境
在早期的計(jì)算機(jī)系統(tǒng)中,隨著應(yīng)用對(duì)計(jì)算性能需求的不斷攀升,多處理器技術(shù)應(yīng)運(yùn)而生,其中對(duì)稱(chēng)多處理(SMP)架構(gòu)備受矚目。在 SMP 架構(gòu)下,多個(gè)處理器平等地連接到同一條共享內(nèi)存總線(xiàn)上,共享同一物理內(nèi)存空間,就像一群小伙伴共同圍繞著一個(gè)公共的玩具箱,每個(gè)人都能平等地從中拿取玩具。這種架構(gòu)的設(shè)計(jì)初衷是為了充分利用多個(gè)處理器的并行計(jì)算能力,通過(guò)操作系統(tǒng)的調(diào)度,讓不同的處理器協(xié)同處理各種任務(wù),從而提升系統(tǒng)的整體性能。
然而,隨著處理器核心數(shù)量的持續(xù)增加,SMP 架構(gòu)逐漸暴露出嚴(yán)重的性能瓶頸。想象一下,當(dāng)眾多小伙伴同時(shí)沖向玩具箱想要取出自己心儀的玩具時(shí),共享內(nèi)存總線(xiàn)就如同那狹窄的玩具箱開(kāi)口,成為了激烈競(jìng)爭(zhēng)的焦點(diǎn)。多個(gè)處理器頻繁地同時(shí)訪(fǎng)問(wèn)內(nèi)存,導(dǎo)致總線(xiàn)爭(zhēng)用異常激烈,內(nèi)存訪(fǎng)問(wèn)延遲急劇上升。在高并發(fā)場(chǎng)景下,為了保證數(shù)據(jù)的一致性,處理器往往需要使用原子指令來(lái)訪(fǎng)問(wèn)內(nèi)存,例如通過(guò)鎖總線(xiàn)的方式獨(dú)占內(nèi)存訪(fǎng)問(wèn)權(quán)。這就好比小伙伴們?cè)跔?zhēng)搶玩具時(shí),有人直接把玩具箱的開(kāi)口堵住,不讓其他人拿玩具,直到自己拿到為止,使得其他處理器只能干巴巴地等待,造成大量的處理器資源閑置浪費(fèi),系統(tǒng)整體性能大打折扣。
以一個(gè)典型的數(shù)據(jù)庫(kù)服務(wù)器為例,在處理大量并發(fā)事務(wù)時(shí),多個(gè)處理器核心需要頻繁讀寫(xiě)內(nèi)存中的數(shù)據(jù)塊。由于 SMP 架構(gòu)下內(nèi)存總線(xiàn)的爭(zhēng)用,處理器常常需要等待很長(zhǎng)時(shí)間才能獲取到所需的數(shù)據(jù),導(dǎo)致事務(wù)處理的響應(yīng)時(shí)間大幅增加,系統(tǒng)吞吐量急劇下降,無(wú)法滿(mǎn)足業(yè)務(wù)對(duì)高性能的要求。這種內(nèi)存訪(fǎng)問(wèn)瓶頸嚴(yán)重制約了 SMP 架構(gòu)在大規(guī)模計(jì)算場(chǎng)景下的應(yīng)用,迫切需要一種新的內(nèi)存架構(gòu)來(lái)打破這一困境。
⑵NUMA 架構(gòu)應(yīng)運(yùn)而生
為了突破 SMP 架構(gòu)在內(nèi)存訪(fǎng)問(wèn)方面的瓶頸,非統(tǒng)一內(nèi)存訪(fǎng)問(wèn)(NUMA)架構(gòu)應(yīng)運(yùn)而生。它像是給計(jì)算機(jī)系統(tǒng)重新規(guī)劃了一個(gè)更合理的 “居住布局”,將整個(gè)系統(tǒng)劃分為多個(gè)節(jié)點(diǎn)(Node),每個(gè)節(jié)點(diǎn)都配備了自己的本地內(nèi)存、處理器以及 I/O 設(shè)備,節(jié)點(diǎn)之間則通過(guò)高速互連網(wǎng)絡(luò)進(jìn)行通信,就如同在一個(gè)大型社區(qū)里,劃分出了多個(gè)相對(duì)獨(dú)立的小區(qū),每個(gè)小區(qū)都有自己的配套設(shè)施,小區(qū)之間有便捷的道路相連。
在 NUMA 架構(gòu)中,處理器訪(fǎng)問(wèn)本地內(nèi)存的速度遠(yuǎn)遠(yuǎn)快于訪(fǎng)問(wèn)其他節(jié)點(diǎn)的遠(yuǎn)程內(nèi)存,這是因?yàn)楸镜貎?nèi)存與處理器之間的物理距離更近,數(shù)據(jù)傳輸延遲更低,就像小區(qū)居民在自家樓下的小超市購(gòu)物,方便快捷;而訪(fǎng)問(wèn)其他節(jié)點(diǎn)的內(nèi)存則像是要跑到隔壁小區(qū)的超市購(gòu)物,需要經(jīng)過(guò)一段路程,花費(fèi)更多的時(shí)間。這種內(nèi)存訪(fǎng)問(wèn)的非一致性特性,使得 NUMA 架構(gòu)能夠有效地減少內(nèi)存總線(xiàn)的爭(zhēng)用,提高內(nèi)存訪(fǎng)問(wèn)的并行性,進(jìn)而提升系統(tǒng)的整體性能。
與 SMP 架構(gòu)相比,NUMA 架構(gòu)最大的不同在于其內(nèi)存訪(fǎng)問(wèn)的非對(duì)稱(chēng)性。SMP 架構(gòu)下,所有處理器對(duì)內(nèi)存的訪(fǎng)問(wèn)延遲是一致的,就像所有居民到公共玩具箱的距離都一樣遠(yuǎn);而 NUMA 架構(gòu)中,不同節(jié)點(diǎn)的內(nèi)存訪(fǎng)問(wèn)延遲存在差異,處理器會(huì)優(yōu)先訪(fǎng)問(wèn)本地內(nèi)存,以獲取更快的數(shù)據(jù)讀寫(xiě)速度。這種差異使得 NUMA 架構(gòu)在處理大規(guī)模數(shù)據(jù)密集型應(yīng)用時(shí)具有顯著優(yōu)勢(shì),能夠更好地適應(yīng)現(xiàn)代計(jì)算機(jī)系統(tǒng)對(duì)高性能、高擴(kuò)展性的需求。例如,在大規(guī)??茖W(xué)計(jì)算、云計(jì)算數(shù)據(jù)中心等場(chǎng)景中,NUMA 架構(gòu)能夠充分發(fā)揮各個(gè)節(jié)點(diǎn)的計(jì)算能力,高效地處理海量數(shù)據(jù),為用戶(hù)提供快速、穩(wěn)定的服務(wù)。
2.2Linux 內(nèi)核中的 NUMA 架構(gòu) “畫(huà)像”
⑴節(jié)點(diǎn)的組織與表示
在 Linux 內(nèi)核的世界里,對(duì)于 NUMA 架構(gòu)的支持可謂是精心設(shè)計(jì)、精妙絕倫。每個(gè) NUMA 節(jié)點(diǎn)在內(nèi)核中是由結(jié)構(gòu)體 pglist_data(在老版本內(nèi)核中叫 pg_data_t,本質(zhì)相同)來(lái)進(jìn)行描述的,它就像是每個(gè)節(jié)點(diǎn)的 “管家”,掌管著節(jié)點(diǎn)內(nèi)諸多關(guān)鍵信息。這個(gè)結(jié)構(gòu)體包含了一個(gè)名為 node_zones 的數(shù)組,其類(lèi)型為 struct zone,這便是內(nèi)存區(qū)域的 “收納盒”,每個(gè)節(jié)點(diǎn)內(nèi)不同特性的內(nèi)存區(qū)域都被收納其中。
為了兼容不同硬件設(shè)備五花八門(mén)的特性以及應(yīng)對(duì) 32 位、64 位系統(tǒng)各自的需求,Linux 內(nèi)核將內(nèi)存劃分成了不同的區(qū)域類(lèi)型。常見(jiàn)的有 ZONE_DMA、ZONE_DMA32、ZONE_NORMAL 等。ZONE_DMA 區(qū)域,通常是低 16M 的內(nèi)存范圍,它可是專(zhuān)為那些支持直接內(nèi)存訪(fǎng)問(wèn)(DMA)的設(shè)備量身定制的。因?yàn)橛行├吓f的 DMA 控制器,它們只能訪(fǎng)問(wèn)這低 16M 的內(nèi)存空間,所以?xún)?nèi)核特意劃分出這片區(qū)域,以確保這些設(shè)備能夠順暢地與內(nèi)存交互,實(shí)現(xiàn)數(shù)據(jù)的高速傳輸,就好比為特殊需求的客人預(yù)留了特定的通道。
隨著硬件的發(fā)展,64 位系統(tǒng)登上舞臺(tái),一些新的 DMA 設(shè)備能夠訪(fǎng)問(wèn)更廣泛的內(nèi)存空間,但又達(dá)不到完整的 4G 范圍,于是 ZONE_DMA32 應(yīng)運(yùn)而生,它主要服務(wù)于這些較新的、能訪(fǎng)問(wèn) 4G 以?xún)?nèi)內(nèi)存的 DMA 設(shè)備,為它們提供了專(zhuān)屬的 “棲息地”。
而 ZONE_NORMAL 區(qū)域,則涵蓋了 16M 到 896M(在 32 位系統(tǒng)且開(kāi)啟物理地址擴(kuò)展 PAE 的情況下)或者更大范圍(64 位系統(tǒng))的內(nèi)存,這片區(qū)域的內(nèi)存可以直接映射到內(nèi)核的虛擬地址空間,內(nèi)核能夠直接、高效地對(duì)其進(jìn)行訪(fǎng)問(wèn),就像是家里的 “常用物品存放區(qū)”,取用物品極為便捷,是內(nèi)核日常運(yùn)行時(shí)頻繁使用的內(nèi)存 “主力軍”。
不同的內(nèi)存區(qū)域有著不同的使命,它們緊密協(xié)作,與硬件設(shè)備默契配合,為整個(gè)系統(tǒng)的穩(wěn)定高效運(yùn)行奠定了堅(jiān)實(shí)基礎(chǔ)。這種精細(xì)的內(nèi)存區(qū)域劃分,充分展現(xiàn)了 Linux 內(nèi)核設(shè)計(jì)的前瞻性與兼容性,使得 Linux 能夠在各種硬件平臺(tái)上縱橫馳騁,大放異彩。
⑵內(nèi)存分配的 “策略藍(lán)圖”
當(dāng)系統(tǒng)需要分配內(nèi)存時(shí),Linux 內(nèi)核就像一位精明的調(diào)度大師,有著一套嚴(yán)謹(jǐn)且周全的策略。首先,它會(huì)依據(jù)預(yù)先計(jì)算好的節(jié)點(diǎn)距離信息,為內(nèi)存分配指引方向。每個(gè)節(jié)點(diǎn)與其他節(jié)點(diǎn)之間的距離都被精準(zhǔn)度量,這個(gè)距離可是影響內(nèi)存分配優(yōu)先級(jí)的關(guān)鍵因素。
以 ZONELIST_FALLBACK 策略為例,假設(shè)系統(tǒng)中有多個(gè) NUMA 節(jié)點(diǎn),節(jié)點(diǎn) 0 在分配內(nèi)存時(shí),會(huì)優(yōu)先查看自身節(jié)點(diǎn)的內(nèi)存情況。因?yàn)樵L(fǎng)問(wèn)自身節(jié)點(diǎn)的內(nèi)存就如同在自家院子里取東西,速度最快,延遲最低。要是自身節(jié)點(diǎn)內(nèi)存告急,無(wú)法滿(mǎn)足需求,內(nèi)核就會(huì)按照節(jié)點(diǎn)距離由近及遠(yuǎn)的順序,依次去相鄰節(jié)點(diǎn) “借” 內(nèi)存,比如先看向節(jié)點(diǎn) 1,再是節(jié)點(diǎn) 2、節(jié)點(diǎn) 3 等。這就好比先向隔壁鄰居求助,若鄰居也沒(méi)辦法,再往稍遠(yuǎn)一點(diǎn)的人家打聽(tīng)。
不過(guò),也有些特殊場(chǎng)景,比如使用 __GFP_THISNODE 標(biāo)志進(jìn)行內(nèi)存分配時(shí),就遵循 ZONELIST_NOFALLBACK 策略,意味著內(nèi)存分配只能在當(dāng)前 NUMA 節(jié)點(diǎn)內(nèi)進(jìn)行,哪怕內(nèi)存吃緊,也絕不 “外借”,有點(diǎn)像堅(jiān)守自家資源,自力更生的意思。
確定好節(jié)點(diǎn)后,接下來(lái)就要挑選具體的內(nèi)存區(qū)域了。這時(shí)候,node_zonelists 數(shù)組就派上了大用場(chǎng)。它就像是一張?jiān)敿?xì)的 “內(nèi)存地圖”,指引著內(nèi)核找到合適的內(nèi)存區(qū)域。在 ZONELIST_FALLBACK 策略下,對(duì)于節(jié)點(diǎn) 0 的 node_zonelists[ZONELIST_FALLBACK],其內(nèi)部的 zoneref 元素會(huì)按照節(jié)點(diǎn)距離排序,同時(shí)每個(gè)節(jié)點(diǎn)內(nèi)的內(nèi)存區(qū)域又依據(jù)優(yōu)先級(jí)從高到低排列,通常是 ZONE_NORMAL 優(yōu)先級(jí)較高,優(yōu)先被考慮,其次是 ZONE_DMA32,最后是 ZONE_DMA。這是因?yàn)?ZONE_NORMAL 區(qū)域的內(nèi)存使用最為頻繁、便捷,而 ZONE_DMA 區(qū)域相對(duì)較為特殊,只用于特定的 DMA 設(shè)備,不能輕易動(dòng)用。
當(dāng)進(jìn)程申請(qǐng)內(nèi)存時(shí),內(nèi)核會(huì)從這張 “地圖” 的起始位置開(kāi)始查找,優(yōu)先鎖定距離最近節(jié)點(diǎn)中的最高優(yōu)先級(jí)內(nèi)存區(qū)域。若該區(qū)域內(nèi)存不足,才會(huì)按照既定順序,逐步往低優(yōu)先級(jí)區(qū)域或者更遠(yuǎn)節(jié)點(diǎn)的內(nèi)存區(qū)域探索,直到找到滿(mǎn)足需求的內(nèi)存為止。如此精細(xì)復(fù)雜的內(nèi)存分配策略,確保了在 NUMA 架構(gòu)下,系統(tǒng)能夠充分利用各個(gè)節(jié)點(diǎn)、各個(gè)區(qū)域的內(nèi)存資源,達(dá)到性能的最優(yōu)平衡,讓計(jì)算機(jī)系統(tǒng)在多任務(wù)、高負(fù)載的復(fù)雜環(huán)境下依然能夠穩(wěn)健運(yùn)行,高效處理各種數(shù)據(jù)與任務(wù)。
三、NUMA核心技術(shù)
三種系統(tǒng)架構(gòu) & 兩種存儲(chǔ)器共享方式
從系統(tǒng)架構(gòu)來(lái)看,目前的商用服務(wù)器大體可以分為三類(lèi):
- 對(duì)稱(chēng)多處理器結(jié)構(gòu)(SMP:Symmetric Multi-Processor)
- 非一致存儲(chǔ)訪(fǎng)問(wèn)結(jié)構(gòu)(NUMA:Non-Uniform Memory Access)
- 海量并行處理結(jié)構(gòu)(MPP:Massive Parallel Processing)。
共享存儲(chǔ)型多處理機(jī)有兩種技術(shù):
- 均勻存儲(chǔ)器存取(Uniform-Memory-Access,簡(jiǎn)稱(chēng)UMA)技術(shù)
- 非均勻存儲(chǔ)器存?。∟onuniform-Memory-Access,簡(jiǎn)稱(chēng)NUMA)技術(shù)
3.1UMA技術(shù)
UMA是并行計(jì)算機(jī)中的共享存儲(chǔ)架構(gòu),即物理存儲(chǔ)器被所有處理機(jī)均勻共享,對(duì)所有存儲(chǔ)字具有相同的存取時(shí)間。每臺(tái)處理機(jī)可以有私用高速緩存,外圍設(shè)備也以一定形式共享。UMA技術(shù)適合于普通需求和多用戶(hù)共享時(shí)間的應(yīng)用,在時(shí)序要求嚴(yán)格的應(yīng)用中,被用作加速單一大型程序的執(zhí)行率。
3.2NUMA技術(shù)
NUMA是用于多進(jìn)程計(jì)算中的存儲(chǔ)設(shè)計(jì),存儲(chǔ)讀取取決于當(dāng)前存儲(chǔ)器與處理器的關(guān)聯(lián)。在NUMA技術(shù)下,處理器訪(fǎng)問(wèn)本地存儲(chǔ)器比非本地存儲(chǔ)器(另一個(gè)處理器的本地存儲(chǔ)器或者處理器共享的存儲(chǔ)器)更快。
3.4vNUMA
vNUMA消除了VM和操作系統(tǒng)之間的透明性,并將NUMA架構(gòu)直通到VM的操作系統(tǒng)。值得一提的是,vNUMA在業(yè)內(nèi)與NUMA同樣盛名。對(duì)于一個(gè)廣泛VM技術(shù),VM運(yùn)行的底層架構(gòu),VM的NUMA拓?fù)淇缭蕉鄠€(gè)NUMA節(jié)點(diǎn)。在啟用了vNUMA的VM的初始功能之后,呈現(xiàn)給操作系統(tǒng)的架構(gòu)是永久定義的,并且不能被修改。這個(gè)限制通常是正面的,因?yàn)楦淖僾NUMA體系結(jié)構(gòu)可能會(huì)導(dǎo)致操作系統(tǒng)的不穩(wěn)定,但是如果VM通過(guò)vMotion遷移到帶有不同NUMA架構(gòu)的管理程序,則可能導(dǎo)致性能問(wèn)題。值得一提的是,盡管大多數(shù)應(yīng)用程序都可以利用vNUMA,但大多數(shù)VM都足夠小,可以裝入NUMA節(jié)點(diǎn);最近對(duì)寬-VM支持或vNUMA的優(yōu)化并不影響它們。
因此,客戶(hù)操作系統(tǒng)或它的應(yīng)用程序如何放置進(jìn)程和內(nèi)存會(huì)顯著影響性能。將NUMA拓?fù)浔┞督oVM的好處是,允許用戶(hù)根據(jù)底層NUMA架構(gòu)做出最優(yōu)決策。通過(guò)假設(shè)用戶(hù)操作系統(tǒng)將在暴露的vNUMA拓?fù)浣Y(jié)構(gòu)中做出最佳決策,而不是在NUMA客戶(hù)機(jī)之間插入內(nèi)存。
3.5NUMA的重要性
多線(xiàn)程應(yīng)用程序需要訪(fǎng)問(wèn)CPU核心的本地內(nèi)存,當(dāng)它必須使用遠(yuǎn)程內(nèi)存時(shí),性能將會(huì)受到延遲的影響。訪(fǎng)問(wèn)遠(yuǎn)程內(nèi)存要比本地內(nèi)存慢得多。所以使用NUMA會(huì)提高性能。現(xiàn)代操作系統(tǒng)試圖在NUMA節(jié)點(diǎn)(本地內(nèi)存+本地CPU=NUMA節(jié)點(diǎn))上調(diào)度進(jìn)程,進(jìn)程將使用本地NUMA節(jié)點(diǎn)訪(fǎng)問(wèn)核心。ESXi還使用NUMA技術(shù)為廣泛的虛擬機(jī),當(dāng)虛擬核心大于8時(shí),將虛擬核心分布在多個(gè)NUMA節(jié)點(diǎn)上。當(dāng)機(jī)器啟動(dòng)時(shí),虛擬核心將被分發(fā)到不同的NUMA節(jié)點(diǎn),它將提高性能,因?yàn)樘摂M核心將訪(fǎng)問(wèn)本地內(nèi)存。
四、探尋NUMA節(jié)點(diǎn)
4.1探測(cè)的 “魔法指令”
在 Linux 系統(tǒng)中,想要揭開(kāi) NUMA 節(jié)點(diǎn)的神秘面紗,查看其詳細(xì)信息,我們有一些非常實(shí)用的 “魔法指令”。就拿 numactl 來(lái)說(shuō),它堪稱(chēng)是探索 NUMA 架構(gòu)的得力助手。當(dāng)我們?cè)诮K端輸入 “numactl --hardware”,系統(tǒng)就如同一位貼心的導(dǎo)游,為我們展示出系統(tǒng)的 NUMA 拓?fù)淙皥D。從這幅圖中,我們能清晰知曉系統(tǒng)里究竟有多少個(gè) NUMA 節(jié)點(diǎn),它們就像是分布在計(jì)算機(jī)世界里的不同 “領(lǐng)地”。
每個(gè)節(jié)點(diǎn)配備的 CPU 核心數(shù)量也一目了然,這些 CPU 核心可是節(jié)點(diǎn)的 “主力軍”,肩負(fù)著處理各種任務(wù)的重任。內(nèi)存總量信息則讓我們對(duì)系統(tǒng)的存儲(chǔ)資源心中有數(shù),清楚每個(gè)節(jié)點(diǎn)能容納多少數(shù)據(jù) “寶藏”,以及當(dāng)前還有多少可用內(nèi)存,為資源分配提供關(guān)鍵參考。另外,節(jié)點(diǎn)之間的距離信息也十分關(guān)鍵,它直觀(guān)地反映了不同節(jié)點(diǎn)間內(nèi)存訪(fǎng)問(wèn)的 “路程遠(yuǎn)近”,幫助我們理解數(shù)據(jù)傳輸?shù)拈_(kāi)銷(xiāo)成本。
舉個(gè)例子,在一臺(tái)配置了雙路處理器、擁有兩個(gè) NUMA 節(jié)點(diǎn)的服務(wù)器上執(zhí)行此命令,可能會(huì)得到類(lèi)似這樣的結(jié)果:節(jié)點(diǎn) 0 擁有 8 個(gè) CPU 核心,內(nèi)存總量為 16GB,當(dāng)前空閑內(nèi)存 2GB,與節(jié)點(diǎn) 1 的距離為 20;節(jié)點(diǎn) 1 同樣有 8 個(gè) CPU 核心,內(nèi)存總量 16GB,空閑內(nèi)存 3GB,節(jié)點(diǎn)間距離相互對(duì)稱(chēng)。有了這些詳細(xì)信息,我們就能精準(zhǔn)把握系統(tǒng)資源布局,為后續(xù)的應(yīng)用部署、性能優(yōu)化提供有力依據(jù),讓系統(tǒng)運(yùn)行更加高效流暢。
除了 numactl,在 /sys/devices/system/node 目錄下也隱藏著諸多關(guān)于 NUMA 節(jié)點(diǎn)的 “情報(bào)”。這里面的每個(gè)以 “node” 開(kāi)頭的子目錄,都對(duì)應(yīng)著一個(gè)具體的 NUMA 節(jié)點(diǎn),仿佛是一個(gè)個(gè)裝滿(mǎn)信息的 “寶箱”。進(jìn)入這些子目錄,查看諸如 “cpulist” 文件,就能知曉該節(jié)點(diǎn)所關(guān)聯(lián)的 CPU 核心列表,就像拿到了節(jié)點(diǎn)的 “兵力部署圖”;“meminfo” 文件則詳細(xì)記錄著內(nèi)存的使用情況,包括已用內(nèi)存、空閑內(nèi)存等,是內(nèi)存資源的 “賬本”。這些文件里的數(shù)據(jù)實(shí)時(shí)更新,時(shí)刻反映著系統(tǒng)運(yùn)行過(guò)程中 NUMA 節(jié)點(diǎn)的動(dòng)態(tài)變化,為系統(tǒng)管理員、開(kāi)發(fā)者提供了一手的資源動(dòng)態(tài)信息,便于及時(shí)調(diào)整策略,保障系統(tǒng)穩(wěn)定高效運(yùn)行。
4.2代碼中的 “蛛絲馬跡”
倘若我們想要深入到 Linux 內(nèi)核的底層,從代碼層面去理解 NUMA 節(jié)點(diǎn)探測(cè)的原理,那就得走進(jìn)內(nèi)核源碼的 “神秘世界”。以常見(jiàn)的 64 位多核操作系統(tǒng)為例,在 Linux 內(nèi)核源碼里,有一系列關(guān)鍵的結(jié)構(gòu)體和函數(shù)在默默運(yùn)作。
首先是 numa_node_id() 函數(shù),它就像是一個(gè) “導(dǎo)航儀”,當(dāng)進(jìn)程在運(yùn)行過(guò)程中需要獲取當(dāng)前所處的 NUMA 節(jié)點(diǎn)編號(hào)時(shí),只要調(diào)用這個(gè)函數(shù),就能快速定位。它的實(shí)現(xiàn)原理涉及到對(duì)硬件寄存器、內(nèi)存映射等底層機(jī)制的巧妙運(yùn)用。在一些基于 Intel 架構(gòu)的系統(tǒng)中,處理器會(huì)通過(guò)特定的寄存器來(lái)記錄當(dāng)前訪(fǎng)問(wèn)內(nèi)存所對(duì)應(yīng)的 NUMA 節(jié)點(diǎn)信息,numa_node_id() 函數(shù)則會(huì)讀取這個(gè)寄存器的值,經(jīng)過(guò)簡(jiǎn)單的轉(zhuǎn)換和校驗(yàn),將準(zhǔn)確的節(jié)點(diǎn)編號(hào)返回給調(diào)用者,確保進(jìn)程能精準(zhǔn)知曉自己的 “歸屬地”。
再深入探究,struct pglist_data 結(jié)構(gòu)體中的諸多成員變量,為我們?nèi)轿唤沂玖?NUMA 節(jié)點(diǎn)的詳細(xì)信息。node_id 成員明確標(biāo)識(shí)了節(jié)點(diǎn)的唯一編號(hào),如同每個(gè)人的身份證號(hào),在整個(gè)系統(tǒng)中獨(dú)一無(wú)二;node_start_pfn 記錄著節(jié)點(diǎn)起始物理頁(yè)幀的編號(hào),這是內(nèi)存管理的重要基石,通過(guò)它可以快速定位節(jié)點(diǎn)內(nèi)存的起始位置,為內(nèi)存分配、回收等操作劃定邊界;node_spanned_pages 則精確統(tǒng)計(jì)了節(jié)點(diǎn)所跨越的物理頁(yè)幀數(shù)量,讓我們清楚了解每個(gè)節(jié)點(diǎn)的內(nèi)存容量大小,以便合理規(guī)劃資源。
當(dāng)系統(tǒng)啟動(dòng)初始化階段,內(nèi)核會(huì)逐個(gè)遍歷識(shí)別出的 NUMA 節(jié)點(diǎn),就像一位嚴(yán)謹(jǐn)?shù)钠詹閱T,對(duì)每個(gè)節(jié)點(diǎn)的硬件信息進(jìn)行仔細(xì)登記。通過(guò)讀取主板 BIOS 提供的 ACPI(高級(jí)配置與電源接口)表,獲取節(jié)點(diǎn)的 CPU 拓?fù)浣Y(jié)構(gòu)、內(nèi)存布局等關(guān)鍵信息,然后將這些信息填充到相應(yīng)的結(jié)構(gòu)體成員中,完成對(duì) struct pglist_data 結(jié)構(gòu)體的初始化。在后續(xù)的系統(tǒng)運(yùn)行過(guò)程中,內(nèi)核就依據(jù)這些初始化后的信息,有條不紊地進(jìn)行內(nèi)存管理、進(jìn)程調(diào)度等一系列復(fù)雜而關(guān)鍵的任務(wù),確保整個(gè)系統(tǒng)在 NUMA 架構(gòu)下高效協(xié)同運(yùn)行,為用戶(hù)提供流暢穩(wěn)定的使用體驗(yàn)。
五、NUMA節(jié)點(diǎn)實(shí)戰(zhàn)
5.1進(jìn)程與節(jié)點(diǎn)的 “綁定術(shù)”
了解了這么多關(guān)于 NUMA 節(jié)點(diǎn)的知識(shí),那如何在實(shí)際應(yīng)用中充分發(fā)揮它的優(yōu)勢(shì)呢?關(guān)鍵就在于將進(jìn)程合理地綁定到特定的 NUMA 節(jié)點(diǎn)或 CPU 核心上。
在 Linux 系統(tǒng)中,我們有一些便捷的工具來(lái)實(shí)現(xiàn)這一操作。就拿 numactl 來(lái)說(shuō),假如我們有一個(gè)對(duì)內(nèi)存帶寬要求極高的計(jì)算密集型任務(wù),像是大規(guī)模的科學(xué)計(jì)算模擬程序,為了減少內(nèi)存訪(fǎng)問(wèn)延遲,提升計(jì)算效率,我們可以使用 “numactl --cpunodebind=0 --membind=0 程序名” 這樣的命令,將該程序綁定到 NUMA 節(jié)點(diǎn) 0 上運(yùn)行。這意味著程序運(yùn)行過(guò)程中所涉及的 CPU 核心都來(lái)自節(jié)點(diǎn) 0,內(nèi)存分配也優(yōu)先從節(jié)點(diǎn) 0 的本地內(nèi)存獲取,如同為這個(gè)任務(wù)開(kāi)辟了一條專(zhuān)屬的 “高速通道”,讓數(shù)據(jù)的讀寫(xiě)能夠以最快的速度完成,避免了跨節(jié)點(diǎn)訪(fǎng)問(wèn)帶來(lái)的額外開(kāi)銷(xiāo)。
除了 numactl,taskset 命令也是我們的得力幫手。它專(zhuān)注于 CPU 核心的綁定,例如 “taskset -c 2,3,4 程序名”,就能將指定程序固定在 CPU 核心 2、3、4 上運(yùn)行。這在一些多線(xiàn)程應(yīng)用場(chǎng)景中尤為實(shí)用,比如網(wǎng)絡(luò)服務(wù)器程序,通過(guò)將不同的線(xiàn)程綁定到不同的 CPU 核心,可以有效減少線(xiàn)程上下文切換的開(kāi)銷(xiāo),提高服務(wù)器的并發(fā)處理能力,確保每個(gè)請(qǐng)求都能得到快速響應(yīng),就像為每個(gè)任務(wù)分配了專(zhuān)屬的 “工作間”,互不干擾,高效協(xié)作。
合理運(yùn)用這些綁定技巧,能夠讓進(jìn)程與 NUMA 節(jié)點(diǎn)、CPU 核心之間形成默契配合,充分挖掘硬件的性能潛力,為各類(lèi)應(yīng)用場(chǎng)景帶來(lái)顯著的性能提升,讓系統(tǒng)運(yùn)行得更加流暢、高效。
5.2優(yōu)化的 “妙手回春”
為了更直觀(guān)地感受 NUMA 節(jié)點(diǎn)優(yōu)化帶來(lái)的神奇效果,我們來(lái)看一個(gè)實(shí)際案例。
假設(shè)有一臺(tái)配備了兩個(gè) NUMA 節(jié)點(diǎn)的服務(wù)器,每個(gè)節(jié)點(diǎn)擁有 8 個(gè) CPU 核心,總內(nèi)存為 32GB。在未進(jìn)行 NUMA 優(yōu)化之前,運(yùn)行一個(gè)多線(xiàn)程的數(shù)據(jù)庫(kù)查詢(xún)應(yīng)用程序,該程序會(huì)頻繁地訪(fǎng)問(wèn)內(nèi)存數(shù)據(jù)。由于進(jìn)程默認(rèn)的內(nèi)存分配和 CPU 調(diào)度是相對(duì)隨機(jī)的,很容易出現(xiàn)跨節(jié)點(diǎn)訪(fǎng)問(wèn)內(nèi)存的情況,導(dǎo)致內(nèi)存訪(fǎng)問(wèn)延遲增加,系統(tǒng)性能受到制約。
通過(guò)使用 numactl 工具,我們將這個(gè)數(shù)據(jù)庫(kù)查詢(xún)應(yīng)用程序綁定到其中一個(gè) NUMA 節(jié)點(diǎn)上,例如 “numactl --cpunodebind=0 --membind=0 數(shù)據(jù)庫(kù)應(yīng)用程序名”。綁定之后,再次運(yùn)行該程序,并使用性能監(jiān)測(cè)工具觀(guān)察相關(guān)指標(biāo)。
對(duì)比綁定前后的數(shù)據(jù),我們發(fā)現(xiàn)內(nèi)存訪(fǎng)問(wèn)延遲有了顯著降低。原本跨節(jié)點(diǎn)訪(fǎng)問(wèn)時(shí),平均延遲可能高達(dá) 200 納秒左右,而綁定后,在節(jié)點(diǎn)內(nèi)部訪(fǎng)問(wèn)內(nèi)存,延遲大幅下降到 50 納秒以?xún)?nèi),這使得查詢(xún)操作能夠更快地獲取所需數(shù)據(jù),整體響應(yīng)時(shí)間縮短了約 30%。
從帶寬利用率來(lái)看,未優(yōu)化前,由于頻繁的跨節(jié)點(diǎn)內(nèi)存訪(fǎng)問(wèn)爭(zhēng)用帶寬,實(shí)際可用帶寬利用率僅能達(dá)到 50% 左右;優(yōu)化后,數(shù)據(jù)基本在本地節(jié)點(diǎn)內(nèi)流轉(zhuǎn),帶寬利用率提升至 80% 以上,數(shù)據(jù)傳輸更加順暢,系統(tǒng)吞吐量明顯增加,能夠同時(shí)處理更多的查詢(xún)請(qǐng)求,大大提升了數(shù)據(jù)庫(kù)服務(wù)器的性能表現(xiàn),為業(yè)務(wù)的高效運(yùn)行提供了有力支撐。