1. 程式人生 > >NUMA CPU架構介紹

NUMA CPU架構介紹

NUMA把一臺計算機分成多個節點(node),每個節點內部擁有多個CPU,節點內部使用共有的記憶體控制器,節點之間是通過互聯模組進行連線和資訊互動。
因此節點的所有記憶體對於本節點所有的CPU都是等同的,對於其他節點中的所有CPU都不同。因此每個CPU可以訪問整個系統記憶體,但是訪問本地節點的記憶體速度最快(不經過互聯模組),訪問非本地節點的記憶體速度較慢(需要經過互聯模組),即CPU訪問記憶體的速度與節點的距離有關,該距離成為Node Distance。

 

NUMA(Non-Uniform Memory Access,非一致性記憶體訪問)和SMP(Symmetric Multi-Processor,對稱多處理器系統)是兩種不同的CPU硬體體系架構。

SMP的主要特徵是共享,所有的CPU共享使用全部資源,例如記憶體、匯流排和I/O,多個CPU對稱工作,彼此之間沒有主次之分,平等地訪問共享的資源,這樣勢必引入資源的競爭問題,從而導致它的擴充套件內力非常有限。

NUMA技術將CPU劃分成不同的組(Node),每個Node由多個CPU組成,並且有獨立的本地記憶體、I/O等資源。Node之間通過互聯模組連線和溝通,因此除了本地記憶體外,每個CPU仍可以訪問遠端Node的記憶體,只不過效率會比訪問本地記憶體差一些,我們用Node之間的距離(Distance,抽象的概念)來定義各個Node之間互訪資源的開銷。

 

Node->Socket->Core->Processor

如果你只知道CPU這麼一個概念,那麼是無法理解CPU的拓撲的。事實上,在NUMA架構下,CPU的概念從大到小依次是:Node、Socket、Core、Processor。Node的概念在上一節已經介紹過,這裡就不多贅述了。

隨著多核技術的發展,我們將多個CPU封裝在一起,這個封裝一般被稱為Socket(插槽的意思,也有人稱之為Packet,不知到哪個更加準確?),而Socket中的每個核心被稱為Core。為了進一步提升CPU的處理能力,Intel又引入了HT(Hyper-Threading,超執行緒)的技術,一個Core開啟HT之後,在OS看來就是兩個核,當然這個核是邏輯上的概念,所以也被稱為Logical Processor,本文簡稱為Processor。

綜上所述,一個NUMA Node可以有一個或者多個Socket,一個多核Socket顯然包含多個Core,一個Core如果開啟HT則變成兩個Logical Processor。Logical processor只是OS內部看到的,實際上兩個Processor還是位於同一個Core上,所以頻繁的排程仍可能導致資源競爭,影響效能。

檢視 CPU Topology

在介紹完CPU拓撲相關的概念後,我們來實際在伺服器上檢視相關的資訊。

檢視Numa Node

numactl是設定程序NUMA策略的命令列工具,也可以用來檢視當前的Nuwa node:

$ numactl --hardware
available: 2 nodes (0-1)
node 0 size: 24211 MB
node 0 free: 110 MB
node 1 size: 24240 MB
node 1 free: 150 MB
node distances:
node   0   1 
  0:  10  20 
  1:  20  10 

從上面可以看出本機有兩個Numa node,如果要進一步知道一個Node包含哪幾個CPU,該怎麼辦?

一種方法是通過檢視ls /sys/devices/system/node/目錄下的資訊,例如:

[[email protected]_AG]$  ls /sys/devices/system/node/node0
cpu12  cpu13  cpu14  cpu15  cpu4  cpu5  cpu6  cpu7  cpumap  distance  meminfo  numastat

可見, node0包含4/5/6/7/12/13/14/15八個Processor(剛好是一個Socket)。

如果是xen的環境,還可以通過xm info來檢視,對應的是node_to_cpu欄位。

檢視Socket

一個Socket對應主機板上的一個插槽,在本文中是指一個CPU封裝。在/proc/cpuinfo中的physical id就是Socket的ID,可以從中找到本機到底有多少個Socket,並且每個Socket有那幾個Processor。

1) 檢視有幾個Socket

$ grep 'physical id' /proc/cpuinfo | awk -F: '{print $2 | "sort -un"}'
 0
 1
$ grep 'physical id' /proc/cpuinfo | awk -F: '{print $2 | "sort -un"}' | wc -l
2

2) 檢視每個Socket有幾個Processor

$ grep 'physical id' /proc/cpuinfo | awk -F: '{print $2}' | sort | uniq -c
      8  0
      8  1

3) 檢視Socket對應那幾個Processor

$ 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) 
        print arr[i];
}' /proc/cpuinfo | cut -c2-
4 5 6 7 12 13 14 15
0 1 2 3 8 9 10 11

檢視Core

/proc/cpuinfo檔案中的cpu cores表明一個socket中有幾個cores,例如:

cat /proc/cpuinfo | grep 'core'  | sort -u
core id		: 0
core id		: 1
core id		: 10
core id		: 9
cpu cores	: 4

上面的結果說明一個socket有4個cores,它們的id分別為0/1/9/10,根據之前查到的我們的機器有2個socket,所以總共有8個core。

檢視Processor

檢視Processors的個數就比較簡單了,從上面的統計結果中我們已經可以知道有16個Logical processor,不過也可以直接從/proc/cpuinfo檔案中獲取:

$ grep 'processor' /proc/cpuinfo | wc -l
16

其實,每個socket中能有幾個processor也可以從siblings欄位中獲取:

$ grep 'siblings' /proc/cpuinfo | sort -u
siblings	: 8

資訊彙總

基於以上檢視CPU拓撲資訊的方法,簡單的寫了一個小程式來查詢:

#!/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 ====='

執行結果為:

===== CPU Topology Table =====

+--------------+---------+-----------+
| Processor ID | Core ID | Socket ID |
+--------------+---------+-----------+
| 0            | 0       | 1         |
+--------------+---------+-----------+
| 1            | 1       | 1         |
+--------------+---------+-----------+
| 2            | 9       | 1         |
+--------------+---------+-----------+
| 3            | 10      | 1         |
+--------------+---------+-----------+
| 4            | 0       | 0         |
+--------------+---------+-----------+
| 5            | 1       | 0         |
+--------------+---------+-----------+
| 6            | 9       | 0         |
+--------------+---------+-----------+
| 7            | 10      | 0         |
+--------------+---------+-----------+
| 8            | 0       | 1         |
+--------------+---------+-----------+
| 9            | 1       | 1         |
+--------------+---------+-----------+
| 10           | 9       | 1         |
+--------------+---------+-----------+
| 11           | 10      | 1         |
+--------------+---------+-----------+
| 12           | 0       | 0         |
+--------------+---------+-----------+
| 13           | 1       | 0         |
+--------------+---------+-----------+
| 14           | 9       | 0         |
+--------------+---------+-----------+
| 15           | 10      | 0         |
+--------------+---------+-----------+

Socket 0: 4 5 6 7 12 13 14 15
Socket 1: 0 1 2 3 8 9 10 11

===== CPU Info Summary =====

Logical processors: 16
Physical socket: 2
Siblings in one socket:  8
Cores in one socket:  4
Cores in total: 8
Hyper-Threading: on

===== END =====