cpu转

下图展示了这些术语之间的逻辑关系:
cpu 概念逻辑关系
一个NUMA node包括一个或者多个Socket,以及与之相连的local memory。一个多核的Socket有多个Core。如果CPU支持HT,OS还会把这个Core看成 2个Logical Processor。为了避免混淆,在下文中统一用socket指代Processor or Socket;为了偷懒,下文中用Processor指代Logical Processor,击键能省则省不是。

查看CPU Topology

本文以笔者能访问的某台Red Hat Enterprise Linux Server release 5.4为例介绍,其他系统请自行google。

NUMA Node

第一种方法使用numactl查看
numactl –hardware

 
1
2
3
4
5
6
7
8
9
available: 2 nodes (0-1)  //当前机器有2个NUMA node,编号0&1
node 0 size: 12091 MB  //node 0 物理内存大小
node 0 free : 988 MB    //node 0 当前free内存大小
node 1 size: 12120 MB
node 1 free : 1206 MB
node distances:        //node 距离,可以简单认为是CPU本node内存访问和跨node内存访问的成本。从下表可知跨node的内存访问成本(20)是本地node内存(10)的2倍。
node   0   1
   0:  10  20
   1:  20  10

第二种方法是通过sysfs查看,这种方式可以查看到更多的信息
ls /sys/devices/system/node/

1
node0  node1 //两个目标表示本机有2个node,每个目录内部有多个文件和子目录描述node内cpu,内存等信息。比如说node0/meminfo描述了node0内存相关信息。

Socket

可以直接通过/proc/cpuinfo查看,cpuinfo里的physical id描述的就是Socket的编号,
cat /proc/cpuinfo|grep “physical id”

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
]
physical id     : 0
physical id     : 0
physical id     : 0
physical id     : 0
physical id     : 1
physical id     : 1
physical id     : 1
physical id     : 1
physical id     : 0
physical id     : 0
physical id     : 0
physical id     : 0
physical id     : 1
physical id     : 1
physical id     : 1
physical id     : 1

由上可知本机有2个Socket,编号为0和1。
还可以简单的使用如下命令直接查看Socket个数
cat /proc/cpuinfo|grep “physical id”|sort -u|wc –l

1
2   //本机有2个物理CPU封装

Core

仍然是可以通过/proc/cpuinfo查看,cpuinfo中跟core相关的信息有2行。
cpu cores : 4 //一个socket有4个核,
core id : 1 //一个core在socket内的编号
通过如下命令可以直接查看core的数量
cat /proc/cpuinfo |grep “cpu cores”|uniq|cut -d: -f2

1
//1个socket有4个core

本机有2个socket,每个有4个core,所以一共有8个core
还可以查看core在Socket里的编号
cat /proc/cpuinfo|grep “core id”|sort -u

1
2
3
4
core id         : 0
core id         : 1
core id         : 10
core id         : 9

一个socket里面4个core的编号为0,1,9,10。是的,core id是不连续的。如果谁知道为啥麻烦通知我,先谢了。

Logical Processor

 

仍然是可以通过/proc/cpuinfo查看在OS的眼里有多少个Logical Processor

 

ls /sys/devices/system/cpu/cpu0/cache/

1
index0  index1  index2  index3

4个目录
index0:1级数据cache
index1:1级指令cache
index2:2级cache
index3:3级cache ,对应cpuinfo里的cache
目录里的文件是cache信息描述,以本机的cpu0/index0为例简单解释一下:

64*4*128=32K
文件 内容 说明
type Data 数据cache,如果查看index1就是Instruction
Level 1 L1
Size 32K 大小为32K
coherency_line_size 64
physical_line_partition 1
ways_of_associativity 4
number_of_sets 128
shared_cpu_map 00000101 表示这个cache被CPU0和CPU8 share

解释一下shared_cpu_map内容的格式:
表面上看是2进制,其实是16进制表示,每个bit表示一个cpu,1个数字可以表示4个cpu
截取00000101的后4位,转换为2进制表示

CPU id 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0×0101的2进制表示 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1

0101表示cpu8和cpu0,即cpu0的L1 data cache是和cpu8共享的。
验证一下?
cat /sys/devices/system/cpu/cpu8/cache/index0/shared_cpu_map
00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000101
再看一下index3 shared_cpu_map的例子
cat /sys/devices/system/cpu/cpu0/cache/index3/shared_cpu_map
00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000f0f

CPU id 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0x0f0f的2进制表示 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1

cpu0,1,2,3和cpu8,9,10,11共享L3 cache

小结

综合以上信息可以绘制出以下的cpu topology图:
cpu topology

抱歉,图比较大,网页上看不清楚,下面放大单node图,另一个node基本上可以类推。
cpu topology 1 node

