1. NUMA的幾個概念(Node,socket,core,thread)
對於socket,core和thread會有不少文章介紹,這里簡單說一下,具體參見下圖:
一句話總結:socket就是主板上的CPU插槽; Core就是socket里獨立的一組程序執行的硬件單元,比如寄存器,計算單元等; Thread:就是超線程hyperthread的概念,邏輯的執行單元,獨立的執行上下文,但是共享core內的寄存器和計算單元。
NUMA體系結構中多了Node的概念,這個概念其實是用來解決core的分組的問題,具體參見下圖來理解(圖中的OS CPU可以理解thread,那么core就沒有在圖中畫出),從圖中可以看出每個Socket里有兩個node,共有4個socket,每個 socket 2個node,每個node中有8個thread,總共4(Socket)× 2(Node)× 8 (4core × 2 Thread) = 64個thread。
另外每個node有自己的內部CPU,總線和內存,同時還可以訪問其他node內的內存,NUMA的最大的優勢就是可以方便的增加CPU的數量,因為 Node內有自己內部總線,所以增加CPU數量可以通過增加Node的數目來實現,如果單純的增加CPU的數量,會對總線造成很大的壓力,所以UMA結構 不可能支持很多的核。
《此圖出自:NUMA Best Practices for Dell PowerEdge 12th Generation Servers》
根據上面提到的,由於每個node內部有自己的CPU總線和內存,所以如果一個虛擬機的vCPU跨不同的Node的話,就會導致一個node中的CPU去 訪問另外一個node中的內存的情況,這就導致內存訪問延遲的增加。在有些特殊場景下,比如NFV環境中,對性能有比較高的要求,就非常需要同一個虛擬機 的vCPU盡量被分配到同一個Node中的pCPU上,所以在OpenStack的Kilo版本中增加了基於NUMA感知的虛擬機調度的特性。
2. 如何查看機器的NUMA拓撲結構
比較常用的命令就是lscpu,具體輸出如下:
- dylan@hp3000:~$ lscpu
- Architecture: x86_64
- CPU op-mode(s): 32-bit, 64-bit
- Byte Order: Little Endian
- CPU(s): 48 //共有48個邏輯CPU(threads)
- On-line CPU(s) list: 0-47
- Thread(s) per core: 2 //每個core有2個threads
- Core(s) per socket: 6 //每個socket有6個cores
- Socket(s): 4 //共有4個sockets
- NUMA node(s): 4 //共有4個NUMA nodes
- Vendor ID: GenuineIntel
- CPU family: 6
- Model: 45
- Stepping: 7
- CPU MHz: 1200.000
- BogoMIPS: 4790.83
- Virtualization: VT-x
- L1d cache: 32K //L1 data cache 32k
- L1i cache: 32K //L1 instruction cache 32k (牛x機器表現,馮諾依曼+哈弗體系結構)
- L2 cache: 256K
- L3 cache: 15360K
- NUMA node0 CPU(s): 0-5,24-29
- NUMA node1 CPU(s): 6-11,30-35
- NUMA node2 CPU(s): 12-17,36-41
- NUMA node3 CPU(s): 18-23,42-47
從上圖輸出,可以看出當前機器有4個sockets,每個 sockets包含1個numa node,每個numa node中有6個cores,每個cores包含2個thread,所以總的threads數 量=4(sockets)×1(node)×6(cores)×2(threads)=48.
另外,也可以通過下面的腳本來打印出當前機器的socket,core和thread的數量。
- #!/bin/bash
- # Simple print cpu topology
- # Author: kodango
- function get_nr_processor()
- {
- grep '^processor' /proc/cpuinfo | wc -l
- }
- function get_nr_socket()
- {
- grep 'physical id' /proc/cpuinfo | awk -F: '{
- print $2 | "sort -un"}' | wc -l
- }
- function get_nr_siblings()
- {
- grep 'siblings' /proc/cpuinfo | awk -F: '{
- print $2 | "sort -un"}'
- }
- function get_nr_cores_of_socket()
- {
- grep 'cpu cores' /proc/cpuinfo | awk -F: '{
- print $2 | "sort -un"}'
- }
- echo '===== CPU Topology Table ====='
- echo
- echo '+--------------+---------+-----------+'
- echo '| Processor ID | Core ID | Socket ID |'
- echo '+--------------+---------+-----------+'
- while read line; do
- if [ -z "$line" ]; then
- printf '| %-12s | %-7s | %-9s |\n' $p_id $c_id $s_id
- echo '+--------------+---------+-----------+'
- continue
- fi
- if echo "$line" | grep -q "^processor"; then
- p_id=`echo "$line" | awk -F: '{print $2}' | tr -d ' '`
- fi
- if echo "$line" | grep -q "^core id"; then
- c_id=`echo "$line" | awk -F: '{print $2}' | tr -d ' '`
- fi
- if echo "$line" | grep -q "^physical id"; then
- s_id=`echo "$line" | awk -F: '{print $2}' | tr -d ' '`
- fi
- done < /proc/cpuinfo
- echo
- awk -F: '{
- if ($1 ~ /processor/) {
- gsub(/ /,"",$2);
- p_id=$2;
- } else if ($1 ~ /physical id/){
- gsub(/ /,"",$2);
- s_id=$2;
- arr[s_id]=arr[s_id] " " p_id
- }
- }
- END{
- for (i in arr)
- printf "Socket %s:%s\n", i, arr[i];
- }' /proc/cpuinfo
- echo
- echo '===== CPU Info Summary ====='
- echo
- nr_processor=`get_nr_processor`
- echo "Logical processors: $nr_processor"
- nr_socket=`get_nr_socket`
- echo "Physical socket: $nr_socket"
- nr_siblings=`get_nr_siblings`
- echo "Siblings in one socket: $nr_siblings"
- nr_cores=`get_nr_cores_of_socket`
- echo "Cores in one socket: $nr_cores"
- let nr_cores*=nr_socket
- echo "Cores in total: $nr_cores"
- if [ "$nr_cores" = "$nr_processor" ]; then
- echo "Hyper-Threading: off"
- else
- echo "Hyper-Threading: on"
- fi
- echo
- echo '===== END ====='
————————————————————