系統(tǒng)性能調(diào)優(yōu)之綁定CPU
支持超線程的numa架構(gòu)
物理硬件視角,
將多個(gè)CPU封裝在一起,這個(gè)封裝被稱(chēng)為插槽Socket;
Core是socket上獨(dú)立的硬件單元;
通過(guò)intel的超線程HT技術(shù)進(jìn)一步提升CPU的處理能力,OS看到的邏輯上的核Processor的數(shù)量。
每個(gè)硬件線程都可以按邏輯cpu尋址,因此這個(gè)處理器看上去有八塊cpu。
對(duì)于操作系統(tǒng)的視角:
- CPU(s):8
- NUMA node0 CPU(s):0,4
- NUMA node1 CPU(s):1,5
- NUMA node2 CPU(s):2,6
- NUMA node3 CPU(s):3,7
操作系統(tǒng)視角.png
L1緩分成兩種,一種是指令緩存,一種是數(shù)據(jù)緩存。L2緩存和L3緩存不分指令和數(shù)據(jù)。L1和L2緩存在第一個(gè)CPU核中,L3則是所有CPU核心共享的內(nèi)存。L1、L2、L3的越離CPU近就越小,速度也越快,越離CPU遠(yuǎn),速度也越慢。再往后面就是內(nèi)存,內(nèi)存的后面就是硬盤(pán)。我們來(lái)看一些他們的速度:
- L1 的存取速度:4 個(gè)CPU時(shí)鐘周期
- L2 的存取速度:11 個(gè)CPU時(shí)鐘周期
- L3 的存取速度:39 個(gè)CPU時(shí)鐘周期
- RAM內(nèi)存的存取速度 :107 個(gè)CPU時(shí)鐘周期
如果 CPU 所要操作的數(shù)據(jù)在緩存中,則直接讀取,這稱(chēng)為緩存命中。命中緩存會(huì)帶來(lái)很大的性能提升,因此,我們的代碼優(yōu)化目標(biāo)是提升 CPU 緩存的命中率。

