Linux NUMA調(diào)優(yōu)實(shí)戰(zhàn):多線程程序加速方法
在當(dāng)今數(shù)據(jù)爆炸的時(shí)代,多線程程序已成為挖掘硬件潛力、加速應(yīng)用運(yùn)行的關(guān)鍵手段。對(duì)于運(yùn)行在 Linux 系統(tǒng)中的多線程程序而言,非統(tǒng)一內(nèi)存訪問(wèn)(NUMA)架構(gòu)既帶來(lái)了機(jī)遇,也提出了挑戰(zhàn)。NUMA 架構(gòu)通過(guò)將內(nèi)存劃分至不同節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)關(guān)聯(lián)特定處理器核心,實(shí)現(xiàn)本地內(nèi)存快速訪問(wèn)。這意味著在多線程程序運(yùn)行時(shí),若線程與內(nèi)存訪問(wèn)能精準(zhǔn)匹配到同一節(jié)點(diǎn),便能大幅提升效率。
然而,實(shí)際情況往往復(fù)雜得多,線程跨節(jié)點(diǎn)訪問(wèn)內(nèi)存的現(xiàn)象時(shí)有發(fā)生,從而引發(fā)性能瓶頸。今天,我們將深入 Linux NUMA 調(diào)優(yōu)實(shí)戰(zhàn)領(lǐng)域,聚焦多線程程序加速方法。從底層原理剖析,到實(shí)用工具運(yùn)用,再到具體優(yōu)化策略實(shí)施,帶你逐步掌握在 NUMA 架構(gòu)下釋放多線程程序性能的精髓,為開(kāi)發(fā)高效能應(yīng)用筑牢根基 。
Part1. NUMA 是什么?
NUMA(非統(tǒng)一內(nèi)存訪問(wèn))是一種多處理器系統(tǒng)的內(nèi)存架構(gòu),其中內(nèi)存訪問(wèn)時(shí)間取決于內(nèi)存相對(duì)于處理器的位置。在 NUMA 系統(tǒng)中,處理器可以比訪問(wèn)遠(yuǎn)程內(nèi)存更快地訪問(wèn)本地內(nèi)存。
在傳統(tǒng)的統(tǒng)一內(nèi)存訪問(wèn)(UMA,Uniform Memory Access)架構(gòu)中,所有處理器通過(guò)共享的內(nèi)存總線訪問(wèn)同一塊物理內(nèi)存,就好像所有的人都要通過(guò)同一個(gè)門(mén)進(jìn)入一個(gè)大倉(cāng)庫(kù)取東西 。這種架構(gòu)下,處理器訪問(wèn)內(nèi)存的延遲是一致的,好處是內(nèi)存訪問(wèn)邏輯簡(jiǎn)單,易于實(shí)現(xiàn)和管理,在處理器數(shù)量較少時(shí)表現(xiàn)良好。但隨著處理器數(shù)量的不斷增加,內(nèi)存總線就成了性能瓶頸,就好比這個(gè)門(mén)太小,人多了就會(huì)擁堵,導(dǎo)致內(nèi)存訪問(wèn)延遲增加,內(nèi)存帶寬不足,限制了系統(tǒng)整體性能的提升。
1.1 傳統(tǒng)SMP架構(gòu)的問(wèn)題
在早期的對(duì)稱(chēng)多處理器(SMP)系統(tǒng)中:
- 所有 CPU 通過(guò)共享總線訪問(wèn)統(tǒng)一的內(nèi)存池
- 隨著 CPU 數(shù)量增加,總線成為瓶頸
- 內(nèi)存帶寬無(wú)法滿足多個(gè) CPU 的需求
為了解決 UMA 架構(gòu)的局限性,NUMA 架構(gòu)應(yīng)運(yùn)而生。在 NUMA 架構(gòu)中,系統(tǒng)被劃分為多個(gè)節(jié)點(diǎn)(Node),每個(gè)節(jié)點(diǎn)包含一個(gè)或多個(gè)處理器、本地內(nèi)存以及 I/O 設(shè)備 。每個(gè)處理器可以訪問(wèn)本地內(nèi)存和其他節(jié)點(diǎn)的內(nèi)存,但訪問(wèn)本地內(nèi)存的速度要比訪問(wèn)其他節(jié)點(diǎn)(遠(yuǎn)程)內(nèi)存的速度快很多。
這就好比每個(gè)團(tuán)隊(duì)都有自己獨(dú)立的小倉(cāng)庫(kù),團(tuán)隊(duì)成員優(yōu)先從自己團(tuán)隊(duì)的小倉(cāng)庫(kù)取東西,速度自然快,如果要去別的團(tuán)隊(duì)倉(cāng)庫(kù)取東西,就會(huì)花費(fèi)更多時(shí)間。例如,在一個(gè)雙路服務(wù)器中,每個(gè) CPU 及其對(duì)應(yīng)的內(nèi)存可以組成一個(gè) NUMA 節(jié)點(diǎn),當(dāng) CPU1 訪問(wèn)自己節(jié)點(diǎn)的內(nèi)存時(shí),延遲較低;而當(dāng) CPU1 訪問(wèn) CPU2 節(jié)點(diǎn)的內(nèi)存時(shí),延遲就會(huì)明顯增加。
傳統(tǒng) SMP 架構(gòu):
CPU1 ─┐
CPU2 ─┼─── 共享總線 ─── 內(nèi)存
CPU3 ─┤
CPU4 ─┘1.2 NUMA的解決方案
NUMA 通過(guò)將系統(tǒng)劃分為多個(gè)節(jié)點(diǎn)來(lái)解決這個(gè)問(wèn)題:
- 每個(gè)節(jié)點(diǎn)包含一組 CPU 和本地內(nèi)存
- 節(jié)點(diǎn)之間通過(guò)高速互連網(wǎng)絡(luò)連接
NUMA 架構(gòu):
節(jié)點(diǎn)0: CPU0,CPU1 ── 本地內(nèi)存0
│
├─── 互連網(wǎng)絡(luò) ──——─┤
│ │
節(jié)點(diǎn)1: CPU2,CPU3 ── 本地內(nèi)存11.3 為什么要進(jìn)行 NUMA 性能調(diào)優(yōu)?
在 NUMA 架構(gòu)下,多線程程序的性能表現(xiàn)與內(nèi)存訪問(wèn)的局部性密切相關(guān)。如果多線程程序在運(yùn)行時(shí)沒(méi)有充分考慮 NUMA 架構(gòu)的特性,就可能出現(xiàn)大量的跨節(jié)點(diǎn)內(nèi)存訪問(wèn),導(dǎo)致性能急劇下降。
假設(shè)你正在運(yùn)行一個(gè)多線程的大數(shù)據(jù)分析程序,這個(gè)程序需要處理海量的數(shù)據(jù)。每個(gè)線程都要頻繁地讀寫(xiě)內(nèi)存中的數(shù)據(jù),如果沒(méi)有進(jìn)行 NUMA 性能調(diào)優(yōu),線程可能會(huì)隨機(jī)地訪問(wèn)不同節(jié)點(diǎn)的內(nèi)存。比如,一個(gè)線程原本在 Node1 上運(yùn)行,卻頻繁地去訪問(wèn) Node2 上的內(nèi)存數(shù)據(jù),這就好比你在自己團(tuán)隊(duì)的倉(cāng)庫(kù)找不到東西,要頻繁地跑去別的團(tuán)隊(duì)倉(cāng)庫(kù)找,一來(lái)一回就浪費(fèi)了很多時(shí)間。這種跨節(jié)點(diǎn)內(nèi)存訪問(wèn)會(huì)增加內(nèi)存訪問(wèn)延遲,降低內(nèi)存帶寬的利用率,使得程序的執(zhí)行速度大幅下降,原本可能幾小時(shí)就能完成的數(shù)據(jù)分析任務(wù),現(xiàn)在可能需要花費(fèi)數(shù)倍的時(shí)間。
再以數(shù)據(jù)庫(kù)服務(wù)器為例,數(shù)據(jù)庫(kù)服務(wù)器需要處理大量的并發(fā)查詢(xún)請(qǐng)求,每個(gè)查詢(xún)請(qǐng)求可能由一個(gè)線程來(lái)處理。如果線程沒(méi)有合理地分配到本地內(nèi)存資源,不同線程對(duì)內(nèi)存的訪問(wèn)在各個(gè)節(jié)點(diǎn)上雜亂無(wú)章,就會(huì)導(dǎo)致大量的跨節(jié)點(diǎn)內(nèi)存訪問(wèn)。這不僅會(huì)增加內(nèi)存訪問(wèn)的延遲,還可能導(dǎo)致內(nèi)存帶寬成為瓶頸,影響數(shù)據(jù)庫(kù)服務(wù)器的并發(fā)處理能力,使得數(shù)據(jù)庫(kù)的響應(yīng)速度變慢,無(wú)法滿足大量用戶的快速查詢(xún)需求。
對(duì)于一些高性能計(jì)算場(chǎng)景,如天氣預(yù)報(bào)模型計(jì)算、基因測(cè)序數(shù)據(jù)分析等,這些任務(wù)通常需要進(jìn)行大規(guī)模的并行計(jì)算,對(duì)內(nèi)存訪問(wèn)性能要求極高。如果多線程程序在 NUMA 架構(gòu)下沒(méi)有進(jìn)行優(yōu)化,跨節(jié)點(diǎn)內(nèi)存訪問(wèn)帶來(lái)的性能損耗會(huì)嚴(yán)重制約計(jì)算效率,使得科研人員需要等待更長(zhǎng)的時(shí)間才能得到計(jì)算結(jié)果,影響科研進(jìn)展。
由此可見(jiàn),進(jìn)行 NUMA 性能調(diào)優(yōu)對(duì)于多線程程序來(lái)說(shuō)至關(guān)重要。通過(guò)優(yōu)化,可以讓線程盡可能地訪問(wèn)本地內(nèi)存,減少跨節(jié)點(diǎn)內(nèi)存訪問(wèn),充分利用 NUMA 架構(gòu)的優(yōu)勢(shì),提高內(nèi)存訪問(wèn)效率,從而提升多線程程序的整體性能,使其能夠更快速、高效地完成任務(wù),滿足不同應(yīng)用場(chǎng)景對(duì)高性能計(jì)算的需求。
Part2. 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)存總線上,共享同一物理內(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)存總線就如同那狹窄的玩具箱開(kāi)口,成為了激烈競(jìng)爭(zhēng)的焦點(diǎn)。多個(gè)處理器頻繁地同時(shí)訪問(wèn)內(nèi)存,導(dǎo)致總線爭(zhēng)用異常激烈,內(nèi)存訪問(wèn)延遲急劇上升。在高并發(fā)場(chǎng)景下,為了保證數(shù)據(jù)的一致性,處理器往往需要使用原子指令來(lái)訪問(wèn)內(nèi)存,例如通過(guò)鎖總線的方式獨(dú)占內(nèi)存訪問(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)存總線的爭(zhēng)用,處理器常常需要等待很長(zhǎng)時(shí)間才能獲取到所需的數(shù)據(jù),導(dǎo)致事務(wù)處理的響應(yīng)時(shí)間大幅增加,系統(tǒng)吞吐量急劇下降,無(wú)法滿足業(yè)務(wù)對(duì)高性能的要求。這種內(nèi)存訪問(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)存訪問(wèn)方面的瓶頸,非統(tǒng)一內(nèi)存訪問(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)中,處理器訪問(wèn)本地內(nèi)存的速度遠(yuǎn)遠(yuǎn)快于訪問(wèn)其他節(jié)點(diǎn)的遠(yuǎn)程內(nèi)存,這是因?yàn)楸镜貎?nèi)存與處理器之間的物理距離更近,數(shù)據(jù)傳輸延遲更低,就像小區(qū)居民在自家樓下的小超市購(gòu)物,方便快捷;而訪問(wèn)其他節(jié)點(diǎn)的內(nèi)存則像是要跑到隔壁小區(qū)的超市購(gòu)物,需要經(jīng)過(guò)一段路程,花費(fèi)更多的時(shí)間。這種內(nèi)存訪問(wèn)的非一致性特性,使得 NUMA 架構(gòu)能夠有效地減少內(nèi)存總線的爭(zhēng)用,提高內(nèi)存訪問(wèn)的并行性,進(jìn)而提升系統(tǒng)的整體性能。
與 SMP 架構(gòu)相比,NUMA 架構(gòu)最大的不同在于其內(nèi)存訪問(wèn)的非對(duì)稱(chēng)性。SMP 架構(gòu)下,所有處理器對(duì)內(nèi)存的訪問(wèn)延遲是一致的,就像所有居民到公共玩具箱的距離都一樣遠(yuǎn);而 NUMA 架構(gòu)中,不同節(jié)點(diǎn)的內(nèi)存訪問(wèn)延遲存在差異,處理器會(huì)優(yōu)先訪問(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ī)模科學(xué)計(jì)算、云計(jì)算數(shù)據(jù)中心等場(chǎng)景中,NUMA 架構(gòu)能夠充分發(fā)揮各個(gè)節(jié)點(diǎn)的計(jì)算能力,高效地處理海量數(shù)據(jù),為用戶提供快速、穩(wěn)定的服務(wù)。
2.2 Linux 內(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)存訪問(wèn)(DMA)的設(shè)備量身定制的。因?yàn)橛行├吓f的 DMA 控制器,它們只能訪問(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è)備能夠訪問(wèn)更廣泛的內(nèi)存空間,但又達(dá)不到完整的 4G 范圍,于是 ZONE_DMA32 應(yīng)運(yùn)而生,它主要服務(wù)于這些較新的、能訪問(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)行訪問(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問(wèn)自身節(jié)點(diǎn)的內(nèi)存就如同在自家院子里取東西,速度最快,延遲最低。要是自身節(jié)點(diǎn)內(nèi)存告急,無(wú)法滿足需求,內(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ū)域探索,直到找到滿足需求的內(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ù)。
Part3. NUMA核心技術(shù)
NUMA(Non - Uniform Memory Access Architecture)即非統(tǒng)一內(nèi)存訪問(wèn)架構(gòu),是一種用于多處理器的電腦內(nèi)存設(shè)計(jì)技術(shù)。其核心內(nèi)容如下:
- 內(nèi)存和 CPU 分組:將 CPU 和內(nèi)存劃分為多個(gè)節(jié)點(diǎn)(Node),每個(gè)節(jié)點(diǎn)包含若干 CPU 核心和本地內(nèi)存。例如,雙路服務(wù)器通常包含 2 個(gè) NUMA 節(jié)點(diǎn)。
- 訪問(wèn)速度差異:CPU 訪問(wèn)本地節(jié)點(diǎn)的內(nèi)存(本地內(nèi)存)速度快,延遲通常約為 100ns。而訪問(wèn)其他節(jié)點(diǎn)的內(nèi)存(遠(yuǎn)程內(nèi)存)速度慢,需通過(guò)跨節(jié)點(diǎn)互聯(lián),如 AMD 的 Infinity Fabric,延遲可達(dá)到 200 - 300ns,帶寬可能減半。
NUMA 技術(shù)的出現(xiàn)主要是為了解決傳統(tǒng) UMA(Uniform Memory Access,統(tǒng)一內(nèi)存訪問(wèn))架構(gòu)中,隨著 CPU 核心數(shù)增加,內(nèi)存總線爭(zhēng)用導(dǎo)致性能下降的問(wèn)題。它通過(guò)將系統(tǒng)劃分為多個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)獨(dú)立管理本地內(nèi)存,減少了全局總線競(jìng)爭(zhēng),提升了系統(tǒng)的擴(kuò)展性。
在實(shí)際應(yīng)用中,可通過(guò)進(jìn)程綁定(CPU Pinning)將進(jìn)程限制在特定 NUMA 節(jié)點(diǎn),強(qiáng)制使用本地內(nèi)存;也可利用中斷親和性(IRQ Affinity)將網(wǎng)卡中斷分配到與進(jìn)程相同的 NUMA 節(jié)點(diǎn),減少跨節(jié)點(diǎn)通信。此外,多線程應(yīng)用中,確保線程訪問(wèn)的數(shù)據(jù)位于同一 NUMA 節(jié)點(diǎn),實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)本地化,也能優(yōu)化性能
3.1 UMA技術(shù)
UMA是并行計(jì)算機(jī)中的共享存儲(chǔ)架構(gòu),即物理存儲(chǔ)器被所有處理機(jī)均勻共享,對(duì)所有存儲(chǔ)字具有相同的存取時(shí)間。每臺(tái)處理機(jī)可以有私用高速緩存,外圍設(shè)備也以一定形式共享。UMA技術(shù)適合于普通需求和多用戶共享時(shí)間的應(yīng)用,在時(shí)序要求嚴(yán)格的應(yīng)用中,被用作加速單一大型程序的執(zhí)行率。
3.2 NUMA技術(shù)
NUMA是用于多進(jìn)程計(jì)算中的存儲(chǔ)設(shè)計(jì),存儲(chǔ)讀取取決于當(dāng)前存儲(chǔ)器與處理器的關(guān)聯(lián)。在NUMA技術(shù)下,處理器訪問(wèn)本地存儲(chǔ)器比非本地存儲(chǔ)器(另一個(gè)處理器的本地存儲(chǔ)器或者處理器共享的存儲(chǔ)器)更快。
3.4 vNUMA
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)化并不影響它們。
因此,客戶操作系統(tǒng)或它的應(yīng)用程序如何放置進(jìn)程和內(nèi)存會(huì)顯著影響性能。將NUMA拓?fù)浔┞督oVM的好處是,允許用戶根據(jù)底層NUMA架構(gòu)做出最優(yōu)決策。通過(guò)假設(shè)用戶操作系統(tǒng)將在暴露的vNUMA拓?fù)浣Y(jié)構(gòu)中做出最佳決策,而不是在NUMA客戶機(jī)之間插入內(nèi)存。
3.5 NUMA的重要性
多線程應(yīng)用程序需要訪問(wèn)CPU核心的本地內(nèi)存,當(dāng)它必須使用遠(yuǎn)程內(nèi)存時(shí),性能將會(huì)受到延遲的影響。訪問(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)訪問(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核心將訪問(wèn)本地內(nèi)存。
Part4. 探尋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)鍵,它直觀地反映了不同節(jié)點(diǎn)間內(nèi)存訪問(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è)裝滿信息的 “寶箱”。進(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)前訪問(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)行,為用戶提供流暢穩(wěn)定的使用體驗(yàn)。
①查看 NUMA 信息
# 查看 NUMA 拓?fù)?lscpu | grep NUMA
numactl --hardware
lstopo # 需要安裝 hwloc
# 查看進(jìn)程的 NUMA 使用情況
numastat
numastat -p <pid>
# 查看內(nèi)存使用情況
cat /proc/meminfo
cat /proc/buddyinfo②監(jiān)控 NUMA 性能
# 查看 NUMA 命中率
numastat -c
# 使用 perf 監(jiān)控 NUMA 事件
perf stat -e node-loads,node-load-misses ./program③調(diào)試 NUMA 問(wèn)題
# 查看進(jìn)程的內(nèi)存映射
cat /proc/<pid>/numa_maps
# 查看 NUMA 平衡統(tǒng)計(jì)
cat /proc/vmstat | grep numa④性能調(diào)優(yōu)
# 禁用自動(dòng) NUMA 平衡(可能提高性能)
echo 0 > /proc/sys/kernel/numa_balancing
# 調(diào)整 zone_reclaim_mode
echo 1 > /proc/sys/vm/zone_reclaim_modePart5. 實(shí)戰(zhàn)前的準(zhǔn)備:工具與環(huán)境
在開(kāi)始 NUMA 性能調(diào)優(yōu)實(shí)戰(zhàn)之前,需要先準(zhǔn)備好相關(guān)的工具,并確保運(yùn)行環(huán)境支持 NUMA。下面介紹一些常用的工具及其安裝和基本使用方法。
5.1 numactl
numactl 是一個(gè)非常實(shí)用的命令行工具,用于在 NUMA 架構(gòu)下控制進(jìn)程和線程的內(nèi)存分配與調(diào)度 。它可以幫助我們將進(jìn)程綁定到特定的 NUMA 節(jié)點(diǎn),指定內(nèi)存分配策略等。
在 Debian/Ubuntu 系統(tǒng)上,可以使用以下命令安裝 numactl:
sudo apt - get install numactl在 CentOS/RHEL 系統(tǒng)上,安裝命令如下:
sudo yum install numactl安裝完成后,我們可以使用以下命令查看系統(tǒng)的 NUMA 架構(gòu)信息:
sudo numactl --hardware執(zhí)行上述命令后,會(huì)輸出系統(tǒng)中每個(gè) NUMA 節(jié)點(diǎn)的編號(hào)、CPU 列表、內(nèi)存信息以及內(nèi)存的距離等。例如:
available: 2 nodes (0 - 1)
node 0 cpus: 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23
node 0 size: 131037 MB
node 0 free: 3019 MB
node 1 cpus: 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31
node 1 size: 131071 MB
node 1 free: 9799 MB
node distances:
node 0 1
0: 10 20
1: 20 10如果我們希望將某個(gè)進(jìn)程綁定到第 0 個(gè) NUMA 節(jié)點(diǎn)上,可以使用如下命令:
sudo numactl --cpunodebind = 0 --membind = 0 your_command其中,--cpunodebind = 0表示將進(jìn)程分配到第 0 個(gè) NUMA 節(jié)點(diǎn)上的 CPU 列表中,--membind = 0表示將進(jìn)程分配到第 0 個(gè) NUMA 節(jié)點(diǎn)上的內(nèi)存,your_command是需要啟動(dòng)的進(jìn)程,比如一個(gè)可執(zhí)行文件或一個(gè)命令行。
5.2 numastat
numastat用于監(jiān)控和顯示系統(tǒng)的內(nèi)存使用情況,特別是各個(gè)NUMA節(jié)點(diǎn)的內(nèi)存分配和使用情況,它能幫助我們了解內(nèi)存的使用分布,以便更好地進(jìn)行性能優(yōu)化 。
安裝 numastat 同樣可以使用包管理器,在 Debian/Ubuntu 系統(tǒng)上:
sudo apt - get install numactl在 CentOS/RHEL 系統(tǒng)上:
sudo yum install numactl安裝好后,直接運(yùn)行numastat命令,就可以查看系統(tǒng)的內(nèi)存使用信息:
numastat輸出結(jié)果類(lèi)似如下:
node0 node1
numa_hit 1775216830 6808979012
numa_miss 4091495 494235148
numa_foreign 494235148 4091495
interleave_hit 52909 53004
local_node 1775205816 6808927908
other_node 4102509 494286252在這些輸出數(shù)據(jù)中,numa_hit表示命中的,即成功分配本地內(nèi)存訪問(wèn)的內(nèi)存大??;numa_miss表示把內(nèi)存訪問(wèn)分配到另一個(gè) node 節(jié)點(diǎn)的內(nèi)存大??;numa_foreign表示另一個(gè) Node 訪問(wèn)本節(jié)點(diǎn)內(nèi)存的大??;local_node表示本節(jié)點(diǎn)的進(jìn)程成功在本節(jié)點(diǎn)上分配內(nèi)存訪問(wèn)的大??;other_node表示本節(jié)點(diǎn)的進(jìn)程在其它節(jié)點(diǎn)上分配的內(nèi)存訪問(wèn)大小。通常,numa_miss和numa_foreign值越高,就越需要考慮進(jìn)行 NUMA 綁定優(yōu)化 。
5.3 perf
perf 是 Linux 內(nèi)核自帶的性能分析工具,它基于事件采樣原理,能夠?qū)μ幚砥飨嚓P(guān)性能指標(biāo)與操作系統(tǒng)相關(guān)性能指標(biāo)進(jìn)行剖析,可用于查找性能瓶頸和定位熱點(diǎn)代碼 。由于 perf 是內(nèi)核自帶工具,大多數(shù) Linux 系統(tǒng)無(wú)需額外安裝。
使用 perf 可以分析程序運(yùn)行期間發(fā)生的硬件事件(如cpu - cycles、instructions 、cache - misses等)和軟件事件(如page - faults、context - switches等)。例如,要分析某個(gè)進(jìn)程的 CPU 周期使用情況,可以使用以下命令:
sudo perf stat -p <pid> -e cpu - cycles其中,-p <pid>指定要分析的進(jìn)程 ID,-e cpu - cycles表示要統(tǒng)計(jì)的事件為 CPU 周期。執(zhí)行后會(huì)輸出該進(jìn)程在運(yùn)行期間的 CPU 周期相關(guān)統(tǒng)計(jì)信息。
如果想要查看某個(gè)進(jìn)程的熱點(diǎn)函數(shù),使用perf top命令,它會(huì)實(shí)時(shí)顯示系統(tǒng)中占用 CPU 周期最多的函數(shù):
sudo perf top -p <pid>在輸出結(jié)果中,第一列是符號(hào)引發(fā)的性能事件的比例,指占用的 CPU 周期比例;第二列是符號(hào)所在的 DSO(Dynamic Shared Object),可以是應(yīng)用程序、內(nèi)核、動(dòng)態(tài)鏈接庫(kù)、模塊;第三列是 DSO 的類(lèi)型,[.]表示此符號(hào)屬于用戶態(tài)的 ELF 文件,[k]表示此符號(hào)屬于內(nèi)核或模塊;第四列是符號(hào)名。
在進(jìn)行 NUMA 性能調(diào)優(yōu)實(shí)戰(zhàn)前,確保系統(tǒng)安裝并熟悉使用這些工具,將為后續(xù)的性能分析和優(yōu)化工作提供有力支持。
Part6. 實(shí)戰(zhàn)開(kāi)始:NUMA性能調(diào)優(yōu)步驟
6.1 查看系統(tǒng) NUMA 配置
在開(kāi)始調(diào)優(yōu)之前,首先要了解系統(tǒng)的 NUMA 配置情況??梢允褂胠scpu命令查看系統(tǒng)的CPU和NUMA節(jié)點(diǎn)信息 。執(zhí)行l(wèi)scpu命令后,會(huì)輸出一系列關(guān)于 CPU 和系統(tǒng)架構(gòu)的詳細(xì)信息,其中與 NUMA 相關(guān)的部分如下:
NUMA node(s): 2
NUMA node0 CPU(s): 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30
NUMA node1 CPU(s): 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31從這些輸出中,我們能得知系統(tǒng)中有 2 個(gè) NUMA 節(jié)點(diǎn),以及每個(gè)節(jié)點(diǎn)包含的 CPU 編號(hào)。
也可以使用numactl -H命令查看更詳細(xì)的 NUMA 架構(gòu)信息,包括每個(gè)節(jié)點(diǎn)的 CPU 列表、內(nèi)存大小、空閑內(nèi)存以及節(jié)點(diǎn)之間的距離 :
available: 2 nodes (0 - 1)
node 0 cpus: 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23
node 0 size: 131037 MB
node 0 free: 3019 MB
node 1 cpus: 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31
node 1 size: 131071 MB
node 1 free: 9799 MB
node distances:
node 0 1
0: 10 20
1: 20 10node distances表示節(jié)點(diǎn)之間的距離,距離值越大,說(shuō)明節(jié)點(diǎn)間的訪問(wèn)延遲越高。這里節(jié)點(diǎn) 0 訪問(wèn)節(jié)點(diǎn) 1 的距離為 20,節(jié)點(diǎn) 1 訪問(wèn)節(jié)點(diǎn) 0 的距離同樣為 20,而節(jié)點(diǎn)訪問(wèn)自身的距離為 10,表明訪問(wèn)本地節(jié)點(diǎn)的延遲更低。這些信息對(duì)于后續(xù)的性能調(diào)優(yōu)至關(guān)重要,它讓我們清楚地了解系統(tǒng)的硬件布局,為合理分配資源提供了依據(jù)。
6.2 進(jìn)程綁定到 NUMA 節(jié)點(diǎn)
將進(jìn)程綁定到特定的 NUMA 節(jié)點(diǎn)是優(yōu)化多線程程序性能的重要一步。這樣可以確保線程訪問(wèn)的內(nèi)存是本地內(nèi)存,減少跨節(jié)點(diǎn)內(nèi)存訪問(wèn)??梢允褂胻askset和numactl命令來(lái)實(shí)現(xiàn)進(jìn)程綁定 。
taskset命令用于設(shè)置或查看進(jìn)程的 CPU 親和性,也可以用來(lái)將進(jìn)程綁定到特定的 NUMA 節(jié)點(diǎn) 。例如,要將進(jìn)程號(hào)為1234的進(jìn)程綁定到 NUMA 節(jié)點(diǎn) 0 上,可以使用以下命令:
taskset -m 0 -p 1234其中,-m選項(xiàng)表示指定 NUMA 節(jié)點(diǎn),0表示 NUMA 節(jié)點(diǎn) 0,-p選項(xiàng)后面跟著要綁定的進(jìn)程號(hào)。
numactl命令功能更強(qiáng)大,不僅可以綁定 CPU,還能指定內(nèi)存綁定 。例如,要啟動(dòng)一個(gè)新的進(jìn)程并將其 CPU 和內(nèi)存都綁定到 NUMA 節(jié)點(diǎn) 1,可以使用如下命令:
numactl --cpunodebind = 1 --membind = 1 your_command--cpunodebind = 1表示將進(jìn)程綁定到節(jié)點(diǎn) 1 的 CPU 上,--membind = 1表示將進(jìn)程的內(nèi)存分配綁定到節(jié)點(diǎn) 1 的內(nèi)存上,your_command是要執(zhí)行的命令或程序。比如我們要啟動(dòng)一個(gè)test程序,并將其綁定到節(jié)點(diǎn) 1,命令如下:
numactl --cpunodebind = 1 --membind = 1 ./test通過(guò)這樣的綁定操作,進(jìn)程在運(yùn)行時(shí)就會(huì)優(yōu)先使用指定 NUMA 節(jié)點(diǎn)的 CPU 和內(nèi)存資源,從而減少跨節(jié)點(diǎn)訪問(wèn)帶來(lái)的性能損耗,提高程序的執(zhí)行效率。
6.3 優(yōu)化內(nèi)存分配策略
內(nèi)存分配策略對(duì)多線程程序在 NUMA 架構(gòu)下的性能有著重要影響。合理調(diào)整內(nèi)存分配策略,可以減少跨節(jié)點(diǎn)內(nèi)存訪問(wèn),提高內(nèi)存使用效率 。
Linux 系統(tǒng)提供了多種內(nèi)存分配策略,其中常用的有首選本地節(jié)點(diǎn)(preferred)和交錯(cuò)(interleaving)模式 。首選本地節(jié)點(diǎn)策略是指進(jìn)程優(yōu)先從本地 NUMA 節(jié)點(diǎn)分配內(nèi)存,這樣可以充分利用本地內(nèi)存訪問(wèn)速度快的優(yōu)勢(shì) ??梢酝ㄟ^(guò)修改/sys/kernel/mm/transparent_hugepage/enabled文件來(lái)設(shè)置內(nèi)存分配策略為首選本地節(jié)點(diǎn)。打開(kāi)該文件,將其內(nèi)容修改為:
echo 'preferred' > /sys/kernel/mm/transparent_hugepage/enabled交錯(cuò)模式則是指進(jìn)程在多個(gè) NUMA 節(jié)點(diǎn)間交錯(cuò)地分配內(nèi)存,這種策略適用于內(nèi)存訪問(wèn)負(fù)載較為均衡,且需要充分利用多個(gè)節(jié)點(diǎn)內(nèi)存資源的場(chǎng)景 。設(shè)置交錯(cuò)模式的命令如下:
echo 'interleave' > /sys/kernel/mm/transparent_hugepage/enabled也可以使用numactl命令在啟動(dòng)進(jìn)程時(shí)臨時(shí)設(shè)置內(nèi)存分配策略 。例如,要以交錯(cuò)模式啟動(dòng)一個(gè)test程序,可以使用以下命令:
numactl --interleave=all ./testinterleave=all表示在所有 NUMA 節(jié)點(diǎn)間交錯(cuò)分配內(nèi)存。通過(guò)根據(jù)程序的內(nèi)存訪問(wèn)特點(diǎn)選擇合適的內(nèi)存分配策略,可以有效提升程序在 NUMA 架構(gòu)下的性能。
6.4 使用大頁(yè)(Huge Pages)
大頁(yè)是一種內(nèi)存管理機(jī)制,它使用比普通內(nèi)存頁(yè)更大的頁(yè)面來(lái)管理內(nèi)存 。在 NUMA 系統(tǒng)中,使用大頁(yè)可以減少頁(yè)表項(xiàng)的數(shù)量,降低內(nèi)存管理開(kāi)銷(xiāo),從而提高內(nèi)存訪問(wèn)效率,尤其對(duì)于內(nèi)存訪問(wèn)密集型的多線程程序,效果更為顯著 。
查看當(dāng)前系統(tǒng)的大頁(yè)配置情況,可以使用如下命令:
cat /proc/meminfo | grep HugePages執(zhí)行該命令后,會(huì)輸出類(lèi)似如下信息:
AnonHugePages: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB從輸出中可以看到當(dāng)前系統(tǒng)的大頁(yè)總數(shù)(HugePages_Total)、空閑大頁(yè)數(shù)(HugePages_Free)、已保留大頁(yè)數(shù)(HugePages_Rsvd)、超額大頁(yè)數(shù)(HugePages_Surp)以及大頁(yè)的大?。℉ugepagesize)。
要分配大頁(yè),可以通過(guò)修改/sys/kernel/mm/hugepages/hugepages-<size>/nr_hugepages文件來(lái)實(shí)現(xiàn),其中<size>是大頁(yè)的大小,如2048kB 。例如,要分配 1024 個(gè) 2MB 的大頁(yè),可以使用以下命令:
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages使用大頁(yè)啟動(dòng)應(yīng)用程序時(shí),可以結(jié)合numactl命令 。比如,要啟動(dòng)一個(gè)test程序,并讓其使用大頁(yè)且將內(nèi)存綁定到 NUMA 節(jié)點(diǎn) 0,可以使用如下命令:
numactl --membind=0 --huge ./test--membind=0表示將內(nèi)存綁定到節(jié)點(diǎn) 0,--huge表示使用大頁(yè)。通過(guò)合理配置和使用大頁(yè),能夠有效提升多線程程序在 NUMA 架構(gòu)下的內(nèi)存訪問(wèn)性能,進(jìn)而提高程序的整體運(yùn)行效率。
6.5 性能監(jiān)控與效果評(píng)估
在完成 NUMA 性能調(diào)優(yōu)后,需要對(duì)調(diào)優(yōu)效果進(jìn)行監(jiān)控和評(píng)估,以確定是否達(dá)到了預(yù)期的性能提升。這就好比一場(chǎng)考試后,要通過(guò)檢查成績(jī)來(lái)判斷學(xué)習(xí)方法是否有效。
(1)監(jiān)控工具的使用
①top 命令:top 命令是 Linux 系統(tǒng)中常用的實(shí)時(shí)性能監(jiān)控工具,通過(guò)在終端輸入 “top” 即可啟動(dòng) 。它能實(shí)時(shí)展示系統(tǒng)的關(guān)鍵性能指標(biāo),如 CPU 使用率、內(nèi)存占用、負(fù)載平均值等,以及每個(gè)進(jìn)程的詳細(xì)信息 。在多線程程序運(yùn)行時(shí),我們可以通過(guò) top 命令查看進(jìn)程的 CPU 使用率和內(nèi)存占用情況。如果某個(gè)多線程進(jìn)程的 CPU 使用率在調(diào)優(yōu)后明顯降低,且內(nèi)存占用也更加合理,這可能意味著調(diào)優(yōu)起到了作用。比如,在調(diào)優(yōu)前,一個(gè)多線程的數(shù)據(jù)處理程序的 CPU 使用率一直維持在 80% 以上,內(nèi)存占用不斷攀升;調(diào)優(yōu)后,CPU 使用率穩(wěn)定在 50% 左右,內(nèi)存占用也趨于穩(wěn)定,這就是一個(gè)積極的信號(hào)。
②perf 工具:perf 是一個(gè)功能強(qiáng)大的性能分析工具,基于事件采樣原理,能對(duì)處理器和操作系統(tǒng)相關(guān)性能指標(biāo)進(jìn)行剖析,查找性能瓶頸和定位熱點(diǎn)代碼 。例如,使用 “perf stat -p -e cpu - cycles” 命令,可以分析指定進(jìn)程的 CPU 周期使用情況 。在 NUMA 性能調(diào)優(yōu)前后,對(duì)多線程程序執(zhí)行此命令,對(duì)比調(diào)優(yōu)前后的 CPU 周期數(shù)。如果調(diào)優(yōu)后 CPU 周期數(shù)明顯減少,說(shuō)明程序的執(zhí)行效率得到了提高,可能是因?yàn)闇p少了跨節(jié)點(diǎn)內(nèi)存訪問(wèn),降低了 CPU 等待內(nèi)存數(shù)據(jù)的時(shí)間。使用 “perf top -p ” 命令可以實(shí)時(shí)顯示系統(tǒng)中占用 CPU 周期最多的函數(shù),幫助我們找出性能熱點(diǎn),以便針對(duì)性地進(jìn)行優(yōu)化。
③numastat 工具:numastat 用于監(jiān)控和顯示系統(tǒng)的內(nèi)存使用情況,特別是各個(gè) NUMA 節(jié)點(diǎn)的內(nèi)存分配和使用情況 。運(yùn)行 “numastat” 命令,它會(huì)輸出各個(gè) NUMA 節(jié)點(diǎn)的內(nèi)存命中(numa_hit)、內(nèi)存缺失(numa_miss)、跨節(jié)點(diǎn)訪問(wèn)(numa_foreign)等信息 。在調(diào)優(yōu)前,如果 numa_miss 和 numa_foreign 的值較高,說(shuō)明存在大量的跨節(jié)點(diǎn)內(nèi)存訪問(wèn)。而在調(diào)優(yōu)后,若這些值顯著降低,numa_hit 的值升高,就表明內(nèi)存訪問(wèn)的局部性得到了改善,多線程程序更多地訪問(wèn)了本地內(nèi)存,從而提高了性能。
(2)性能指標(biāo)對(duì)比
①運(yùn)行時(shí)間:在調(diào)優(yōu)前后,多次運(yùn)行多線程程序,記錄其運(yùn)行時(shí)間 ??梢允褂?“time” 命令來(lái)測(cè)量程序的執(zhí)行時(shí)間,例如 “time your_command” 。假設(shè)一個(gè)多線程的圖像渲染程序,調(diào)優(yōu)前完成一次渲染需要 10 分鐘,調(diào)優(yōu)后縮短到了 6 分鐘,這直觀地顯示了調(diào)優(yōu)對(duì)程序運(yùn)行效率的提升。
②吞吐量:對(duì)于一些數(shù)據(jù)處理或網(wǎng)絡(luò)服務(wù)類(lèi)的多線程程序,吞吐量是一個(gè)重要的性能指標(biāo) 。比如,一個(gè)多線程的網(wǎng)絡(luò)服務(wù)器,調(diào)優(yōu)前每秒能處理 1000 個(gè)請(qǐng)求,調(diào)優(yōu)后每秒能處理 1500 個(gè)請(qǐng)求,吞吐量的提升表明調(diào)優(yōu)使得程序能夠更高效地處理任務(wù),滿足更多的業(yè)務(wù)需求。
③CPU 使用率:通過(guò) top 命令或其他性能監(jiān)控工具獲取調(diào)優(yōu)前后多線程程序的 CPU 使用率 。如果調(diào)優(yōu)后 CPU 使用率降低,說(shuō)明程序?qū)?CPU 資源的利用更加高效,可能是因?yàn)闇p少了不必要的計(jì)算或等待時(shí)間,這在服務(wù)器環(huán)境中尤為重要,可以為其他任務(wù)釋放更多的 CPU 資源。
④內(nèi)存訪問(wèn)延遲:雖然沒(méi)有直接的工具可以精確測(cè)量?jī)?nèi)存訪問(wèn)延遲,但可以通過(guò) perf 工具結(jié)合其他指標(biāo)來(lái)間接評(píng)估 。例如,觀察 cache - misses(緩存未命中)的情況,緩存未命中次數(shù)的減少通常意味著內(nèi)存訪問(wèn)延遲的降低。因?yàn)楫?dāng)內(nèi)存訪問(wèn)更傾向于本地內(nèi)存時(shí),數(shù)據(jù)更容易被緩存命中,從而減少了從內(nèi)存中讀取數(shù)據(jù)的時(shí)間,提高了程序的整體性能。
通過(guò)上述性能監(jiān)控工具和指標(biāo)對(duì)比,我們可以全面、準(zhǔn)確地評(píng)估 NUMA 性能調(diào)優(yōu)對(duì)多線程程序的效果,驗(yàn)證調(diào)優(yōu)策略的有效性,為進(jìn)一步的優(yōu)化提供數(shù)據(jù)支持。
























