Linux 的 NUMA 技術

原文地址:http://www.ibm.com/developerworks/cn/linux/l-numa/     (2004 年 7 月 01 日)

一、引言

隨着科學計算、事務處理對計算機性能要求的不斷提高,SMP(對稱多處理器)系統的應用越來越廣泛,規模也越來越大,但由於傳統的SMP系統中,所有處理 器都共享系統總線,因此當處理器的數目增大時,系統總線的競爭衝突加大,系統總線將成爲瓶頸,所以目前SMP系統的CPU數目一般只有數十個,可擴展能力 受到極大限制。NUMA技術有效結合了SMP系統易編程性和MPP(大規模並行)系統易擴展性的特點,較好解決了SMP系統的可擴展性問題,已成爲當今高 性能服務器的主流體系結構之一。目前國外著名的服務器廠商都先後推出了基於NUMA架構的高性能服務器,如HP的Superdome、SGI的Altix 3000、IBM的 x440、NEC的TX7、AMD的Opteron等。隨着Linux在服務器平臺上的表現越來越成熟,Linux內核對NUMA架構的支持也越來越完 善,特別是從2.5開始,Linux在調度器、存儲管理、用戶級API等方面進行了大量的NUMA優化工作,目前這部分工作還在不斷地改進,如新近推出的 2.6.7-RC1內核中增加了NUMA調度器。本文主要從存儲管理、調度器和CpuMemSets三個方面展開討論。


二、NUMA存儲管理

NUMA系統是由多個結點通過高速互連網絡連接而成的,如圖1是SGI Altix 3000 ccNUMA系統中的兩個結點。

圖1 SGI Altix3000系統的兩個結點

wKioL1XuSg2xI_D5AAA3EAMXK1o461.gif

NUMA系統的結點通常是由一組CPU(如,SGI Altix 3000是2個Itanium2 CPU)和本地內存組成,有的結點可能還有I/O子系統。由於每個結點都有自己的本地內存,因此全系統的內存在物理上是分佈的,每個結點訪問本地內存和訪 問其它結點的遠地內存的延遲是不同的,爲了減少非一致性訪存對系統的影響,在硬件設計時應儘量降低遠地內存訪存延遲(如通過Cache一致性設計等),而 操作系統也必須能感知硬件的拓撲結構,優化系統的訪存。

目前IA64 Linux所支持的NUMA架構服務器的物理拓撲描述是通過ACPI(Advanced Configuration and Power Interface)實現的。ACPI是由Compaq、Intel、Microsoft、Phoenix和Toshiba聯合制定的BIOS規範,它定 義了一個非常廣泛的配置和電源管理,目前該規範的版本已發展到4.0,5.0o版本正在制定中,具體信息可以從http://www.acpi.info網站上獲得。ACPI規範也已廣泛應用於IA-32架構的至強服務器系統中。

Linux對NUMA系統的物理內存分佈信息是從系統firmware的ACPI表中獲得的,最重要的是SRAT(System Resource Affinity Table)和SLIT(System Locality Information Table)表,其中SRAT包含兩個結構:

  • Processor Local APIC/SAPIC Affinity Structure:記錄某個CPU的信息;

  • Memory Affinity Structure:記錄內存的信息;

SLIT表則記錄了各個結點之間的距離,在系統中由數組node_distance[ ]記錄。

Linux採用Node、Zone和頁三級結構來描述物理內存的,如圖2所示,

圖2 Linux中Node、Zone和頁的關係

wKioL1XuTFmiINsLAAAoUQN08NU857.gif

2.1 結點

Linux用一個struct pg_data_t結構來描述系統的內存,系統中每個結點都掛接在一個pgdat_list列表中,對UMA體系結構,則只有一個靜態的 pg_data_t結構contig_page_data。對NUMA系統來說則非常容易擴充,NUMA系統中一個結點可以對應Linux存儲描述中的一 個結點,具體描述見linux/mmzone.h。

typedef struct pglist_data {
        struct zone node_zones[MAX_NR_ZONES];
        struct zonelist node_zonelists[MAX_ZONELISTS];
        int nr_zones;
#ifdef CONFIG_FLAT_NODE_MEM_MAP 
        struct page *node_mem_map;
#ifdef CONFIG_CGROUP_MEM_RES_CTLR
        struct page_cgroup *node_page_cgroup;
#endif
#endif
        struct bootmem_data *bdata;
#ifdef CONFIG_MEMORY_HOTPLUG
        spinlock_t node_size_lock;
#endif
        unsigned long node_start_pfn;
        unsigned long node_present_pages; 
        unsigned long node_spanned_pages; 
        int node_id;
        wait_queue_head_t kswapd_wait;
        struct task_struct *kswapd;
        int kswapd_max_order;
} pg_data_t;