使用CPU Topology

好吧,现在我们知道了如何查看CPU topology。那么这与各位攻城狮的工作有什么关系呢?

以淘宝搜索常见的服务模型为例,服务端把离线处理的数据load到内存中,开始监听某个服务端口,接收到客户端请求后从线程池中分配一个工作线程,该线程解析请求,读取内存中对应的数据,进行一些计算,然后把结果返回给客户端。
把这个过程简化简化再简化,抽象抽象再抽象,可以得到一个简单的测试程序,程序流程为:
1. 主线程申请2块256M的内存,使用memset初始化这两块内存的每个byte
2. 启动2个子线程,每个线程内循环16M次,在每次循环中随机读取2块内存中的各1K数据,对每个byte进行简单加和,返回。
3. 主线程等待子线程结束,打印每个线程的结果,结束。
使用-O2编译出可执行文件test,分别使用下面2个命令运行该程序。运行时间和机器配置以及当前load有关,绝对值没有意义,这里仅比较相对值。

命令 time ./test time numactl -m 0 –physcpubind=2,3 ./test
用时 real 0m38.678s
user 1m6.270s
sys 0m5.569s
real 0m28.410s
user 0m54.997s
sys 0m0.961s

发生了什么?为什么有这么大的差异?
第一个命令直观,那么我们看一下第二个命令具体做了什么:
numactl -m 0 –physcpubind=2,3 ./test
-m 0:在node 0上分配内存
–physcpubind=2,3:在cpu 2和3上运行程序,即一个线程运行在cpu2上,另一个运行在cpu3上。

参考上面的CPUtopology图就很容易理解了,由于线程绑定cpu2和3执行,共享了L3 cache,且全部内存都是本node访问,运行效率自然比随机选择cpu运行,运行中还有可能切换cpu,内存访问有可能跨node的第一种方式要快了。

接下来,让我们看看完整的表格,读者可以看看有没有惊喜:

情况 命令 用时 解释
完全由OS控制 time ./test real 0m38.678s
user 1m6.270s
sys 0m5.569s
乐观主义者,甩手掌柜型
绑定跨node的Cpu执行 time numactl –physcpubind=2,6 ./test real 0m38.657s
user 1m7.126s
sys 0m5.045s
Cpu 2和6不在同一个node,不能share L3 cache
绑定单node的Cpu执行 time numactl –physcpubind=2,3 ./test real 0m28.605s
user 0m55.161s
sys 0m0.856s
Cpu 2和3在同一个node,share L3 cache。内存使用由OS控制,一般来说node 0和1内存都会使用。
跨node内存访问+绑定单node CPU执行 time numactl -m 1 –physcpubind=2,3 ./test real 0m33.218s
user 1m4.494s
sys 0m0.911s
内存全使用node1,2个cpu在node0,内存访问比较吃亏
单node内存访问+绑定本node CPU执行 time numactl -m 0 –physcpubind=2,3 ./test real 0m28.367s
user 0m55.062s
sys 0m0.825s
内存&cpu都使用node0
单node内存访问+绑定本node 单core执行 time numactl -m 0 –physcpubind=2,10 ./test real 0m58.062s
user 1m55.520s
sys 0m0.270s
CPU2和10不但在同一个node,且在同一个core,本意是希望共享L1,L2cache,提升性能。但是不要忘了,CPU2和10是HT出来的logical Processor,在本例cpu密集型的线程中硬件争用严重,效率急剧下降。有没有发现和上一个case的时间比率很有意思?

现在谁还能说了解点cpu topology没用呢?☺

Tips

补充几个小tips,方便有兴趣的同学分析上面表格的各个case

1.查看进程的内存numa node分布

简单的说可以查看进程的numa_maps文件
cat /proc//numa_maps
文件格式可以直接:man numa_maps
为了避免输入数字pid,我使用如下命令查看:
cat /proc/$(pidof test|cut –d” ” -f1)/numa_maps

2.查看线程run在哪个processor

可以使用top命令查看一个进程的各个线程分别run在哪个processor上
同样,为了避免输入数字pid,我使用如下命令启动top:
top -p$(pidof test |sed -e ‘s/ /,/g’)
在默认配置下不显示线程信息,需要进入Top后按“shift+H”,打开线程显示。
另外,如果没有P列,还需要按“f”,按“j”,添加,这一列显示的数字就是这个线程上次run的processor id。
关于top的使用,请读者自行man top

3.另一种绑定cpu执行的方法

如果读者的程序不涉及大量内存的访问,可以通过taskset绑定cpu执行。别怪我没提醒你,仔细判断是否应该绑定到同一个core的processor上哦。
关于命令的使用,请读者自行Man taskset

猜你喜欢

转载自sunqi.iteye.com/blog/1915926
cpu