NUMA(Non-Uniform Memory Access)字面直譯為“非一致性內存訪問”,對於Linux內核來說最早出現在2.6.7版本上。這種特性對於當下大內存+多CPU為潮流的X86平台來說確實會有不少的性能提升,但相反的,如果配置不當的話,也是一個很大的坑。本文就從頭開始說說Linux下關於CPU NUMA特性的配置和調優。
在若干年前,對於x86架構的計算機,那時的內存控制器還沒有整合進CPU,所有內存的訪問都需要通過北橋芯片來完成。此時的內存訪問如下圖所示,被稱為UMA(uniform memory access, 一致性內存訪問 )。這樣的訪問對於軟件層面來說非常容易實現:總線模型保證了所有的內存訪問是一致的,不必考慮由不同內存地址之前的差異。

之后的x86平台經歷了一場從“拼頻率”到“拼核心數”的轉變,越來越多的核心被盡可能地塞進了同一塊芯片上,各個核心對於內存帶寬的爭搶訪問成為了瓶頸;此時軟件、OS方面對於SMP多核心CPU的支持也愈發成熟;再加上各種商業上的考量,x86平台也順水推舟的搞了NUMA(Non-uniform memory access, 非一致性內存訪問)。
在這種架構之下,每個Socket都會有一個獨立的內存控制器IMC(integrated memory controllers, 集成內存控制器),分屬於不同的socket之內的IMC之間通過QPI link通訊。

然后就是進一步的架構演進,由於每個socket上都會有多個core進行內存訪問,這就會在每個core的內部出現一個類似最早SMP架構相似的內存訪問總線,這個總線被稱為IMC bus。