下面就該結構中的主要域進行說明:



說明
node_zones該結點的zone類型,一般包括ZONE_HIGHMEM、ZONE_NORMAL和ZONE_DMA三類
node_zonelists分配時內存時zone的排序。它是由free_area_init_core()通過page_alloc.c中的build_zonelists()設置zone的順序
nr_zones該結點的 zone 個數,可以從 1 到 3,但並不是所有的結點都需要有 3 個 zone
node_mem_map它是 struct page 數組的第一頁,該數組表示結點中的每個物理頁框。根據該結點在系統中的順序,它可在全局 mem_map 數組中的某個位置
node_page_cgroup
bdata在系統啓動期間,內存管理子系統初始化之前,內核頁需要使用內存(另外,還需要保留部分內存用於初始化內存管理子系統)。爲解決這個問題,內核使用了前面文章講解的自舉內存分配器。
node_size_lock
node_start_pfn該NUMA結點第一個頁幀的邏輯編號。系統中所有的頁幀是依次編號的,每個頁幀的號碼都是全局唯一的(不只是結點內唯一)
node_present_pages結點中頁幀的數目  
node_spanned_pages該結點以頁幀爲單位計算的長度,包含內存空洞
node_id該結點的 ID,全系統結點 ID 從 0 開始
kswapd_wait交換守護進程的等待隊列,在將頁幀換出結點時會用到。後面的文章會詳細討論。  
kswapd指向負責該結點的交換守護進程的task_struct
kswapd_max_order定義需要釋放的區域的長度

系統中所有結點都維護在 pgdat_list 列表中,在 init_bootmem_core 函數中完成該列表初始化工作。

2.2 Zone

每個結點的內存被分爲多個塊,稱爲zones,它表示內存中一段區域。一個zone用struct_zone_t結構描述,zone的類型主要有 ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM。ZONE_DMA位於低端的內存空間,用於某些舊的ISA設備。 ZONE_NORMAL的內存直接映射到Linux內核線性地址空間的高端部分,許多內核操作只能在ZONE_NORMAL中進行。例如,在X86 中,zone的物理地址如下:

類型地址範圍
ZONE_DMA前16MB內存
ZONE_NORMAL16MB - 896MB
ZONE_HIGHMEM896 MB以上

Zone是用struct zone_t描述的,它跟蹤頁框使用、空閒區域和鎖等信息,具體描述如下(新版內核不一樣):

typedef struct zone_struct {
spinlock_t lock;
unsigned long free_pages;
unsigned long pages_min, pages_low, pages_high;
int need_balance;
free_area_t free_area[MAX_ORDER];
wait_queue_head_t * wait_table;
unsigned long wait_table_size;
unsigned long wait_table_shift;
struct pglist_data *zone_pgdat;
struct page *zone_mem_map;
unsigned long zone_start_paddr;
unsigned long zone_start_mapnr;
char *name;
unsigned long size;
} zone_t;

下面就該結構中的主要域進行說明,

說明
Lock旋轉鎖,用於保護該zone
free_pages該zone空閒頁總數
pages_min,

pages_low,

pages_high

Zone的閾值
need_balance該標誌告訴kswapd需要對該zone的頁進行交換
Free_area空閒區域的位圖,用於buddy分配器
wait_table等待釋放該頁進程的隊列散列表,這對wait_on_page()和unlock_page()是非常重要的。當進程都在一條隊列上等待時,將引起進程的抖動
zone_mem_map全局mem_map中該zone所引用的第一頁
zone_start_paddr含義與node_start_paddr類似
zone_start_mapnr含義與node_start_mapnr類似
Name該zone的名字。如,“DMA”,“Normal”或“HighMem”
SizeZone的大小,以頁爲單位

    

當系統    最小值爲20頁,最大值一般爲255頁。當到達pages_min時, 分配器將採用同步方式進行kswapd的工作;當空閒頁的數目達到pages_low時,kswapd被buddy分配器喚醒,開始釋放頁;當達到 pages_high時,kswapd將被喚醒,此時kswapd不會考慮如何平衡該zone,直到有pages_high空閒頁爲止。一般情況 下,pages_high缺省值是pages_min的3倍。