在主流的服務(wù)器上,一個(gè) CPU 處理器會(huì)有 10 到 20 多個(gè)物理核。同時(shí),為了提升服務(wù)器的處理能力,服務(wù)器上通常還會(huì)有多個(gè) CPU 處理器(也稱(chēng)為多 CPU Socket),每個(gè)處理器有自己的物理核(包括 L1、L2 緩存),L3 緩存,以及連接的內(nèi)存,同時(shí),不同處理器間通過(guò)總線連接。通過(guò)lscpu來(lái)看:
- root@ubuntu:~# lscpu
- Architecture: x86_64
- CPU(s): 32
- Thread(s) per core: 1
- Core(s) per socket: 8
- Socket(s): 4
- L1d cache: 32K
- L1i cache: 32K
- L2 cache: 256K
- L3 cache: 20480K
- NUMA node0 CPU(s): 0-7
- NUMA node1 CPU(s): 8-15
- NUMA node2 CPU(s): 16-23
- NUMA node3 CPU(s): 24-31
你可能注意到,三級(jí)緩存要比一、二級(jí)緩存大許多倍,這是因?yàn)楫?dāng)下的 CPU 都是多核心的,每個(gè)核心都有自己的一、二級(jí)緩存,但三級(jí)緩存卻是一顆 CPU 上所有核心共享的。
但是,有個(gè)地方需要你注意一下:如果應(yīng)用程序先在一個(gè) Socket 上運(yùn)行,并且把數(shù)據(jù)保存到了內(nèi)存,然后被調(diào)度到另一個(gè) Socket 上運(yùn)行,此時(shí),應(yīng)用程序再進(jìn)行內(nèi)存訪問(wèn)時(shí),就需要訪問(wèn)之前 Socket 上連接的內(nèi)存,這種訪問(wèn)屬于遠(yuǎn)端內(nèi)存訪問(wèn)。和訪問(wèn) Socket 直接連接的內(nèi)存相比,遠(yuǎn)端內(nèi)存訪問(wèn)會(huì)增加應(yīng)用程序的延遲。
常用性能監(jiān)測(cè)工具
Linux系統(tǒng)下,CPU與內(nèi)存子系統(tǒng)性能調(diào)優(yōu)的常用性能監(jiān)測(cè)工具有top、perf、numactl這3個(gè)工具。1) top工具 top工具是最常用的Linux性能監(jiān)測(cè)工具之一。通過(guò)top工具可以監(jiān)視進(jìn)程和系統(tǒng)整體性能。
- top 查看系統(tǒng)整體的資源使用情況
- top后輸入1 查看看每一個(gè)邏輯核cpu的資源使用情況
- top -p $PID -H 查看某個(gè)進(jìn)程內(nèi)所有檢查的CPU資源使用情況
- top后輸入F,并選擇P選項(xiàng) 查看線程執(zhí)行過(guò)程中是否調(diào)度到其他cpu上執(zhí)行,上下文切換過(guò)多時(shí),需要注意。
2) perf工具 perf工具是非常強(qiáng)大的Linux性能分析工具,可以通過(guò)該工具獲得進(jìn)程內(nèi)的調(diào)用情況、資源消耗情況并查找分析熱點(diǎn)函數(shù)。以CentOS為例,使用如下命令安裝perf工具:
- perf top 查看占用 CPU 時(shí)鐘最多的函數(shù)或者指令,因此可以用來(lái)查找熱點(diǎn)函數(shù)。
- perf -g record -- sleep 1 -p $PID 記錄進(jìn)程在1s內(nèi)的系統(tǒng)調(diào)用。
- perf -g latency --sort max 查看上一步記錄的結(jié)果,以調(diào)度延遲排序。
- perf report 查看記錄
3) numactl工具 numactl工具可用于查看當(dāng)前服務(wù)器的NUMA節(jié)點(diǎn)配置、狀態(tài),可通過(guò)該工具將進(jìn)程綁定到指定CPU核上,由指定CPU核來(lái)運(yùn)行對(duì)應(yīng)進(jìn)程。以CentOS為例,使用如下命令安裝numactl工具:
- numactl -H 查看當(dāng)前服務(wù)器的NUMA配置。
- numastat 查看當(dāng)前的NUMA運(yùn)行狀態(tài)。
優(yōu)化方法
(1) NUMA優(yōu)化,減少跨NUMA訪問(wèn)內(nèi)存 不同NUMA內(nèi)的CPU核訪問(wèn)同一個(gè)位置的內(nèi)存,性能不同。內(nèi)存訪問(wèn)延時(shí)從高到低為:跨CPU>跨NUMA,不跨CPU>NUMA內(nèi)。因此在應(yīng)用程序運(yùn)行時(shí)要盡可能地避免跨NUMA訪問(wèn)內(nèi)存,這可以通過(guò)設(shè)置線程的CPU親和性來(lái)實(shí)現(xiàn)。常用的修改方式有如下:(1)將設(shè)備中斷綁定到特定CPU核上??梢酝ㄟ^(guò)如下命令綁定:
- echo $cpuNumber > /proc/irq/$irq/smp_affinity_list
- 例子:echo 0-4 > /proc/irq/78/smp_affinity_list
- echo 3,8 > /proc/irq/78/smp_affinity_list
(2)通過(guò)numactl啟動(dòng)程序,如下面的啟動(dòng)命令表示啟動(dòng)程序./mongod,mongo就只能在CPU core 0到core7運(yùn)行(-C控制)。
- numactl -C 0-7 ./mongod
(3)可以使用 taskset 命令把一個(gè)程序綁定在一個(gè)核上運(yùn)行。
- taskset -c 0 ./redis-server
(4)在C/C++代碼中通過(guò)sched_setaffinity函數(shù)來(lái)設(shè)置線程親和性。(5)很多開(kāi)源軟件已經(jīng)支持在自帶的配置文件中修改線程的親和性,例如Nginx可以修改nginx.conf文件中worker_cpu_affinity參數(shù)來(lái)設(shè)置Nginx線程親和性。
綁核注意事項(xiàng)
在 CPU 的 NUMA 架構(gòu)下,對(duì) CPU 核的編號(hào)規(guī)則,并不是先把一個(gè) CPU Socket 中的所有邏輯核編完,再對(duì)下一個(gè) CPU Socket 中的邏輯核編碼,而是先給每個(gè) CPU Socket 中每個(gè)物理核的第一個(gè)邏輯核依次編號(hào),再給每個(gè) CPU Socket 中的物理核的第二個(gè)邏輯核依次編號(hào)。
注意的是在多個(gè)進(jìn)程要進(jìn)行親和性綁核的,你一定要注意 NUMA 架構(gòu)下 CPU 核的編號(hào)方法,這樣才不會(huì)綁錯(cuò)核。
本文轉(zhuǎn)載自微信公眾號(hào)「運(yùn)維開(kāi)發(fā)故事」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系運(yùn)維開(kāi)發(fā)故事公眾號(hào)。