於是,很明顯的,在這種架構之下,兩個socket各自管理1/2的內存插槽,如果要訪問不屬於本socket的內存則必須通過QPI link。也就是說內存的訪問出現了本地/遠程(local/remote)的概念,內存的延時是會有顯著的區別的。
——————————————————————————————————————————————————————————————————————————————————————————————————————
回到當前世面上的CPU,工程上的實現其實更加復雜了。以Xeon 2699 v4系列CPU的標准來看,兩個Socket之之間通過各自的一條9.6GT/s的QPI link互訪。而每個Socket事實上有2個內存控制器。雙通道的緣故,每個控制器又有兩個內存通道(channel),每個通道最多支持3根內存條(DIMM)。理論上最大單socket支持76.8GB/s的內存帶寬,而兩個QPI link,每個QPI link有9.6GT/s的速率(~57.6GB/s)事實上QPI link已經出現瓶頸了。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Linux提供了一個一個手工調優的命令numactl(默認不安裝),在Centos7.0上的安裝命令如下
[root@realhost /]# yum -y install numactl
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
iso | 3.6 kB 00:00:00
Resolving Dependencies
--> Running transaction check
---> Package numactl.x86_64 0:2.0.12-3.el7 will be installed
--> Finished Dependency Resolution
Dependencies Resolved
==============================================================================================================================================================================================================
Package Arch Version Repository Size
==============================================================================================================================================================================================================
Installing:
numactl x86_64 2.0.12-3.el7 iso 65 k
Transaction Summary
==============================================================================================================================================================================================================
Install 1 Package
Total download size: 65 k
Installed size: 141 k
Downloading packages:
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
Installing : numactl-2.0.12-3.el7.x86_64 1/1
Verifying : numactl-2.0.12-3.el7.x86_64 1/1
Installed:
numactl.x86_64 0:2.0.12-3.el7
Complete!
首先你可以通過它查看系統的numa狀態
[root@realhost /]# numactl --hardware
available: 1 nodes (0)
node 0 cpus: 0 1 2 3
node 0 size: 1023 MB
node 0 free: 147 MB
node distances:
node 0
0: 10
可以看到,此系統共有1個node(因為是跑在windows上的虛擬機),共有1170M內存
這里假設我的虛擬機有多個node ,並且我要執行一個java param命令。最好的優化方案時python在node0中執行,而java在node1中執行,那命令是
numactl --cpubind=0 --membind=0 python param
numactl --cpubind=1 --membind=1 java param
numactl 命令詳解
語法:
numactl [--interleave nodes] [--preferred node] [--membind nodes]
[--cpunodebind nodes] [--physcpubind cpus] [--localalloc] [--] {arguments ...}
numactl --show
numactl --hardware
numactl [--huge] [--offset offset] [--shmmode shmmode] [--length length] [--strict]
[--shmid id] --shm shmkeyfile | --file tmpfsfile
[--touch] [--dump] [--dump-nodes] memory policy
主要參數:
--interleave=nodes, -i nodes
這個選項用於設定內存的交織分配模式。 也就是說系統在為多個節點分配內存空間的時候,將會以輪詢分發的方式被分配給這多個節點.
如果在當前眾多的交織分配內存節點中的目標節點無法正確的分配內存空間的話,內存空間將會由其他的節點來分配。
--membind=nodes, -m nodes
選項 '--membind' 僅用來從節點中分配內存空間所用。 如果在這些節點中無法分配出所請求的空間大小的話該分配操作將會失敗.
上述命令中指定需要分配空間的 nodes 的方式可以遵照上述 N,N,N , N-N ,N 這種方式來指定.
--cpunodebind=nodes, -N nodes
上述命令僅用於施加在運行與 cpu 上的進程。這個命令用於顯示 cpu 的個數,cpu 數目信息同樣記錄在系統中的存放處理器領域信息的 /proc/cpuinfo 文件夾下,
或者是按照關聯的中央處理器信息 在當前的中央處理器集中所存放.
--localalloc , -l
這個命令選項通常是為當前的節點分配內存的
--preferred=node
該命令由於指定優先分配內存空間的節點,如果無法將空間分配給該節點的話,應該分配給該節點上的空間將會被分發到其他的節點上
該命令選項后面僅接收一個單獨的節點標號. 相關的表示方式也可以使用.
--show,-s
該命令用於顯示 NUMA 機制作用在當前運行的那些進程上
--hardware , -H
該命令用於顯示當前系統中有多少個可用的節點.
--huge
當創建一個基於大內存頁面的系統級的共享內存段的時候,使用 --huge 這個選項。
--huge 選項僅在 --shmid 或是 --shm 命令的后面使用才有效.
--offset
該參數選項用於指定共享內存段中的位移量的偏移。 默認的情況下偏移量是 0 。 有效的偏移量單位是 m (用於表示 MB)
g (用於表示 GB) , k (用於表示 KB ), 其他沒有指定的被認為是以字節為單位.
--strict
這個參數選項 當施加了 NUMA 調度機制的共享內存段區域的頁面被施加了另一個機制而導致錯誤的時候,
使用 --strict 選項將會把錯誤信息顯示出來. 默認情況是不使用該選項的。
--shmmode shmmode
該選項僅在 --shmid 或是 --shm 之前使用才會生效。 當創建一個共享內存段的時候,通過整型數值來指定
共享內存的共享的模式類型.
--length length
Apply policy to length range in the shared memory segment or make the segment length long Default is to use the remaining
length Required when a shared memory segment is created and specifies the length of the new segment then .
Valid units are m ( for MB ) , g( for GB) , k ( for KB) , otherwise it specifies bytes.
--shmid id
通過ID 號碼來創建或使用一個共享內存段。
(如果共享內存段已經存在,那么通過 shmid 來指定下面要使用某個 ID 的共享內存段 ; 如果該 ID 對應的共享內存段並不存在的話,那么就創建一個)
--shm shmkeyfile
通過存放在 shmkeyfile(共享內存-鍵文件)中的 ID 號碼來創建或者是使用一個共享內存段。
訪問 shmkeyfile 文件的進程是通過 fork(3 arguments) 方法來實現的.
--file tmpfsfile
將 numa 機制施加於文件上面, 這個文件屬於 tmpfs或者是 hugetlbfs 這種特殊的文件系統
--touch
通過將 numa 機制施加於剛剛頁面上來實現內存的早期 numa 化。
默認情況下是不使用該選項,如果存在映射或是訪問頁面的應用的話,將會使用該早期實行 NUMA 機制的這種方法.
--dump
該選項用於廢除將已經 numa 化的特定區域上的 NUMA性質.
(--dump ) 選項后,有效指定 node 的書寫方式
all 用於將所有的節點上的 NUMA 特性移除
number 通過指定 node 后接的數值來廢除該數字對應的 node
number1(number2) node number1(node number2)上的 NUMA 特性將會被移除
number1-number2 node number1 -- node number2 區間上的所有存在的 node 的 NUMA 特性將會被移除
!nodes 除了 nodes 所指定的節點以外的所有節點上的 NUMA 特性全都會被移除