Linux存儲管理的這種層次式結構可以將ACPI的SRAT和SLIT信息 與Node、Zone實現有效的映射,從而克服了傳統Linux中平坦式結構無法反映NUMA架構的缺點。當一個任務請求分配內存時,Linux採用局部 結點分配策略,首先在自己的結點內尋找空閒頁;如果沒有,則到相鄰的結點中尋找空閒頁;如果還沒有,則到遠程結點中尋找空閒頁,從而在操作系統級優化了訪 存性能。


三、NUMA調度器

NUMA系統中,由於局部內存的訪存延遲低於遠地內存訪存延遲,因此將進程分配到局部內存附近 的處理器上可極大優化應用程序的性能。Linux 2.4內核中的調度器由於只設計了一個運行隊列,可擴展性較差,在SMP平臺表現一直不理想。當運行的任務數較多時,多個CPU增加了系統資源的競爭,限 制了負載的吞吐率。在2.5內核開發時,Ingo Molnar寫了一個多隊列調度器,稱爲O(1),從2.5.2開始O(1)調度器已集成到2.5內核版本中。O(1)是多隊列調度器,每個處理器都有一 條自己的運行隊列,但由於O(1)調度器不能較好地感知NUMA系統中結點這層結構,從而不能保證在調度後該進程仍運行在同一個結點上,爲此,Eirch Focht開發了結點親和的NUMA調度器,它是建立在Ingo Molnar的O(1)調度器基礎上的,Eirch將該調度器向後移植到2.4.X內核中,該調度器最初是爲基於IA64的NUMA機器的2.4內核開發 的,後來Matt Dobson將它移植到基於X86的NUMA-Q硬件上。

3.1 初始負載平衡

在每 個任務創建時都會賦予一個HOME結點(所謂HOME結點,就是該任務獲得最初內存分配的結點),它是當時創建該任務時全系統負載最輕的結點,由於目前 Linux中不支持任務的內存從一個結點遷移到另一個結點,因此在該任務的生命期內HOME結點保持不變。一個任務最初的負載平衡工作(也就是選該任務的 HOME結點)缺省情況下是由exec()系統調用完成的,也可以由fork()系統調用完成。在任務結構中的node_policy域決定了最初的負載 平衡選擇方式。

Node_policy平衡方式註釋
0(缺省值)do_execve()任務由fork()創建,但不在同一個結點上運行exec()
1do_fork()如果子進程有新的mm結構,選擇新的HOME結點
2do_fork()選擇新的HOME結點

3.2 動態負載平衡

在結點內,該NUMA調度器如同 O(1)調度器一樣。在一個空閒處理器上的動態負載平衡是由每隔1ms的時鐘中斷觸發的,它試圖尋找一個高負載的處理器,並將該處理器上的任務遷移到空閒 處理器上。在一個負載較重的結點,則每隔200ms觸發一次。調度器只搜索本結點內的處理器,只有還沒有運行的任務可以從Cache池中移動到其它空閒的 處理器。

如果本結點的負載均衡已經非常好,則計算其它結點的負載情況。如果某個結點的負載超過本結點的25%,則選擇該結點進行負載均衡。如果本地結點具有平均的負載,則延遲該結點的任務遷移;如果負載非常差,則延遲的時間非常短,延遲時間長短依賴於系統的拓撲結構。

    

四、CpuMemSets

SGI的Origin 3000 ccNUMA系統在許多領域得到了廣泛應用,是個非常成功的系統,爲了優化Origin 3000的性能,SGI的IRIX操作系統在其上實現了CpuMemSets,通過將應用與CPU和內存的綁定,充分發揮NUMA系統本地訪存的優勢。 Linux在NUMA項目中也實現了CpuMemSets,並且在SGI的Altix 3000的服務器中得到實際應用。

CpuMemSets 爲Linux提供了系統服務和應用在指定CPU上調度和在指定結點上分配內存的機制。CpuMemSets是在已有的Linux調度和資源分配代碼基礎上 增加了cpumemmap和cpumemset兩層結構,底層的cpumemmap層提供一個簡單的映射對,主要功能是:將系統的CPU號映射到應用的 CPU號、將系統的內存塊號映射到應用的內存塊號;上層的cpumemset層主要功能是:指定一個進程在哪些應用CPU上調度任務、指定內核或虛擬存儲 區可分配哪些應用內存塊。

4.1 cpumemmap

