前言
https://www.cnblogs.com/studywithallofyou/p/17435497.html
https://www.cnblogs.com/studywithallofyou/p/16695550.html
上面的文章提到了一些相關的知識,本篇單獨針對CPU進行詳細講解。
CPU構造
CPU Central Processing Unit CPU Package
這個一般指的是整個CPU,包括包裝運行單元,控制單元,緩衝等。
比如下面Intel 80486DX2正反面
正面是一個金屬殼,用於散熱
反面是很多針腳,用於傳遞數據
CPU Socket CPU插槽
這個就是指主板上有幾個可以插CPU的地方,比如下面,就有兩個
Non-uniform memory access NUMA Node
爲了處理多CPU訪問內存的問題,提出了NUMA架構,就是把不同的CPU做區分,分配不同的內存通道。Node訪問自己所屬的內存(也叫local memory)速度比較快,訪問另一個Node的內存(也叫做remote memory)相對就慢一些。
正常情況NUMA Node與CPU Socket數量是一致的,一個插槽上插一個CPU,一個CPU分配一個NUMA Node。不過也有不一致的情況。
UMA
計算機一開始的架構是UMA:多個Processor使用同一個bus總線訪問內存,大家是平等的。後來隨着CPU核心增多,總線成爲了瓶頸,就把Processor分組,各自訪問一塊內存,變成了NUMA(非一致性內存訪問)。
NUMA與MySQL
Linux默認會啓用NUMA,讓程序儘可能申請local內存,如果超過了local內存,就會頻繁的swap。
如果是佔用內存比較大的程序,需要確保開啓NUMA後,local內存足夠,不然會導致性能抖動或者下降。
如果MySQL使用內存大於local memory的解決方案:
- 設置綁定策略,讓MySQL可以使用所有內存
- MySQL也有相關配置,應對這個場景。需要確認使用版本是否有該功能,如果沒有,需要下載最新的,或者編譯對應版本,在編譯是開啓對NUMA的支持。
Processor Die CPU Die 處理器芯片
Die就是指從晶圓上切下來的一個個小方塊,後續通過光刻機,刻上晶體管,形成core。
一個Die上可以擁有多個core。
Processor core
Processor core是一個獨立的單元,可以與其他core並行運算。
Integrated Circuit IC 集成電路
一個或多個Die加上一些緩存、集成顯卡組成一塊集成電路
Printed Circuit Board PCB 印刷電路板
集成電路被安裝在印刷電路版上。印刷電路板和外殼等組成一塊CPU。
如下圖,這是一塊CPU,外面綠色的是印刷電路板,中間兩個突起的方塊是Die,一個Die有兩個Core,那麼這個CPU就是雙核四核心(我們經常說的)。
一個Die打開如下圖,中間分開,上下對稱,用晶體管實現了兩個core
總結
由於CPU的設計,對不同資源做了區分,所以在高性能程序開發中,需要配置,保證最大化利用系統資源。
讓一個進程綁定在同一個core或者同一個Node下的core上,保證CPU緩存命中率,避免使用remote memory,避免上下文切換。
具體使用思路:把需要高性能運行的進程綁定到一個(一組core)上,把其他進程綁定到其他core上,避免其他進程在高性能進程佔用的core上運行,使得高性能進程綁定的core只運行一個進程,避免其他進程打擾,避免緩存丟失,避免上下文切換,提高性能。
https://en.wikipedia.org/wiki/Central_processing_unit
https://www.cc.ntu.edu.tw/chinese/epaper/0015/20101220_1508.htm
https://superuser.com/questions/324284/what-is-meant-by-the-terms-cpu-core-die-and-package
https://en.wikipedia.org/wiki/Die_(integrated_circuit)
查看cpu信息
lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 12 core個數
On-line CPU(s) list: 0-11 在線core個數
Thread(s) per core: 1 每個物理core可以有幾個thread。是否可以多個任務併發進行。邏輯核。用指令把一個物理core再分成兩個或多個併發運行,提高使用率。
Core(s) per socket: 6 每個插槽上的cpu有幾個core
Socket(s): 2 有幾個插槽,主板上插了幾塊cpu
NUMA node(s): 2 有幾個node,一般一個socket分成一個node
Vendor ID: GenuineIntel
CPU family: 6
Model: 45
Stepping: 7
CPU MHz: 1200.000
BogoMIPS: 3999.45
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 15360K
NUMA node0 CPU(s): 0-5 每個node上core編號
NUMA node1 CPU(s): 6-11
查看numa內存分配
numactl --hardware
available: 2 nodes (0-1) 兩個numa node,0和1
node 0 cpus: 0 1 2 3 4 5 node0包含0 1 2 3 4 5core
node 0 size: 8162 MB node0 local內存大小
node 0 free: 1855 MB
node 1 cpus: 6 7 8 9 10 11
node 1 size: 8192 MB
node 1 free: 4611 MB
node distances: node和local內存/remote內存距離關係
node 0 1
0: 10 20 node0訪問node0的內存是10,訪問node1的內存是20(性能下降一倍)
1: 20 10 node1訪問node0的內存是20,訪問node1的內存是10
libc
獲取系統CPU核心數
sysconf
#include <unistd.h>
long sysconf(int name);
- _SC_NPROCESSORS_CONF
The number of processors configured. See also
get_nprocs_conf(3).
- _SC_NPROCESSORS_ONLN
The number of processors currently online (available).
See also get_nprocs(3).
一個是獲取全部的CPU核心數,一個是獲取可用的(online)核心數。也有專門的APIget_nprocs_conf
和get_nprocs
。
https://man7.org/linux/man-pages/man3/sysconf.3.html
get_nprocs get_nprocs_conf
#include <sys/sysinfo.h>
int get_nprocs(void);
int get_nprocs_conf(void);
https://man7.org/linux/man-pages/man3/get_nprocs_conf.3.html
mask CPU核心表示方法
linux用位掩碼(bitmask)表示每個core
0001
第一個core[core0](core編號從0開始)
0011
第一個和第二個core[core0 core1]
1000 0001
第一個和第八個core[core0 core7]
動態申請CPU core個數
cpu_set_t *CPU_ALLOC(int num_cpus);
Allocate a CPU set large enough to hold CPUs in the range 0 to num_cpus-1.
申請保存指定cpu core個數(num_cpus)的mask空間。
size_t CPU_ALLOC_SIZE(int num_cpus);
Return the size in bytes of the CPU set that would be needed to hold CPUs in the range 0 to num_cpus-1. This macro provides the value that can be used for the setsize argument in the CPU_*_S() macros
計算指定cpu core個數(num_cpus)的mask的長度。
void CPU_FREE(cpu_set_t *set);
Free a CPU set previously allocated by CPU_ALLOC().
保存core mask的結構體cpu_set_t
默認是unsigned long
,有128個,可以表示1024個core。正常情況足夠了,如果core大於1024,需要動態申請。具體系統的core數量,通過上面的api獲取。CPU_ALLOC
與CPU_ALLOC_SIZE
中的參數num_cpus
必須一致,不然在後續使用中不匹配,導致報錯。
獲取和設置cpu親緣性
#include <sched.h>
int sched_setaffinity(pid_t pid, size_t cpusetsize,
cpu_set_t *mask);
int sched_getaffinity(pid_t pid, size_t cpusetsize,
cpu_set_t *mask);
如果pid爲0,就表示當前進程。
獲取cpu親緣性
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
sched_getaffinity(0, sizeof(cpuset), &cpuset);
cpuset中保存了當前進程使用的cpu core。
設置cpu親緣性
cpu_set_t set;
CPU_ZERO(&set);
//把core0和core2綁定到當前進程
CPU_SET(0, &set);
CPU_SET(2, &set);
sched_setaffinity(0, sizeof(set), &set);
https://man7.org/linux/man-pages/man2/sched_setaffinity.2.html
https://man7.org/linux/man-pages/man3/CPU_COUNT.3.html
https://linux.die.net/man/3/cpu_set
https://linux.die.net/man/7/cpuset
taskset
除了在代碼中使用libc api設置cpu親緣性,也可以使用taskset
進行設置。taskset底層還是調用的libc的api。
#啓動一個程序,並且綁定到mask指定的core上
taskset [options] mask command [argument...]
#不加mask,表示獲取pid指定程序的mask;增加mask,表示設置pid指定程序的mask
taskset [options] -p [mask] pid
#mask,如果不特殊說明,都是16進制,--cpu-list按照core編號指定,如下都是正確的
0x00000001
is processor #0,
0x00000003
is processors #0 and #1,
FFFFFFFF
is processors #0 through #31,
0x32
is processors #1, #4, and #5,
--cpu-list 0-2,6
is processors #0, #1, #2, and #6.
--cpu-list 0-10:2
is processors #0, #2, #4, #6, #8 and #10. The suffix ":N"
specifies stride in the range, for example 0-10:3 is
interpreted as 0,3,6,9 list.
獲取mask
taskset -p 15948
pid 15948's current affinity mask: 2
爲啓動進程設置mask
taskset -c 0 ls
爲指定進程id設置mask
taskset -pc 0,1,2 15948