內核任務調度和內存分配代碼使用系統號,系統中的 CPU和內存塊都有對應的系統號。應用程序使用的CPU號和內存塊號是應用號,它用於指定在cpumemmap中CPU和內存的親和關係。每個進程、每個 虛擬內存區和Linux內核都有cpumemmap,這些映射是在fork()、exec()調用或創建虛擬內存區時繼承下來的,具有root權限的進程 可以擴展cpumemmap,包括增加系統CPU和內存塊。映射的修改將導致內核調度代碼開始運用新的系統CPU,存儲分配代碼使用新的內存塊分配內存 頁,而已在舊塊上分配的內存則不能遷移。Cpumemmap中不允許有空洞,例如,假設cpumemmap的大小爲n,則映射的應用號必須從0到n-1。 Cpumemmap中系統號和應用號並不是一對一的映射,多個應用號可以映射到同一個系統號。

4.2 cpumemset

系統啓動時,Linux內核創建一個缺省的cpumemmap和cpumemset,在初始的cpumemmap映射和cpumemset中包含系統目前所有的CPU和內存塊信息。

Linux內核只在該任務cpumemset的CPU上調度該任務,並只從該區域的內存列表中選擇內存區分配給用戶虛擬內存區,內核則只從附加到正在執行分配請求CPU的cpumemset內存列表中分配內存。

一 個新創建的虛擬內存區是從任務創建的當前cpumemset獲得的,如果附加到一個已存在的虛擬內存區時,情況會複雜些,如內存映射對象和Unix System V的共享內存區可附加到多個進程,也可以多次附加到同一個進程的不同地方。如果被附加到一個已存在的內存區,缺省情況下新的虛擬內存區繼承當前附加進程的 cpumemset,如果此時標誌位爲CMS_SHARE,則新的虛擬內存區鏈接到同一個cpumemset。

當分配頁時,如果該任務運行的CPU在cpumemset中有對應的存儲區,則內核從該CPU的內存列表中選擇,否則從缺省的CPU對應的cpumemset選擇內存列表。

4.3硬分區和CpuMemSets

在 一個大的NUMA系統中,用戶往往希望控制一部分CPU和內存給某些特殊的應用。目前主要有兩種技術途徑:硬分區和軟分區技術,CpuMemSets是屬 於軟分區技術。將一個大NUMA系統的硬分區技術與大NUMA系統具有的單系統映像優勢是矛盾的,而CpuMemSets允許用戶更加靈活的控制,它可以 重疊、劃分系統的CPU和內存,允許多個進程將系統看成一個單系統映像,並且不需要重啓系統,保障某些CPU和內存資源在不同的時間分配給指定的應用。

SGI 的CpuMemSets軟分區技術有效解決硬分區中的不足,一個單系統的SGI ProPack Linux服務器可以分成多個不同的系統,每個系統可以有自己的控制檯、根文件系統和IP網絡地址。每個軟件定義的CPU組可以看成一個分區,每個分區可 以重啓、安裝軟件、關機和更新軟件。分區間通過SGI NUMAlink連接進行通訊,分區間的全局共享內存由XPC和XPMEM內核模塊支持,它允許一個分區的進程訪問另一個分區的物理內存。

    

五、測試

爲了有效驗證Linux NUMA系統的性能和效率,我們在SGI公司上海辦事處測試了NUMA架構對SGI Altix 350性能。

該系統的配置如下:        
CPU:8個1.5 GHz Itanium2        
內存:8GB        
互連結構:如圖3所示

圖3   SGI Altix350 4個計算模塊的Ring拓撲

圖3 SGI Altix350 4個計算模塊的Ring拓撲

測試用例:

1、Presta MPI測試包(來自ASCI Purple的Benchmark)

從互連拓撲結構可以看出,計算模塊內部的訪存延遲不需要通過互連,延遲最逗,剩下的需要通過1步或2步互連到達計算模塊,我們通過Presta MPI測試包,重點測試每步互連對系統的影響,具體結果如下:

最小延遲(us)一步延遲(us)兩步延遲(us)
1.61.82.0

    

2、NASA的NPB測試

進程數

程序名

1248
IS牆鍾(s)10.255.383.001.66
加速比11.93.46.17
EP牆鍾(s)144.2672.1336.1218.09
加速比123.97.97
FT牆鍾(s)138.2990.3947.4622.21
加速比11.522.916.25
CG牆鍾(s)131.6567.3436.7921.58
加速比11.93.66.1
LU牆鍾(s)584.14368.92144.7366.38
加速比11.64.08.7
SP牆鍾(s)627.73
248.22
加速比1
2.5
BT牆鍾(s)1713.89
521.63
加速比1
3.2

上述測試表明,SGI Altix 350系統具有較高的訪存和計算性能,Linux NUMA技術已進入實用階段。

本文的Linux NUMA測試工作得到國防科大計算機學院周恩強講師、SGI公司上海辦事處孫曉工程師的大力支持,在此表示衷心的感謝。

    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章