plantegg

java tcp mysql performance network docker Linux

kubernetes calico网络

cni 网络

cni0 is a Linux network bridge device, all veth devices will connect to this bridge, so all Pods on the same node can communicate with each other, as explained in Kubernetes Network Model and the hotel analogy above.

cni(Container Network Interface)

CNI 全称为 Container Network Interface,是用来定义容器网络的一个 规范containernetworking/cni 是一个 CNCF 的 CNI 实现项目,包括基本额 bridge,macvlan等基本网络插件。

一般将cni各种网络插件的可执行文件二进制放到 /opt/cni/bin ,在 /etc/cni/net.d/ 下创建配置文件,剩下的就交给 K8s 或者 containerd 了,我们不关心也不了解其实现。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ls -lh /opt/cni/bin/
总用量 90M
-rwxr-x--- 1 root root 4.0M 12月 23 09:39 bandwidth
-rwxr-x--- 1 root root 35M 12月 23 09:39 calico
-rwxr-x--- 1 root root 35M 12月 23 09:39 calico-ipam
-rwxr-x--- 1 root root 3.0M 12月 23 09:39 flannel
-rwxr-x--- 1 root root 3.5M 12月 23 09:39 host-local
-rwxr-x--- 1 root root 3.1M 12月 23 09:39 loopback
-rwxr-x--- 1 root root 3.8M 12月 23 09:39 portmap
-rwxr-x--- 1 root root 3.3M 12月 23 09:39 tuning

[root@hygon3 15:55 /root]
#ls -lh /etc/cni/net.d/
总用量 12K
-rw-r--r-- 1 root root 607 12月 23 09:39 10-calico.conflist
-rw-r----- 1 root root 292 12月 23 09:47 10-flannel.conflist
-rw------- 1 root root 2.6K 12月 23 09:39 calico-kubeconfig

CNI 插件都是直接通过 exec 的方式调用,而不是通过 socket 这样 C/S 方式,所有参数都是通过环境变量、标准输入输出来实现的。

Step-by-step communication from Pod 1 to Pod 6:

  1. Package leaves *Pod 1 netns* through the *eth1* interface and reaches the root netns* through the virtual interface veth1*;
  2. Package leaves veth1* and reaches cni0*, looking for Pod 6*’s* address;
  3. Package leaves cni0* and is redirected to eth0*;
  4. Package leaves *eth0* from Master 1* and reaches the gateway*;
  5. Package leaves the *gateway* and reaches the *root netns* through the eth0* interface on Worker 1*;
  6. Package leaves eth0* and reaches cni0*, looking for Pod 6*’s* address;
  7. Package leaves *cni0* and is redirected to the *veth6* virtual interface;
  8. Package leaves the *root netns* through *veth6* and reaches the *Pod 6 netns* though the *eth6* interface;

image-20220115124747936

kubernetes calico 网络

1
2
3
4
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml

#或者老版本的calico
curl https://docs.projectcalico.org/v3.15/manifests/calico.yaml -o calico.yaml

默认calico用的是ipip封包(这个性能跟原生网络差多少有待验证,本质也是overlay网络,比flannel那种要好很多吗?)

跨宿主机的两个容器之间的流量链路是:

cali-容器eth0->宿主机cali27dce37c0e8->tunl0->内核ipip模块封包->物理网卡(ipip封包后)—远程–> 物理网卡->内核ipip模块解包->tunl0->cali-容器

image.png

Calico IPIP模式对物理网络无侵入,符合云原生容器网络要求;使用IPIP封包,性能略低于Calico BGP模式;无法使用传统防火墙管理、也无法和存量网络直接打通。Pod在Node做SNAT访问外部,Pod流量不易被监控。

img

calico ipip网络不通

集群有五台机器192.168.0.110-114, 同时每个node都有另外一个ip:192.168.3.110-114,部分节点之间不通。每台机器部署好calico网络后,会分配一个 /26 CIRD 子网(64个ip)。

案例1

目标机是10.122.127.128(宿主机ip 192.168.3.112),如果从10.122.17.64(宿主机ip 192.168.3.110) ping 10.122.127.128不通,查看10.122.127.128路由表:

1
2
3
4
5
6
[root@az3-k8s-13 ~]# ip route |grep tunl0
10.122.17.64/26 via 10.122.127.128 dev tunl0 //这条路由不通
[root@az3-k8s-13 ~]# ip route del 10.122.17.64/26 via 10.122.127.128 dev tunl0 ; ip route add 10.122.17.64/26 via 192.168.3.110 dev tunl0 proto bird onlink

[root@az3-k8s-13 ~]# ip route |grep tunl0
10.122.17.64/26 via 192.168.3.110 dev tunl0 proto bird onlink //这样就通了

在10.122.127.128抓包如下,明显可以看到icmp request到了 tunl0网卡,tunl0网卡也回复了,但是回复包没有经过kernel ipip模块封装后发到eth1上:

image.png

正常机器应该是这样,上图不正常的时候缺少红框中的reply:

image.png

解决:

1
2
ip route del 10.122.17.64/26 via 10.122.127.128 dev tunl0 ; 
ip route add 10.122.17.64/26 via 192.168.3.110 dev tunl0 proto bird onlink

删除错误路由增加新的路由就可以了,新增路由的意思是从tunl0发给10.122.17.64/26的包下一跳是 192.168.3.110。

via 192.168.3.110 表示下一跳的ip

onlink参数的作用:
使用这个参数将会告诉内核,不必检查网关是否可达。因为在linux内核中,网关与本地的网段不同是被认为不可达的,从而拒绝执行添加路由的操作。

因为tunl0网卡ip的 CIDR 是32,也就是不属于任何子网,那么这个网卡上的路由没有网关,配置路由的话必须是onlink, 内核存也没法根据子网来选择到这块网卡,所以还会加上 dev 指定网卡。

案例2

集群有五台机器192.168.0.110-114, 同时每个node都有另外一个ip:192.168.3.110-114,只有node2没有192.168.3.111这个ip,结果node2跟其他节点都不通:

1
2
3
4
5
6
7
8
9
10
11
12
#calicoctl node status
Calico process is running.

IPv4 BGP status
+---------------+-------------------+-------+------------+-------------+
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
+---------------+-------------------+-------+------------+-------------+
| 192.168.0.111 | node-to-node mesh | up | 2020-08-29 | Established |
| 192.168.3.112 | node-to-node mesh | up | 2020-08-29 | Established |
| 192.168.3.113 | node-to-node mesh | up | 2020-08-29 | Established |
| 192.168.3.114 | node-to-node mesh | up | 2020-08-29 | Established |
+---------------+-------------------+-------+------------+-------------+

从node4 ping node2,然后在node2上抓包,可以看到 icmp request都发到了node2上,但是node2收到后没有发给tunl0:

image.png

所以icmp没有回复,这里的问题在于kernel收到包后为什么不给tunl0

同样,在node2上ping node4,同时在node2上抓包,可以看到发给node4的request包和reply包:

image.png

从request包可以看到src ip 是0.111, dest ip是 3.113,因为 node2 没有192.168.3.111这个ip

非常关键的我们看到node4的回复包 src ip 不是3.113,而是0.113(根据node4的路由就应该是0.113)

image.png

这就是问题所在,从node4过来的ipip包src ip都是0.113,实际这里ipip能认识的只是3.113.

如果这个时候在3.113机器上把0.113网卡down掉,那么3.113上的:

10.122.124.128/26 via 192.168.0.111 dev tunl0 proto bird onlink 路由被自动删除,3.113将不再回复request。这是因为calico记录的node2的ip是192.168.0.111,所以会自动增加

解决办法,在node4上删除这条路由记录,也就是强制让回复包走3.113网卡,这样收发的ip就能对应上了

1
2
3
4
ip route del 192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.113
//同时将默认路由改到3.113
ip route del default via 192.168.0.253 dev eth0;
ip route add default via 192.168.3.253 dev eth1

最终OK后,node4上的ip route是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@az3-k8s-14 ~]# ip route
default via 192.168.3.253 dev eth1
10.122.17.64/26 via 192.168.3.110 dev tunl0 proto bird onlink
10.122.124.128/26 via 192.168.0.111 dev tunl0 proto bird onlink
10.122.127.128/26 via 192.168.3.112 dev tunl0 proto bird onlink
blackhole 10.122.157.128/26 proto bird
10.122.157.129 dev cali19f6ea143e3 scope link
10.122.157.130 dev cali09e016ead53 scope link
10.122.157.131 dev cali0ad3225816d scope link
10.122.157.132 dev cali55a5ff1a4aa scope link
10.122.157.133 dev cali01cf8687c65 scope link
10.122.157.134 dev cali65232d7ada6 scope link
10.122.173.128/26 via 192.168.3.114 dev tunl0 proto bird onlink
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.3.0/24 dev eth1 proto kernel scope link src 192.168.3.113

正常后的抓包, 注意这里reques dest ip 和reply的 src ip终于一致了:

1
2
3
4
5
6
7
8
9
//request
00:16:3e:02:06:1e > ee:ff:ff:ff:ff:ff, ethertype IPv4 (0x0800), length 118: (tos 0x0, ttl 64, id 57971, offset 0, flags [DF], proto IPIP (4), length 104)
192.168.0.111 > 192.168.3.110: (tos 0x0, ttl 64, id 18953, offset 0, flags [DF], proto ICMP (1), length 84)
10.122.124.128 > 10.122.17.64: ICMP echo request, id 22001, seq 4, length 64

//reply
ee:ff:ff:ff:ff:ff > 00:16:3e:02:06:1e, ethertype IPv4 (0x0800), length 118: (tos 0x0, ttl 64, id 2565, offset 0, flags [none], proto IPIP (4), length 104)
192.168.3.110 > 192.168.0.111: (tos 0x0, ttl 64, id 26374, offset 0, flags [none], proto ICMP (1), length 84)
10.122.17.64 > 10.122.124.128: ICMP echo reply, id 22001, seq 4, length 64

总结下来这两个案例都还是对路由不够了解,特别是案例2,因为有了多个网卡后导致路由更复杂。calico ipip的基本原理就是利用内核进行ipip封包,然后修改路由来保证网络的畅通。

抓包

如下图,172.16.40.116是宿主机ip,192.168.196.0 是tunl0 ip

image-20230531141428895

参考资料

https://morven.life/notes/networking-3-ipip/

https://www.cnblogs.com/bakari/p/10564347.html

https://www.cnblogs.com/goldsunshine/p/10701242.html

手工拉起flannel网络

kubernetes Flannel网络剖析

cni(Container Network Interface)

CNI 全称为 Container Network Interface,是用来定义容器网络的一个 规范containernetworking/cni 是一个 CNCF 的 CNI 实现项目,包括基本的 bridge,macvlan等基本网络插件。

一般将cni各种网络插件的可执行文件二进制放到 /usr/libexec/cni/ ,在 /etc/cni/net.d/ 下创建配置文件,剩下的就交给 K8s 或者 containerd 了,我们不关心也不了解其实现。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# ls -lh /usr/libexec/cni/
总用量 133M
-rwxr-xr-x 1 root root 4.4M 8月 18 11:51 bandwidth
-rwxr-xr-x 1 root root 4.3M 3月 6 2021 bridge
-rwxr-x--- 1 root root 31M 8月 18 11:51 calico
-rwxr-x--- 1 root root 30M 8月 18 11:51 calico-ipam
-rwxr-xr-x 1 root root 12M 3月 6 2021 dhcp
-rwxr-xr-x 1 root root 5.6M 3月 6 2021 firewall
-rwxr-xr-x 1 root root 3.1M 8月 18 11:51 flannel
-rwxr-xr-x 1 root root 3.8M 3月 6 2021 host-device
-rwxr-xr-x 1 root root 3.9M 8月 18 11:51 host-local
-rwxr-xr-x 1 root root 4.0M 3月 6 2021 ipvlan
-rwxr-xr-x 1 root root 3.6M 8月 18 11:51 loopback
-rwxr-xr-x 1 root root 4.0M 3月 6 2021 macvlan
-rwxr-xr-x 1 root root 4.2M 8月 18 11:51 portmap
-rwxr-xr-x 1 root root 4.2M 3月 6 2021 ptp
-rwxr-xr-x 1 root root 2.7M 3月 6 2021 sample
-rwxr-xr-x 1 root root 3.2M 3月 6 2021 sbr
-rwxr-xr-x 1 root root 2.8M 3月 6 2021 static
-rwxr-xr-x 1 root root 3.7M 8月 18 11:51 tuning
-rwxr-xr-x 1 root root 4.0M 3月 6 2021 vlan

#ls -lh /etc/cni/net.d/
总用量 12K
-rw-r--r-- 1 root root 607 12月 23 09:39 10-calico.conflist
-rw-r----- 1 root root 292 12月 23 09:47 10-flannel.conflist
-rw------- 1 root root 2.6K 12月 23 09:39 calico-kubeconfig

CNI 插件都是直接通过 exec 的方式调用,而不是通过 socket 这样 C/S 方式,所有参数都是通过环境变量、标准输入输出来实现的。

跨主机通信流程

Step-by-step communication from Pod 1 to Pod 6:

  1. Package leaves *Pod 1 netns* through the *eth1* interface and reaches the root netns* through the virtual interface veth1*;
  2. Package leaves veth1* and reaches cni0*, looking for Pod 6*’s* address;
  3. Package leaves cni0* and is redirected to eth0*;
  4. Package leaves *eth0* from Master 1* and reaches the gateway*;
  5. Package leaves the *gateway* and reaches the *root netns* through the eth0* interface on Worker 1*;
  6. Package leaves eth0* and reaches cni0*, looking for Pod 6*’s* address;
  7. Package leaves *cni0* and is redirected to the *veth6* virtual interface;
  8. Package leaves the *root netns* through *veth6* and reaches the *Pod 6 netns* though the *eth6* interface;

image-20220115124747936

cni0 is a Linux network bridge device, all veth devices will connect to this bridge, so all Pods on the same node can communicate with each other, as explained in Kubernetes Network Model and the hotel analogy above.

默认cni 网络是没法跨宿主机的,跨宿主机需要走overlay(比如flannel的vxlan)或者仅限宿主机全在一个二层网络可达(比如用flannel的host-gw模式)

flannel vxlan网络

什么是 flannel

Flannel is a simple and easy way to configure a layer 3 network fabric designed for Kubernetes.

Flannel 工作原理

Flannel runs a small, single binary agent called flanneld on each host, and is responsible for allocating a subnet lease to each host out of a larger, preconfigured address space. Flannel uses either the Kubernetes API or etcd directly to store the network configuration, the allocated subnets, and any auxiliary data (such as the host’s public IP). Packets are forwarded using one of several backend mechanisms including VXLAN and various cloud integrations.

核心原理就是将pod网络包通过vxlan协议封装成一个udp包,udp包的ip是数据ip,内层是pod原始网络通信包。

假如POD1访问POD4:

  1. 从POD1中出来的包先到Bridge cni0上(因为POD1对应的veth挂在了cni0上),目标mac地址是cni0的Mac
  2. 然后进入到宿主机网络,宿主机有路由 10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink ,也就是目标ip 10.244.2.3的包交由 flannel.1 来处理,目标mac地址是POD4所在机器的flannel.1的Mac
  3. flanneld 进程将包封装成vxlan 丢到eth0从宿主机1离开(封装后的目标ip是192.168.2.91,现在都是由内核来完成flanneld这个封包过程,性能好)
  4. 这个封装后的vxlan udp包正确路由到宿主机2
  5. 然后经由 flanneld 解包成 10.244.2.3 ,命中宿主机2上的路由:10.244.2.0/24 dev cni0 proto kernel scope link src 10.244.2.1 ,交给cni0(这里会过宿主机iptables
  6. cni0将包送给POD4

img

flannel容器启动的时候会给自己所在的node注入一些信息:

1
2
3
4
5
6
7
#kubectl describe node hygon4  |grep -i flannel
Annotations: flannel.alpha.coreos.com/backend-data: {"VNI":1,"VtepMAC":"66:c6:ba:a2:8f:a1"}
flannel.alpha.coreos.com/backend-type: vxlan
flannel.alpha.coreos.com/kube-subnet-manager: true
flannel.alpha.coreos.com/public-ip: 10.176.4.245 ---宿主机ip,vxlan封包所用

"VtepMAC":"66:c6:ba:a2:8f:a1"----宿主机网卡 flannel.1的mac

flannel.1 知道如何通过物理网卡打包网络包到目标地址,flanneld 会在每个host 添加 arp,以及将本机的 vxlan fdb 添加到新的 host上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//这个 flannel 集群有四个 host,这是其中一个host 
//4e:95:a9:e2:ed:28是对方 host 上 flannel.1 的 mac
#ip neigh show dev flannel.1
172.19.2.0 lladdr 4e:95:a9:e2:ed:28 PERMANENT
172.19.3.0 lladdr 2e:8b:65:d7:54:3e PERMANENT
172.19.1.0 lladdr 6a:78:f3:db:b1:9e PERMANENT

#bridge fdb show flannel.1
01:00:5e:00:00:01 dev enp125s0f0 self permanent
01:00:5e:00:00:01 dev enp125s0f1 self permanent
01:00:5e:00:00:01 dev enp125s0f2 self permanent
01:00:5e:00:00:01 dev enp125s0f3 self permanent
33:33:00:00:00:01 dev enp125s0f3 self permanent
33:33:ff:8e:d6:ac dev enp125s0f3 self permanent
01:00:5e:00:00:01 dev enp2s0f0 self permanent
01:00:5e:00:00:01 dev enp2s0f1 self permanent
33:33:00:00:00:01 dev cni0 self permanent
01:00:5e:00:00:01 dev cni0 self permanent
f2:64:e3:49:4c:c8 dev cni0 vlan 1 master cni0 permanent
f2:64:e3:49:4c:c8 dev cni0 master cni0 permanent
72:d6:f3:54:7d:d6 dev vethe54b12b5 master cni0


# ip neigh show dev flannel.1 //另一个host
172.19.2.0 lladdr 4e:95:a9:e2:ed:28 PERMANENT
172.19.3.0 lladdr 2e:8b:65:d7:54:3e PERMANENT
172.19.0.0 lladdr 92:5c:b2:af:37:62 PERMANENT

包流程:

image-20220915113511706

ARP 和 FDB:

ARP (Address Resolution Protocol) table is used by a Layer 3 device (router, switch, server, desktop) to store the IP address to MAC address entries for a specific network device.

The FDB (forwarding database) table is used by a Layer 2 device (switch/bridge) to store the MAC addresses that have been learned and which ports that MAC address was learned on. The MAC addresses are learned through transparent bridging on switches and dedicated bridges.

抓包演示packet流转以及封包解包

一次完整的抓包过程演示包的流转,从hygon3上的pod 192.168.0.4(22:d8:63:6c:e8:96) 访问 hygon4上的pod 192.168.2.56(52:e6:8e:02:80:35)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//hygon3上的pod 192.168.0.4(22:d8:63:6c:e8:96) 访问 hygon4上的pod 192.168.2.56(52:e6:8e:02:80:35),在cni0(a2:99:4f:dc:9d:5c)上抓包,跨机不走peer veth
[root@hygon3 11:08 /root]
#tcpdump -i cni0 host 192.168.2.56 -nnetvv
dropped privs to tcpdump
tcpdump: listening on cni0, link-type EN10MB (Ethernet), capture size 262144 bytes
22:d8:63:6c:e8:96 > a2:99:4f:dc:9d:5c, ethertype IPv4 (0x0800), length 614: (tos 0x0, ttl 64, id 53303, offset 0, flags [DF], proto TCP (6), length 600)
192.168.0.4.40712 > 192.168.2.56.3100: Flags [P.], cksum 0x85d7 (incorrect -> 0x801a), seq 150533649:150534197, ack 3441674662, win 507, options [nop,nop,TS val 1239838869 ecr 2297983667], length 548

//hygon3上的pod 192.168.0.4 访问 hygon4上的pod 192.168.2.56,在本机flannel.1(a2:06:5e:83:44:78)上抓包
[root@hygon3 10:53 /root]
#tcpdump -i flannel.1 host 192.168.0.4 -nnetvv
dropped privs to tcpdump
tcpdump: listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes
a2:06:5e:83:44:78 > 66:c6:ba:a2:8f:a1, ethertype IPv4 (0x0800), length 729: (tos 0x0, ttl 63, id 52997, offset 0, flags [DF], proto TCP (6), length 715)
192.168.0.4.40712 > 192.168.2.56.3100: Flags [P.], cksum 0x864a (incorrect -> 0x02ae), seq 150429115:150429778, ack 3441664870, win 507, options [nop,nop,TS val 1239381169 ecr 2297525566], length 663

[root@hygon3 11:13 /root] //通过arp 可以看到对端 flannel.1 的mac地址被缓存到了本地
#arp -n |grep 66:c6:ba:a2:8f:a1
192.168.2.0 ether 66:c6:ba:a2:8f:a1 CM flannel.1
#ip route
default via 10.176.3.247 dev p1p1
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.0.0/24 dev cni0 proto kernel scope link src 192.168.0.1
192.168.1.0/24 via 192.168.1.0 dev flannel.1 onlink
192.168.2.0/24 via 192.168.2.0 dev flannel.1 onlink
192.168.3.0/24 via 192.168.3.0 dev flannel.1 onlink
#ip a
18: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether a2:06:5e:83:44:78 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.0/32 brd 192.168.0.0 scope global flannel.1
valid_lft forever preferred_lft forever
19: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default qlen 1000
link/ether a2:99:4f:dc:9d:5c brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/24 brd 192.168.0.255 scope global cni0
valid_lft forever preferred_lft forever

//宿主机物理网卡抓包,被封成了udp的vxlan包
[root@hygon3 11:12 /root]
#tcpdump -i p1p1 udp and port 8472 -nnetvv
0c:42:a1:db:b1:a8 > 88:66:39:89:9b:cc, ethertype IPv4 (0x0800), length 967: (tos 0x0, ttl 64, id 33722, offset 0, flags [none], proto UDP (17), length 953)
10.176.3.245.45173 > 10.176.4.245.8472: [bad udp cksum 0x88c6 -> 0xe4db!] OTV, flags [I] (0x08), overlay 0, instance 1
a2:06:5e:83:44:78 > 66:c6:ba:a2:8f:a1, ethertype IPv4 (0x0800), length 917: (tos 0x0, ttl 63, id 53539, offset 0, flags [DF], proto TCP (6), length 903)
192.168.0.4.40712 > 192.168.2.56.3100: Flags [P.], cksum 0x8706 (incorrect -> 0xe31b), seq 150613328:150614179, ack 3441682214, win 507, options [nop,nop,TS val 1240166469 ecr 2298311268], length 851

---------跨机分割线--------

[root@hygon4 11:15 /root] //udp ttl为61,经过了3跳(icmp ttl为63),不过这些都和vxlan内容无关了
#tcpdump -i p1p1 udp and port 8472 -nnetvv
88:66:39:2b:3f:ec > 0c:42:a1:e9:77:2c, ethertype IPv4 (0x0800), length 736: (tos 0x0, ttl 61, id 49748, offset 0, flags [none], proto UDP (17), length 722)
10.176.3.245.45173 > 10.176.4.245.8472: [udp sum ok] OTV, flags [I] (0x08), overlay 0, instance 1
a2:06:5e:83:44:78 > 66:c6:ba:a2:8f:a1, ethertype IPv4 (0x0800), length 686: (tos 0x0, ttl 63, id 53631, offset 0, flags [DF], proto TCP (6), length 672)
192.168.0.4.40712 > 192.168.2.56.3100: Flags [P.], cksum 0x7f0c (correct), seq 150646020:150646640, ack 3441685158, win 507, options [nop,nop,TS val 1240301769 ecr 2298444568], length 620
0c:42:a1:e9:77:2c > 88:66:39:2b:3f:ec, ethertype IPv4 (0x0800), length 180: (tos 0x0, ttl 64, id 57062, offset 0, flags [none], proto UDP (17), length 166)
10.176.4.245.41515 > 10.176.3.245.8472: [bad udp cksum 0x9a23 -> 0x8e11!] OTV, flags [I] (0x08), overlay 0, instance 1
66:c6:ba:a2:8f:a1 > a2:06:5e:83:44:78, ethertype IPv4 (0x0800), length 130: (tos 0x0, ttl 63, id 12391, offset 0, flags [DF], proto TCP (6), length 116)
192.168.2.56.3100 > 192.168.0.4.40712: Flags [P.], cksum 0x83f3 (incorrect -> 0x77e1), seq 1:65, ack 620, win 501, options [nop,nop,TS val 2298447868 ecr 1240301769], length 64

//到对端hygon4上抓包, 因为途中都是vxlan,所以ttl、mac地址都不变
[root@hygon4 10:55 /root]
#tcpdump -i flannel.1 host 192.168.2.56 -nnetvv
dropped privs to tcpdump
tcpdump: listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes
a2:06:5e:83:44:78 > 66:c6:ba:a2:8f:a1, ethertype IPv4 (0x0800), length 933: (tos 0x0, ttl 63, id 52807, offset 0, flags [DF], proto TCP (6), length 919)
192.168.0.4.40712 > 192.168.2.56.3100: Flags [P.], cksum 0x8d0d (correct), seq 150361706:150362573, ack 3441658790, win 507, options [nop,nop,TS val 1239073069 ecr 2297216169], length 867

#ip a //only for flannel.1 and cni0
10: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether 66:c6:ba:a2:8f:a1 brd ff:ff:ff:ff:ff:ff
inet 192.168.2.0/32 brd 192.168.2.0 scope global flannel.1
valid_lft forever preferred_lft forever
11: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default qlen 1000
link/ether 16:97:3a:7b:53:00 brd ff:ff:ff:ff:ff:ff
inet 192.168.2.1/24 brd 192.168.2.255 scope global cni0
valid_lft forever preferred_lft forever

[root@hygon4 11:24 /root]
#arp -n | grep 44:78
192.168.0.0 ether a2:06:5e:83:44:78 CM flannel.1

//mac地址替换,ttl减1
[root@hygon4 10:55 /root]
#tcpdump -i cni0 host 192.168.2.56 -nnetvv
dropped privs to tcpdump
tcpdump: listening on cni0, link-type EN10MB (Ethernet), capture size 262144 bytes
16:97:3a:7b:53:00 > 52:e6:8e:02:80:35, ethertype IPv4 (0x0800), length 935: (tos 0x0, ttl 62, id 52829, offset 0, flags [DF], proto TCP (6), length 921)
192.168.0.4.40712 > 192.168.2.56.3100: Flags [P.], cksum 0x7aa8 (correct), seq 150369440:150370309, ack 3441659494, win 507, options [nop,nop,TS val 1239115869 ecr 2297259166], length 869

这个流转流程如下图:

flannel-network-flow

对应宿主机查询到的ip、路由信息(和上图不是对应的)

1
2
3
4
5
6
7
8
9
10
11
12
13
#ip -d -4 addr show cni0
475: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default qlen 1000
link/ether 8e:34:ba:e2:a4:c6 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535
bridge forward_delay 1500 hello_time 200 max_age 2000 ageing_time 30000 stp_state 0 priority 32768 vlan_filtering 0 vlan_protocol 802.1Q bridge_id 8000.8e:34:ba:e2:a4:c6 designated_root 8000.8e:34:ba:e2:a4:c6 root_port 0 root_path_cost 0 topology_change 0 topology_change_detected 0 hello_timer 0.00 tcn_timer 0.00 topology_change_timer 0.00 gc_timer 161.46 vlan_default_pvid 1 vlan_stats_enabled 0 group_fwd_mask 0 group_address 01:80:c2:00:00:00 mcast_snooping 1 mcast_router 1 mcast_query_use_ifaddr 0 mcast_querier 0 mcast_hash_elasticity 4 mcast_hash_max 512 mcast_last_member_count 2 mcast_startup_query_count 2 mcast_last_member_interval 100 mcast_membership_interval 26000 mcast_querier_interval 25500 mcast_query_interval 12500 mcast_query_response_interval 1000 mcast_startup_query_interval 3124 mcast_stats_enabled 0 mcast_igmp_version 2 mcast_mld_version 1 nf_call_iptables 0 nf_call_ip6tables 0 nf_call_arptables 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 192.168.3.1/24 brd 192.168.3.255 scope global cni0
valid_lft forever preferred_lft forever

#ip -d -4 addr show flannel.1 //vxlan id 1 local 10.133.2.252 dev bond0 --指定了物理网卡
474: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether fe:49:64:ae:36:af brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535
vxlan id 1 local 10.133.2.252 dev bond0 srcport 0 0 dstport 8472 nolearning ttl auto ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 192.168.3.0/32 brd 192.168.3.0 scope global flannel.1
valid_lft forever preferred_lft forever

包流转示意图

image-20220119114929034

flannel网络不通排查案例

当网络不通时,可以根据以上演示的包流转路径在不同的网络设备上抓包来定位哪个环节不通

firewalld

在麒麟系统的物理机上通过kubeadm setup集群,发现有的环境flannel网络不通,在宿主机上ping 其它物理机flannel.0网卡的ip,通过在对端宿主机抓包发现icmp收到后被防火墙扔掉了,抓包中可以看到错误信息:icmp unreachable - admin prohibited

下图中正常的icmp是直接ping 物理机ip

image-20211228203650921

The “admin prohibited filter” seen in the tcpdump output means there is a firewall blocking a connection. It does it by sending back an ICMP packet meaning precisely that: the admin of that firewall doesn’t want those packets to get through. It could be a firewall at the destination site. It could be a firewall in between. It could be iptables on the Linux system.

发现有问题的环境中宿主机的防火墙设置报错了:

1
2
12月 28 23:35:08 hygon253 firewalld[10493]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t filter -X DOCKER-ISOLATION-STAGE-1' failed: iptables: No chain/target/match by that name.
12月 28 23:35:08 hygon253 firewalld[10493]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t filter -F DOCKER-ISOLATION-STAGE-2' failed: iptables: No chain/target/match by that name.

应该是因为启动docker的时候 firewalld 是运行着的

Do you have firewalld enabled, and was it (re)started after docker was started? If so, then it’s likely that firewalld wiped docker’s IPTables rules. Restarting the docker daemon should re-create those rules.

停掉 firewalld 服务可以解决这个问题,k8s集群

flannel网络不通

Starting from Docker 1.13 default iptables policy for FORWARDING is DROP

flannel能收到包,但是cni0收不到包,说明包进到了目标宿主机,但是从flannel解开udp转送到cni的时候出了问题,大概率是iptables 拦截了包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
It seems docker version >=1.13 will add iptables rule like below,and it make this issue happen:
iptables -P FORWARD DROP

All you need to do is add a rule below:
iptables -P FORWARD ACCEPT //将FORWARD 默认规则(没有匹配到其它规则的话)改成ACCEPT

//flannel 会检查 forward chain并将之改成 accept?以下是flannel 容器日志
I0913 07:52:30.965060 1 main.go:698] Using interface with name enp2s0f0 and address 192.168.0.1
I0913 07:52:30.965128 1 main.go:720] Defaulting external address to interface address (192.168.0.1)
I0913 07:52:30.965134 1 main.go:733] Defaulting external v6 address to interface address (<nil>)
I0913 07:52:30.965243 1 vxlan.go:137] VXLAN config: VNI=1 Port=0 GBP=false Learning=false DirectRouting=false
I0913 07:52:30.966878 1 kube.go:339] Setting NodeNetworkUnavailable
I0913 07:52:30.977942 1 main.go:340] Setting up masking rules
I0913 07:52:31.332105 1 main.go:361] Changing default FORWARD chain policy to ACCEPT

宿主机多 ip 下 flannel 网络不通

宿主机有两个ip,flannel组网ip是192.168,但是默认路由在1.1.网络下,此时能 ping 通,但是curl不通端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#tcpdump -i enp2s0f0 -nettvv host 192.168.0.3 and udp
tcpdump: listening on enp2s0f0, link-type EN10MB (Ethernet), capture size 262144 bytes

//握手请求syn包,udp src ip:192.168.0.1
1660897108.334556 0c:42:a1:4f:d1:e2 > 0c:42:a1:4f:d1:ee, ethertype IPv4 (0x0800), length 124: (tos 0x0, ttl 64, id 32118, offset 0, flags [none], proto UDP (17), length 110)
192.168.0.1.56773 > 192.168.0.3.otv: [bad udp cksum 0x81c0 -> 0x459f!] OTV, flags [I] (0x08), overlay 0, instance 1
56:fa:69:e3:dc:6b > 4e:95:a9:e2:ed:28, ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 63, id 41108, offset 0, flags [DF], proto TCP (6), length 60)
172.19.0.6.35118 > 172.19.2.39.http: Flags [S], cksum 0x10c8 (correct), seq 582983385, win 64860, options [mss 1410,sackOK,TS val 2648241865 ecr 0,nop,wscale 7], length 0

//对端回复syn包, 注意udp的目标ip:1.1.1.198,应该是 192.168.0.1 才对,mac是192.168.0.1 的,mac和ip不匹配,所以被内核扔掉(但是icmp不会被扔,原因未知)
1660897108.334738 0c:42:a1:4f:d1:ee > 0c:42:a1:4f:d1:e2, ethertype IPv4 (0x0800), length 124: (tos 0x0, ttl 64, id 41433, offset 0, flags [none], proto UDP (17), length 110)
192.168.0.3.38086 > 1.1.1.198.otv: [bad udp cksum 0x5aff -> 0x1769!] OTV, flags [I] (0x08), overlay 0, instance 1
4e:95:a9:e2:ed:28 > 56:fa:69:e3:dc:6b, ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto TCP (6), length 60)
172.19.2.39.http > 172.19.0.6.35118: Flags [S.], cksum 0x8027 (correct), seq 3633913151, ack 582983386, win 64308, options [mss 1410,sackOK,TS val 3514485603 ecr 2648241865,nop,wscale 7], length 0

//没有回复第三次握手,继续发syn,因为收到syn+ack后被扔掉了
1660897109.363382 0c:42:a1:4f:d1:e2 > 0c:42:a1:4f:d1:ee, ethertype IPv4 (0x0800), length 124: (tos 0x0, ttl 64, id 32123, offset 0, flags [none], proto UDP (17), length 110)
192.168.0.1.60933 > 192.168.0.3.otv: [bad udp cksum 0x81c0 -> 0x355f!] OTV, flags [I] (0x08), overlay 0, instance 1
56:fa:69:e3:dc:6b > 4e:95:a9:e2:ed:28, ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 63, id 41109, offset 0, flags [DF], proto TCP (6), length 60)
172.19.0.6.35118 > 172.19.2.39.http: Flags [S], cksum 0x0cc3 (correct), seq 582983385, win 64860, options [mss 1410,sackOK,TS val 2648242894 ecr 0,nop,wscale 7], length 0

多ip宿主机的网卡及路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
5: enp125s0f3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 64:2c:ac:e9:78:3d brd ff:ff:ff:ff:ff:ff
inet 1.1.1.198/25 brd 1.1.1.255 scope global dynamic noprefixroute enp125s0f3
valid_lft 12463sec preferred_lft 12463sec
inet6 fe80::859a:7861:378e:d6ac/64 scope link noprefixroute
valid_lft forever preferred_lft forever
6: enp2s0f0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 0c:42:a1:4f:d1:e2 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/24 brd 192.168.0.255 scope global noprefixroute enp2s0f0
valid_lft forever preferred_lft forever

#ip route
default via 1.1.1.254 dev enp125s0f3 proto dhcp metric 101
1.1.1.128/25 dev enp125s0f3 proto kernel scope link src 1.1.1.198 metric 101
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
172.19.0.0/24 dev cni0 proto kernel scope link src 172.19.0.1
172.19.2.0/24 via 172.19.2.0 dev flannel.1 onlink
172.19.3.0/24 via 172.19.3.0 dev flannel.1 onlink
192.168.0.0/24 dev enp2s0f0 proto kernel scope link src 192.168.0.1 metric 100

解决办法:真正生效的是 flannel.1 中的地址

1
2
3
4
5
//比如 flannel 选用了以下公网ip(默认路由上的ip)导致flannel网络不通,应该选内网ip
#ip -details link show flannel.1
29: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/ether 96:ad:e2:29:29:09 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535
vxlan id 1 local 30.1.1.1 dev eno1 srcport 0 0 dstport 8472 nolearning ttl auto ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

解决办法得先删掉 flannel 网络,然后在 flannel.yaml 中指定内网网卡:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
containers:
- name: kube-flannel
image: registry:5000/quay.io/coreos/flannel:v0.14.0
command:
- /opt/bin/flanneld
args:
- --ip-masq
- --kube-subnet-mgr
#指定网卡, enp33s0f0 为内网网卡,不是默认路由
#- --iface=enp33s0f0
#— --iface-regex=[enp0s8|enp0s9]

//然后会看到 flannel.1 的地址用的是 enp33s0f0(192.168.0.1)
#ip -details link show flannel.1
40: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/ether 92:5c:b2:af:37:62 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535
vxlan id 1 local 192.168.0.1 dev enp2s0f0 srcport 0 0 dstport 8472 nolearning ttl auto ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

If you happen to have different interfaces to be matched, you can match it on a regex pattern. Let’s say the worker nodes could’ve enp0s8 or enp0s9 configured, then the flannel args would be — --iface-regex=[enp0s8|enp0s9]

修改node的annotation中flannel的 public-ip。如果因为 public-ip 不对导致网络不通,在annotation中修改public-ip没用,这个值是 flannel 读取underlay 网络配置后写进来的,同时也写到了 flannel.1 的 config 中

1
2
kubectl annotate node ky1 flannel.alpha.coreos.com/public-ip-
kubectl annotate node ky1 flannel.alpha.coreos.com/public-ip=192.168.0.1

容器调试

可以起一个容器,里面带有各种工具,然后attach 到目标容器 :https://github.com/zeromake/docker-debug/blob/master/README-zh-Hans.md

1
./docker-debug-linux-amd64 --image=CentOS8 nginx top -Hp 12 //可以先把工具安装在CentOS8,然后attach 到被调试的 nginx容器

抓包和调试 – nsenter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
获取pid:docker inspect -f {{.State.Pid}} c8f874efea06

进入namespace:nsenter --net --pid --target 17277
nsenter --net --pid --target `docker inspect -f {{.State.Pid}} c8f874efea06`

//只进入network namespace,这样看到的文件还是宿主机的,能直接用tcpdump,但是看到的网卡是容器的
nsenter --target 17277 --net

// ip netns 获取容器网络信息
1022 [2021-04-14 15:53:06] docker inspect -f '{{.State.Pid}}' ab4e471edf50 //获取容器进程id
1023 [2021-04-14 15:53:30] ls /proc/79828/ns/net
1024 [2021-04-14 15:53:57] ln -sfT /proc/79828/ns/net /var/run/netns/ab4e471edf50 //link 以便ip netns List能访问

// 宿主机上查看容器ip
1026 [2021-04-14 15:54:11] ip netns list
1028 [2021-04-14 15:55:19] ip netns exec ab4e471edf50 ifconfig

//nsenter 调试网络
Get the pause container's sandboxkey:
root@worker01:~# docker inspect k8s_POD_ubuntu-5846f86795-bcbqv_default_ea44489d-3dd4-11e8-bb37-02ecc586c8d5_0 | grep SandboxKey
"SandboxKey": "/var/run/docker/netns/82ec9e32d486",
root@worker01:~#
Now, using nsenter you can see the container's information.
root@worker01:~# nsenter --net=/var/run/docker/netns/82ec9e32d486 ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
3: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
link/ether 0a:58:0a:f4:01:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.244.1.2/24 scope global eth0
valid_lft forever preferred_lft forever
Identify the peer_ifindex, and finally you can see the veth pair endpoint in root namespace.
root@worker01:~# nsenter --net=/var/run/docker/netns/82ec9e32d486 ethtool -S eth0
NIC statistics:
peer_ifindex: 7
root@worker01:~#
root@worker01:~# ip -d link show | grep '7: veth'
7: veth5e43ca47@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default
root@worker01:~#

nsenter相当于在setns的示例程序之上做了一层封装,使我们无需指定命名空间的文件描述符,而是指定进程号即可,详细case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#docker inspect cb7b05d82153 | grep -i SandboxKey   //根据 pause 容器id找network namespace
"SandboxKey": "/var/run/docker/netns/d6b2ef3cf886",

[root@hygon252 19:00 /root]
#nsenter --net=/var/run/docker/netns/d6b2ef3cf886 ip addr show
3: eth0@if496: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default //496对应宿主机上的veth编号
link/ether 1e:95:dd:d9:88:bd brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.3.22/24 brd 192.168.3.255 scope global eth0
valid_lft forever preferred_lft forever
#nsenter --net=/var/run/docker/netns/d6b2ef3cf886 ethtool -S eth0
NIC statistics:
peer_ifindex: 496

#ip -d -4 addr show cni0
475: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default qlen 1000
link/ether 8e:34:ba:e2:a4:c6 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535
bridge forward_delay 1500 hello_time 200 max_age 2000 ageing_time 30000 stp_state 0 priority 32768 vlan_filtering 0 vlan_protocol 802.1Q bridge_id 8000.8e:34:ba:e2:a4:c6 designated_root 8000.8e:34:ba:e2:a4:c6 root_port 0 root_path_cost 0 topology_change 0 topology_change_detected 0 hello_timer 0.00 tcn_timer 0.00 topology_change_timer 0.00 gc_timer 43.31 vlan_default_pvid 1 vlan_stats_enabled 0 group_fwd_mask 0 group_address 01:80:c2:00:00:00 mcast_snooping 1 mcast_router 1 mcast_query_use_ifaddr 0 mcast_querier 0 mcast_hash_elasticity 4 mcast_hash_max 512 mcast_last_member_count 2 mcast_startup_query_count 2 mcast_last_member_interval 100 mcast_membership_interval 26000 mcast_querier_interval 25500 mcast_query_interval 12500 mcast_query_response_interval 1000 mcast_startup_query_interval 3124 mcast_stats_enabled 0 mcast_igmp_version 2 mcast_mld_version 1 nf_call_iptables 0 nf_call_ip6tables 0 nf_call_arptables 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 192.168.3.1/24 brd 192.168.3.255 scope global cni0
valid_lft forever preferred_lft forever

清理

cni信息

1
2
3
4
5
6
7
8
/etc/cni/net.d/*
/var/lib/cni/ 下存放有ip分配信息

#cat /run/flannel/subnet.env
FLANNEL_NETWORK=192.168.0.0/16
FLANNEL_SUBNET=192.168.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

calico创建的tunl0网卡是个tunnel,可以通过 ip tunnel show来查看,清理不掉(重启可以清理掉tunl0)

1
2
3
4
5
ip link set dev tunl0 name tunl0_fallback
或者
/sbin/ip link set eth1 down
/sbin/ip link set eth1 name eth123
/sbin/ip link set eth123 up

清理和创建flannel网络

查看容器网卡和宿主机上的虚拟网卡veth pair:

1
2
ip link //宿主机上执行
cat /sys/class/net/eth0/iflink //容器中执行

清理

1
2
ip link delete cni0
ip link delete flannel.1

创建

1
2
3
4
5
6
7
8
9
ip link add cni0 type bridge
ip addr add dev cni0 172.30.0.0/24

查看A simpler solution:
ip -details link show
ls -l /sys/class/net/ - virtual ones will show all in virtual and lan is on the PCI bus.

brctl show cni0
brctl addif cni0 veth1 veth2 veth3 //往cni bridge添加多个容器peer 网卡

完全可以手工创建cni0、flannel.1等网络设备,然后将 veth添加到cni0网桥上,再在宿主机配置ip route,基本一个纯手工版本打造的flannel vxlan网络就实现了,深入理解到此任何flannel网络问题都可以解决了。

flannel ip在多个node之间分配错乱

当铲掉重新部署的时候可能cni等网络有残留,导致下一次部署会报ip已存在的错误

1
(combined from similar events): Failed create pod sandbox: rpc error: code = Unknown desc = failed to set up sandbox container "f7aa44bf81b27bf0ff6c02339df2d2743cf952c1519fead4c563892d2d41a979" network for pod "nginx-deployment-6c8c86b759-f8fb7": NetworkPlugin cni failed to set up pod "nginx-deployment-6c8c86b759-f8fb7_default" network: failed to set bridge addr: "cni0" already has an IP address different from 172.19.2.1/24

可以铲掉网卡重新分配,或者给cni重新分配错误信息提示的ip

1
ifconfig cni0 172.19.2.1/24

or

1
2
3
ip link set cni0 down && ip link set flannel.1 down 
ip link delete cni0 && ip link delete flannel.1
systemctl restart containerd && systemctl restart kubelet

host-gw

实现超级简单,就是在宿主机上配置路由规则,把其它宿主机ip当成其上所有pod的下一跳,不用封包解包,所以性能奇好,但是要求所有宿主机在一个2层网络,因为ip路由规则要求是直达其它宿主机。

手工配置实现就是vxlan的超级精简版,略!

netns 操作

以下case创建一个名为 ren 的netns,然后在里面增加一对虚拟网卡veth1 veth1_p, veth1放置在ren里面,veth1_p 放在物理机上,给他们配置上ip并up就能通了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
 1004  [2021-10-27 10:49:08] ip netns add ren
1005 [2021-10-27 10:49:12] ip netns show
1006 [2021-10-27 10:49:22] ip netns exec ren route //为空
1007 [2021-10-27 10:49:29] ip netns exec ren iptables -L
1008 [2021-10-27 10:49:55] ip link add veth1 type veth peer name veth1_p //此时宿主机上能看到这两块网卡
1009 [2021-10-27 10:50:07] ip link set veth1 netns ren //将veth1从宿主机默认网络空间挪到ren中,宿主机中看不到veth1了
1010 [2021-10-27 10:50:18] ip netns exec ren route
1011 [2021-10-27 10:50:25] ip netns exec ren iptables -L
1012 [2021-10-27 10:50:39] ifconfig
1013 [2021-10-27 10:50:51] ip link list
1014 [2021-10-27 10:51:29] ip netns exec ren ip link list
1017 [2021-10-27 10:53:27] ip netns exec ren ip addr add 172.19.0.100/24 dev veth1
1018 [2021-10-27 10:53:31] ip netns exec ren ip link list
1019 [2021-10-27 10:53:39] ip netns exec ren ifconfig
1020 [2021-10-27 10:53:42] ip netns exec ren ifconfig -a
1021 [2021-10-27 10:54:13] ip netns exec ren ip link set dev veth1 up
1022 [2021-10-27 10:54:16] ip netns exec ren ifconfig
1023 [2021-10-27 10:54:22] ping 172.19.0.100
1024 [2021-10-27 10:54:35] ifconfig -a
1025 [2021-10-27 10:55:03] ip netns exec ren ip addr add 172.19.0.101/24 dev veth1_p
1026 [2021-10-27 10:55:10] ip addr add 172.19.0.101/24 dev veth1_p
1027 [2021-10-27 10:55:16] ifconfig veth1_p
1028 [2021-10-27 10:55:30] ip link set dev veth1_p up
1029 [2021-10-27 10:55:32] ifconfig veth1_p
1030 [2021-10-27 10:55:38] ping 172.19.0.101
1031 [2021-10-27 10:55:43] ping 172.19.0.100
1032 [2021-10-27 10:55:53] ip link set dev veth1_p down
1033 [2021-10-27 10:55:54] ping 172.19.0.100
1034 [2021-10-27 10:55:58] ping 172.19.0.101
1035 [2021-10-27 10:56:08] ifconfig veth1_p
1036 [2021-10-27 10:56:32] ping 172.19.0.101
1037 [2021-10-27 10:57:04] ip netns exec ren route
1038 [2021-10-27 10:57:52] ip netns exec ren ping 172.19.0.101
1039 [2021-10-27 10:57:58] ip link set dev veth1_p up
1040 [2021-10-27 10:57:59] ip netns exec ren ping 172.19.0.101
1041 [2021-10-27 10:58:06] ip netns exec ren ping 172.19.0.100
1042 [2021-10-27 10:58:14] ip netns exec ren ifconfig
1043 [2021-10-27 10:58:19] ip netns exec ren route
1044 [2021-10-27 10:58:26] ip netns exec ren ping 172.19.0.100 -I veth1
1045 [2021-10-27 10:58:58] ifconfig veth1_p
1046 [2021-10-27 10:59:10] ping 172.19.0.100
1047 [2021-10-27 10:59:26] ip netns exec ren ping 172.19.0.101 -I veth1

把网卡加入到docker0的bridge下
1160 [2021-10-27 12:17:37] brctl show
1161 [2021-10-27 12:18:05] ip link set dev veth3_p master docker0
1162 [2021-10-27 12:18:09] ip link set dev veth1_p master docker0
1163 [2021-10-27 12:18:13] ip link set dev veth2 master docker0
1164 [2021-10-27 12:18:15] brctl show

brctl showmacs br0
brctl show cni0
brctl addif cni0 veth1 veth2 veth3 //往cni bridge添加多个容器peer 网卡

Linux 上存在一个默认的网络命名空间,Linux 中的 1 号进程初始使用该默认空间。Linux 上其它所有进程都是由 1 号进程派生出来的,在派生 clone 的时候如果没有额外特别指定,所有的进程都将共享这个默认网络空间。

所有的网络设备刚创建出来都是在宿主机默认网络空间下的。可以通过 ip link set 设备名 netns 网络空间名 将设备移动到另外一个空间里去,socket也是归属在某一个网络命名空间下的,由创建socket进程所在的netns来决定socket所在的netns

1
2
3
4
5
6
7
8
9
10
11
12
//file: net/socket.c
int sock_create(int family, int type, int protocol, struct socket **res)
{
return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
}

//file: include/net/sock.h
static inline
void sock_net_set(struct sock *sk, struct net *net)
{
write_pnet(&sk->sk_net, net);
}

内核提供了三种操作命名空间的方式,分别是 clone、setns 和 unshare。ip netns add 使用的是 unshare,原理和 clone 是类似的。

Image

每个 net 下都包含了自己的路由表、iptable 以及内核参数配置等等

etcd 中存储的 flannel 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
kubectl exec -it etcd-uos21 -n=kube-system -- /bin/sh

然后:
ETCDCTL_API=3 etcdctl --key /etc/kubernetes/pki/etcd/peer.key --cert /etc/kubernetes/pki/etcd/peer.crt --cacert /etc/kubernetes/pki/etcd/ca.crt --endpoints=https://localhost:2379 get /registry/configmaps/kube-system/kube-flannel-cfg

cni-conf.json�{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
Z
net-conf.jsonI{
"Network": "172.19.0.0/18",
"Backend": {
"Type": "vxlan"
}
}
"

总结

通过无论是对flannel还是calico的学习,不管是使用vxlan还是host-gw发现这些所谓的overlay网络不过是披着一层udp的皮而已,只要我们对ip route/mac地址足够了解,这些新技术剖析下来仍然逃不过 RFC1180 描述的几个最基础的知识点(基础知识的力量)的使用而已,这一切硬核的基础知识无比简单,只要你多看看我这篇旧文《就是要你懂网络–一个网络包的旅程》

参考资料

https://morven.life/notes/networking-3-ipip/

https://www.cnblogs.com/bakari/p/10564347.html

https://www.cnblogs.com/goldsunshine/p/10701242.html

手工拉起flannel网络

《就是要你懂网络–一个网络包的旅程》

不同CPU性能大PK

前言

比较Hygon7280、Intel、AMD、鲲鹏920、飞腾2500的性能情况

CPU型号 Hygon 7280 AMD 7H12 AMD 7T83 Intel 8163 鲲鹏920 飞腾2500 倚天710
物理核数 32 32 64 24 48 64 128core
超线程 2 2 2 2
2 2 2 2 2 2 1
NUMA Node 8 2 4 2 4 16 2
L1d 32K 32K 32K 32K 64K 32K 64K
L2 512K 512K 512K 1024K 512K 2048K 1024K

AMD 7T83 有8个Die, 每个Die L3大小 32M,L2 大小4MiB, 每个Die上 L1I/L1D 各256KiB,每个Die有8core,2、3代都是带有独立 IO Die
倚天710是一路服务器,单芯片2块对称的 Die

image-20220528105526139

参与比较的几款CPU参数

IPC的说明:

IPC: insns per cycle insn/cycles 也就是每个时钟周期能执行的指令数量,越大程序跑的越快

程序的执行时间 = 指令数/(主频*IPC) //单核下,多核的话再除以核数

Hygon 7280

Hygon 7280 就是AMD Zen架构,最大IPC能到5.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
架构:                           x86_64
CPU 运行模式: 32-bit, 64-bit
字节序: Little Endian
Address sizes: 43 bits physical, 48 bits virtual
CPU: 128
在线 CPU 列表: 0-127
每个核的线程数: 2
每个座的核数: 32
座: 2
NUMA 节点: 8
厂商 ID: HygonGenuine
CPU 系列: 24
型号: 1
型号名称: Hygon C86 7280 32-core Processor
步进: 1
CPU MHz: 2194.586
BogoMIPS: 3999.63
虚拟化: AMD-V
L1d 缓存: 2 MiB
L1i 缓存: 4 MiB
L2 缓存: 32 MiB
L3 缓存: 128 MiB
NUMA 节点0 CPU: 0-7,64-71
NUMA 节点1 CPU: 8-15,72-79
NUMA 节点2 CPU: 16-23,80-87
NUMA 节点3 CPU: 24-31,88-95
NUMA 节点4 CPU: 32-39,96-103
NUMA 节点5 CPU: 40-47,104-111
NUMA 节点6 CPU: 48-55,112-119
NUMA 节点7 CPU: 56-63,120-127

架构说明:

每个CPU有4个Die,每个Die有两个CCX(2 core-Complexes),每个CCX最多有4core(例如7280/7285)共享一个L3 cache;每个Die有两个Memory Channel,每个CPU带有8个Memory Channel,并且每个Memory Channel最多支持2根Memory;

海光7系列架构图:

img

曙光H620-G30A 机型硬件结构,CPU是hygon 7280(截图只截取了Socket0)

image-20211231202402561

AMD EPYC 7T83(NC)

两路服务器,4 numa node,Z3架构

img

image-20220902113036283

image-20220902113336705

详细信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 256
On-line CPU(s) list: 0-255
Thread(s) per core: 2
Core(s) per socket: 64
Socket(s): 2
NUMA node(s): 4
Vendor ID: AuthenticAMD
CPU family: 25
Model: 1
Model name: AMD EPYC 7T83 64-Core Processor
Stepping: 1
CPU MHz: 2154.005
CPU max MHz: 2550.0000
CPU min MHz: 1500.0000
BogoMIPS: 5090.93
Virtualization: AMD-V
L1d cache: 32K
L1i cache: 32K
L2 cache: 512K
L3 cache: 32768K
NUMA node0 CPU(s): 0-31,128-159
NUMA node1 CPU(s): 32-63,160-191
NUMA node2 CPU(s): 64-95,192-223
NUMA node3 CPU(s): 96-127,224-255

#cat /sys/devices/system/cpu/cpu{0,1,8,16,30,31,32,128}/cache/index3/shared_cpu_map
00000000,00000000,00000000,000000ff,00000000,00000000,00000000,000000ff
00000000,00000000,00000000,000000ff,00000000,00000000,00000000,000000ff
00000000,00000000,00000000,0000ff00,00000000,00000000,00000000,0000ff00
00000000,00000000,00000000,00ff0000,00000000,00000000,00000000,00ff0000
00000000,00000000,00000000,ff000000,00000000,00000000,00000000,ff000000
00000000,00000000,00000000,ff000000,00000000,00000000,00000000,ff000000
00000000,00000000,000000ff,00000000,00000000,00000000,000000ff,00000000
00000000,00000000,00000000,000000ff,00000000,00000000,00000000,000000ff

#cat /sys/devices/system/cpu/cpu0/cache/index2/shared_cpu_map
00000000,00000000,00000000,00000001,00000000,00000000,00000000,00000001

L3是8个物理核,16个超线程共享,相当于单核2MB,一块CPU有8个L3,总共是256MB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#cat cpu0/cache/index3/shared_cpu_list
0-7,128-135
#cat cpu0/cache/index3/size
32768K
#cat cpu0/cache/index2/shared_cpu_list
0,128

#cat /sys/devices/system/cpu/cpu{0,1,8,16,30,31,32,128}/cache/index3/shared_cpu_list
0-7,128-135
0-7,128-135
8-15,136-143
16-23,144-151
24-31,152-159
24-31,152-159
32-39,160-167
0-7,128-135

L1D、L1I各为 2MiB,单物理核为32KB

空跑nop的IPC为6(有点吓人)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#perf stat ./cpu/test
Performance counter stats for process id '449650':

2,574.29 msec task-clock # 1.000 CPUs utilized
0 context-switches # 0.000 K/sec
0 cpu-migrations # 0.000 K/sec
0 page-faults # 0.000 K/sec
8,985,622,182 cycles # 3.491 GHz (83.33%)
4,390,929 stalled-cycles-frontend # 0.05% frontend cycles idle (83.34%)
4,387,560,442 stalled-cycles-backend # 48.83% backend cycles idle (83.34%)
53,711,907,863 instructions # 5.98 insn per cycle
# 0.08 stalled cycles per insn (83.34%)
418,902,363 branches # 162.725 M/sec (83.34%)
15,036 branch-misses # 0.00% of all branches (83.32%)

2.574347594 seconds time elapsed

sysbench 测试7T83 比7H12 略好,可能是ECS、OS等带来的差异。

测试环境:4.19.91-011.ali4000.alios7.x86_64,5.7.34-log MySQL Community Server (GPL)

测试核数 AMD EPYC 7H12 2.5G(QPS、IPC) 说明
单核 24363 0.58 CPU跑满
一对HT 33519 0.40 CPU跑满
2物理核(0-1) 48423 0.57 CPU跑满
2物理核(0,32) 跨node 46232 0.55 CPU跑满
2物理核(0,64) 跨socket 45072 0.52 CPU跑满
4物理核(0-3) 97759 0.58 CPU跑满
16物理核(0-15) 367992 0.55 CPU跑满,sys占比20%,si 10%
32物理核(0-31) 686998 0.51 CPU跑满,sys占比20%, si 12%
64物理核(0-63) 1161079 0.50 CPU跑到95%以上,sys占比20%, si 12%
64物理核(0-31,64-95) 964441 0.49 socket2上的32核一直比较闲,数据无参考意义
64物理核(0-31,64-95) 1147846 0.48 重启mysqld,立即绑核,sysbench 在32-63上,导致0-31的CPU只能跑到89%

说明,压测过程动态通过taskset绑核,所以会有数据残留其它核的cache问题

跨socket taskset绑核的时候要压很久任务才会跨socket迁移过去,也就是刚taskset后CPU是跑不满的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#numastat -p 437803

Per-node process memory usage (in MBs) for PID 437803 (mysqld)
Node 0 Node 1 Node 2
--------------- --------------- ---------------
Huge 0.00 0.00 0.00
Heap 1.15 0.00 5403.27
Stack 0.00 0.00 0.09
Private 1921.60 16.22 10647.66
---------------- --------------- --------------- ---------------
Total 1922.75 16.22 16051.02

Node 3 Total
--------------- ---------------
Huge 0.00 0.00
Heap 0.03 5404.45
Stack 0.00 0.09
Private 16.20 12601.68
---------------- --------------- ---------------
Total 16.23 18006.22

AMD EPYC 7H12(ECS)

AMD EPYC 7H12 64-Core(ECS,非物理机),最大IPC能到5.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 64
On-line CPU(s) list: 0-63
Thread(s) per core: 2
Core(s) per socket: 16
座: 2
NUMA 节点: 2
厂商 ID: AuthenticAMD
CPU 系列: 23
型号: 49
型号名称: AMD EPYC 7H12 64-Core Processor
步进: 0
CPU MHz: 2595.124
BogoMIPS: 5190.24
虚拟化: AMD-V
超管理器厂商: KVM
虚拟化类型: 完全
L1d 缓存: 32K
L1i 缓存: 32K
L2 缓存: 512K
L3 缓存: 16384K
NUMA 节点0 CPU: 0-31
NUMA 节点1 CPU: 32-63

AMD EPYC 7T83 ECS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[root@bugu88 cpu0]# cd /sys/devices/system/cpu/cpu0
[root@bugu88 cpu0]# cat cache/index0/size
32K
[root@bugu88 cpu0]# cat cache/index1/size
32K
[root@bugu88 cpu0]# cat cache/index2/size
512K
[root@bugu88 cpu0]# cat cache/index3/size
32768K
[root@bugu88 cpu0]# lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 16
On-line CPU(s) list: 0-15
Thread(s) per core: 2
Core(s) per socket: 8
座: 1
NUMA 节点: 1
厂商 ID: AuthenticAMD
CPU 系列: 25
型号: 1
型号名称: AMD EPYC 7T83 64-Core Processor
步进: 1
CPU MHz: 2545.218
BogoMIPS: 5090.43
超管理器厂商: KVM
虚拟化类型: 完全
L1d 缓存: 32K
L1i 缓存: 32K
L2 缓存: 512K
L3 缓存: 32768K
NUMA 节点0 CPU: 0-15

stream:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@bugu88 lmbench-master]# for i in $(seq 0 15); do echo $i; numactl -C $i -m 0 ./bin/stream -W 5 -N 5 -M 64M; done
0
STREAM copy latency: 0.68 nanoseconds
STREAM copy bandwidth: 23509.84 MB/sec
STREAM scale latency: 0.69 nanoseconds
STREAM scale bandwidth: 23285.51 MB/sec
STREAM add latency: 0.96 nanoseconds
STREAM add bandwidth: 25043.73 MB/sec
STREAM triad latency: 1.40 nanoseconds
STREAM triad bandwidth: 17121.79 MB/sec
1
STREAM copy latency: 0.68 nanoseconds
STREAM copy bandwidth: 23513.96 MB/sec
STREAM scale latency: 0.68 nanoseconds
STREAM scale bandwidth: 23580.06 MB/sec
STREAM add latency: 0.96 nanoseconds
STREAM add bandwidth: 25049.96 MB/sec
STREAM triad latency: 1.35 nanoseconds
STREAM triad bandwidth: 17741.93 MB/sec

Intel 8163

这次对比测试的Intel 8163 CPU信息如下,最大IPC 是4:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 96
On-line CPU(s) list: 0-95
Thread(s) per core: 2
Core(s) per socket: 24
Socket(s): 2
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 85
Model name: Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz
Stepping: 4
CPU MHz: 2499.121
CPU max MHz: 3100.0000
CPU min MHz: 1000.0000
BogoMIPS: 4998.90
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 1024K
L3 cache: 33792K
NUMA node0 CPU(s): 0-95

-----8269CY
#lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 104
On-line CPU(s) list: 0-103
Thread(s) per core: 2
Core(s) per socket: 26
Socket(s): 2
NUMA node(s): 2
Vendor ID: GenuineIntel
CPU family: 6
Model: 85
Model name: Intel(R) Xeon(R) Platinum 8269CY CPU @ 2.50GHz
Stepping: 7
CPU MHz: 3200.000
CPU max MHz: 3800.0000
CPU min MHz: 1200.0000
BogoMIPS: 4998.89
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 1024K
L3 cache: 36608K
NUMA node0 CPU(s): 0-25,52-77
NUMA node1 CPU(s): 26-51,78-103

不同 intel 型号的差异

如下图是8269CY和E5-2682上跑的MySQL在相同业务、相同流量下的差异:

image-20221121105650582

CPU使用率差异(下图8051C是E5-2682,其它是 8269CY,主频也有30%的差异)

image-20221121110004127

鲲鹏920

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[root@ARM 19:15 /root/lmbench3]
#numactl -H
available: 4 nodes (0-3)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
node 0 size: 192832 MB
node 0 free: 146830 MB
node 1 cpus: 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
node 1 size: 193533 MB
node 1 free: 175354 MB
node 2 cpus: 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
node 2 size: 193533 MB
node 2 free: 175718 MB
node 3 cpus: 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
node 3 size: 193532 MB
node 3 free: 183643 MB
node distances:
node 0 1 2 3
0: 10 12 20 22
1: 12 10 22 24
2: 20 22 10 12
3: 22 24 12 10

#lscpu
Architecture: aarch64
Byte Order: Little Endian
CPU(s): 96
On-line CPU(s) list: 0-95
Thread(s) per core: 1
Core(s) per socket: 48
Socket(s): 2
NUMA node(s): 4
Model: 0
CPU max MHz: 2600.0000
CPU min MHz: 200.0000
BogoMIPS: 200.00
L1d cache: 64K
L1i cache: 64K
L2 cache: 512K
L3 cache: 24576K
NUMA node0 CPU(s): 0-23
NUMA node1 CPU(s): 24-47
NUMA node2 CPU(s): 48-71
NUMA node3 CPU(s): 72-95
Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma dcpop asimddp asimdfhm

飞腾2500

飞腾2500用nop去跑IPC的话,只能到1,但是跑其它代码能到2.33

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#lscpu
Architecture: aarch64
Byte Order: Little Endian
CPU(s): 128
On-line CPU(s) list: 0-127
Thread(s) per core: 1
Core(s) per socket: 64
Socket(s): 2
NUMA node(s): 16
Model: 3
BogoMIPS: 100.00
L1d cache: 32K
L1i cache: 32K
L2 cache: 2048K
L3 cache: 65536K
NUMA node0 CPU(s): 0-7
NUMA node1 CPU(s): 8-15
NUMA node2 CPU(s): 16-23
NUMA node3 CPU(s): 24-31
NUMA node4 CPU(s): 32-39
NUMA node5 CPU(s): 40-47
NUMA node6 CPU(s): 48-55
NUMA node7 CPU(s): 56-63
NUMA node8 CPU(s): 64-71
NUMA node9 CPU(s): 72-79
NUMA node10 CPU(s): 80-87
NUMA node11 CPU(s): 88-95
NUMA node12 CPU(s): 96-103
NUMA node13 CPU(s): 104-111
NUMA node14 CPU(s): 112-119
NUMA node15 CPU(s): 120-127
Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid

#perf stat ./nop
failed to read counter stalled-cycles-frontend
failed to read counter stalled-cycles-backend
failed to read counter branches

Performance counter stats for './nop':

78638.700540 task-clock (msec) # 0.999 CPUs utilized
1479 context-switches # 0.019 K/sec
55 cpu-migrations # 0.001 K/sec
37 page-faults # 0.000 K/sec
165127619524 cycles # 2.100 GHz
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
165269372437 instructions # 1.00 insns per cycle
<not supported> branches
3057191 branch-misses # 0.00% of all branches

78.692839007 seconds time elapsed

#dmidecode -t processor
# dmidecode 3.0
Getting SMBIOS data from sysfs.
SMBIOS 3.2.0 present.
# SMBIOS implementations newer than version 3.0 are not
# fully supported by this version of dmidecode.

Handle 0x0004, DMI type 4, 48 bytes
Processor Information
Socket Designation: BGA3576
Type: Central Processor
Family: <OUT OF SPEC>
Manufacturer: PHYTIUM
ID: 00 00 00 00 70 1F 66 22
Version: S2500
Voltage: 0.8 V
External Clock: 50 MHz
Max Speed: 2100 MHz
Current Speed: 2100 MHz
Status: Populated, Enabled
Upgrade: Other
L1 Cache Handle: 0x0005
L2 Cache Handle: 0x0007
L3 Cache Handle: 0x0008
Serial Number: N/A
Asset Tag: No Asset Tag
Part Number: NULL
Core Count: 64
Core Enabled: 64
Thread Count: 64
Characteristics:
64-bit capable
Multi-Core
Hardware Thread
Execute Protection
Enhanced Virtualization
Power/Performance Control

其它

2Die,2node

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#lscpu
Architecture: aarch64
Byte Order: Little Endian
CPU(s): 128
On-line CPU(s) list: 0-127
Thread(s) per core: 1
Core(s) per socket: 128
Socket(s): 1
NUMA node(s): 2
Model: 0
BogoMIPS: 100.00
L1d cache: 64K
L1i cache: 64K
L2 cache: 1024K
L3 cache: 65536K //64core share
NUMA node0 CPU(s): 0-63
NUMA node1 CPU(s): 64-127
Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm ssbs sb dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh

#cat cpu{0,1,8,16,30,31,32,127}/cache/index3/shared_cpu_list
0-63
0-63
0-63
0-63
0-63
0-63
0-63
64-127

#grep -E "core|64.000" lat.log
core:0
64.00000 59.653
core:8
64.00000 62.265
core:16
64.00000 59.411
core:24
64.00000 55.836
core:32
64.00000 55.909
core:40
64.00000 56.176
core:48
64.00000 57.240
core:56
64.00000 59.485
core:64
64.00000 131.818
core:72
64.00000 127.182
core:80
64.00000 122.452
core:88
64.00000 121.673
core:96
64.00000 126.533
core:104
64.00000 125.673
core:112
64.00000 124.188
core:120
64.00000 130.202

#numactl -H
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
node 0 size: 515652 MB
node 0 free: 514913 MB
node 1 cpus: 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
node 1 size: 516086 MB
node 1 free: 514815 MB
node distances:
node 0 1
0: 10 15
1: 15 10

单核以及HT计算Prime性能比较

以上两款CPU但从物理上的指标来看似乎AMD要好很多,从工艺上AMD也要领先一代(2年),从单核参数上来说是2.0 VS 2.5GHz,但是IPC 是5 VS 4,算下来理想的单核性能刚好一致(2*5=2.5 *4)。

从外面的一些跑分结果显示也是AMD 要好,但是实际性能怎么样呢?

测试命令,这个测试命令无论在哪个CPU下,用2个物理核用时都是一个物理核的一半,所以这个计算是可以完全并行的

1
taskset -c 1 /usr/bin/sysbench --num-threads=1 --test=cpu --cpu-max-prime=50000 run //单核用一个threads,绑核; HT用2个threads,绑一对HT

测试结果为耗时,单位秒

测试项 AMD EPYC 7H12 2.5G CentOS 7.9 Hygon 7280 2.1GHz CentOS Hygon 7280 2.1GHz 麒麟 Intel 8269 2.50G Intel 8163 CPU @ 2.50GHz Intel E5-2682 v4 @ 2.50GHz
单核 prime 50000 耗时 59秒 IPC 0.56 77秒 IPC 0.55 89秒 IPC 0.56; 83 0.41 105秒 IPC 0.41 109秒 IPC 0.39
HT prime 50000 耗时 57秒 IPC 0.31 74秒 IPC 0.29 87秒 IPC 0.29 48 0.35 60秒 IPC 0.36 74秒 IPC 0.29

相同CPU下的 指令数 基本= 耗时 * IPC * 核数

以上测试结果显示Hygon 7280单核计算能力是要强过Intel 8163的,但是超线程在这个场景下太不给力,相当于没有。

当然上面的计算Prime太单纯了,代表不了复杂的业务场景,所以接下来用MySQL的查询场景来看看。

如果是arm芯片在计算prime上明显要好过x86,猜测是除法取余指令上有优化

1
2
#taskset -c 11 sysbench cpu --threads=1 --events=50000  run
sysbench 1.0.20 (using bundled LuaJIT 2.1.0-beta2)

测试结果为10秒钟的event

测试项 FT2500 2.1G 鲲鹏920-4826 2.6GHz Intel 8163 CPU @ 2.50GHz Hygon C86 7280 2.1GHz AMD 7T83
单核 prime 10秒 events 21626 IPC 0.89 30299 IPC 1.01 8435 IPC 0.41 10349 IPC 0.63 40112 IPC 1.38

对比MySQL sysbench和tpcc性能

分别将MySQL 5.7.34社区版部署到intel+AliOS以及hygon 7280+CentOS上,将mysqld绑定到单核,一样的压力配置均将CPU跑到100%,然后用sysbench测试点查, HT表示将mysqld绑定到一对HT核。

sysbench点查

测试命令类似如下:

1
sysbench --test='/usr/share/doc/sysbench/tests/db/select.lua' --oltp_tables_count=1 --report-interval=1 --oltp-table-size=10000000  --mysql-port=3307 --mysql-db=sysbench_single --mysql-user=root --mysql-password='Bj6f9g96!@#'  --max-requests=0   --oltp_skip_trx=on --oltp_auto_inc=on  --oltp_range_size=5  --mysql-table-engine=innodb --rand-init=on   --max-time=300 --mysql-host=x86.51 --num-threads=4 run

测试结果(测试中的差异AMD、Hygon CPU跑在CentOS7.9, intel CPU、Kunpeng 920 跑在AliOS上, xdb表示用集团的xdb替换社区的MySQL Server, 麒麟是国产OS):

测试核数 AMD EPYC 7H12 2.5G Hygon 7280 2.1G Hygon 7280 2.1GHz 麒麟 Intel 8269 2.50G Intel 8163 2.50G Intel 8163 2.50G XDB5.7 鲲鹏 920-4826 2.6G 鲲鹏 920-4826 2.6G XDB8.0 FT2500 alisql 8.0 本地–socket
单核 24674 0.54 13441 0.46 10236 0.39 28208 0.75 25474 0.84 29376 0.89 9694 0.49 8301 0.46 3602 0.53
一对HT 36157 0.42 21747 0.38 19417 0.37 36754 0.49 35894 0.6 40601 0.65 无HT 无HT 无HT
4物理核 94132 0.52 49822 0.46 38033 0.37 90434 0.69 350% 87254 0.73 106472 0.83 34686 0.42 28407 0.39 14232 0.53
16物理核 325409 0.48 171630 0.38 134980 0.34 371718 0.69 1500% 332967 0.72 446290 0.85 //16核比4核好! 116122 0.35 94697 0.33 59199 0.6 8core:31210 0.59
32物理核 542192 0.43 298716 0.37 255586 0.33 642548 0.64 2700% 588318 0.67 598637 0.81 CPU 2400% 228601 0.36 177424 0.32 114020 0.65
  • 麒麟OS下CPU很难跑满,大致能跑到90%-95%左右,麒麟上装的社区版MySQL-5.7.29;飞腾要特别注意mysqld所在socket,同时以上飞腾数据都是走–sock压测所得,32core走网络压测QPS为:99496(15%的网络损耗)[^说明]

Mysqld 二进制代码所在 page cache带来的性能影响

如果是飞腾跨socket影响很大,mysqld二进制跨socket性能会下降30%以上

对于鲲鹏920,双路服务器上测试,mysqld绑在node0, 但是分别将mysqld二进制load进不同的node上的page cache,然后执行点查

mysqld node0 node1 node2 node3
QPS 190120 IPC 0.40 182518 IPC 0.39 189046 IPC 0.40 186533 IPC 0.40

以上数据可以看出这里node0到node1还是很慢的,居然比跨socket还慢,反过来说鲲鹏跨socket性能很好

绑定mysqld到不同node的page cache操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#systemctl stop mysql-server

[root@poc65 /root/vmtouch]
#vmtouch -e /usr/local/mysql/bin/mysqld
Files: 1
Directories: 0
Evicted Pages: 5916 (23M)
Elapsed: 0.00322 seconds

#vmtouch -v /usr/local/mysql/bin/mysqld
/usr/local/mysql/bin/mysqld
[ ] 0/5916

Files: 1
Directories: 0
Resident Pages: 0/5916 0/23M 0%
Elapsed: 0.000204 seconds

#taskset -c 24 md5sum /usr/local/mysql/bin/mysqld

#grep mysqld /proc/`pidof mysqld`/numa_maps //检查mysqld具体绑定在哪个node上
00400000 default file=/usr/local/mysql/bin/mysqld mapped=3392 active=1 N0=3392 kernelpagesize_kB=4
0199b000 default file=/usr/local/mysql/bin/mysqld anon=10 dirty=10 mapped=134 active=10 N0=134 kernelpagesize_kB=4
01a70000 default file=/usr/local/mysql/bin/mysqld anon=43 dirty=43 mapped=120 active=43 N0=120 kernelpagesize_kB=4

网卡以及node距离带来的性能差异

在鲲鹏920+mysql5.7+alios,将内存分配锁在node0上,然后分别绑核在1、24、48、72core,进行sysbench点查对比

Core1 Core24 Core48 Core72
QPS 10800 10400 7700 7700

以上测试的时候业务进程分配的内存全限制在node0上(下面的网卡中断测试也是同样内存结构)

1
2
3
4
5
6
7
8
9
10
#/root/numa-maps-summary.pl </proc/123853/numa_maps
N0 : 5085548 ( 19.40 GB)
N1 : 4479 ( 0.02 GB)
N2 : 1 ( 0.00 GB)
active : 0 ( 0.00 GB)
anon : 5085455 ( 19.40 GB)
dirty : 5085455 ( 19.40 GB)
kernelpagesize_kB: 2176 ( 0.01 GB)
mapmax : 348 ( 0.00 GB)
mapped : 4626 ( 0.02 GB)

对比测试,将内存锁在node3上,重复进行以上测试结果如下:

Core1 Core24 Core48 Core72
QPS 10500 10000 8100 8000
1
2
3
4
5
6
7
8
9
10
11
#/root/numa-maps-summary.pl </proc/54478/numa_maps
N0 : 16 ( 0.00 GB)
N1 : 4401 ( 0.02 GB)
N2 : 1 ( 0.00 GB)
N3 : 1779989 ( 6.79 GB)
active : 0 ( 0.00 GB)
anon : 1779912 ( 6.79 GB)
dirty : 1779912 ( 6.79 GB)
kernelpagesize_kB: 1108 ( 0.00 GB)
mapmax : 334 ( 0.00 GB)
mapped : 4548 ( 0.02 GB)

机器上网卡eth1插在node0上,由以上两组对比测试发现网卡影响比内存跨node影响更大,网卡影响有20%。而内存的影响基本看不到(就近好那么一点点,但是不明显,只能解释为cache命中率很高了)

此时软中断都在node0上,如果将软中断绑定到node3上,第72core的QPS能提升到8500,并且非常稳定。同时core0的QPS下降到10000附近。

网卡软中断以及网卡远近的测试结论

测试机器只是用了一块网卡,网卡插在node0上。

一般网卡中断会占用一些CPU,如果把网卡中断挪到其它node的core上,在鲲鹏920上测试,业务跑在node3(使用全部24core),网卡中断分别在node0和node3,QPS分别是:179000 VS 175000 (此时把中断放到node0或者是和node3最近的node2上差别不大)

如果将业务跑在node0上(全部24core),网卡中断分别在node0和node1上得到的QPS分别是:204000 VS 212000

tpcc 1000仓

测试结果(测试中Hygon 7280分别跑在CentOS7.9和麒麟上, 鲲鹏/intel CPU 跑在AliOS、麒麟是国产OS):

tpcc测试数据,结果为1000仓,tpmC (NewOrders) ,未标注CPU 则为跑满了

测试核数 Intel 8269 2.50G Intel 8163 2.50G Hygon 7280 2.1GHz 麒麟 Hygon 7280 2.1G CentOS 7.9 鲲鹏 920-4826 2.6G 鲲鹏 920-4826 2.6G XDB8.0
1物理核 12392 9902 4706 7011 6619 4653
一对HT 17892 15324 8950 11778 无HT 无HT
4物理核 51525 40877 19387 380% 30046 23959 20101
8物理核 100792 81799 39664 750% 60086 42368 40572
16物理核 160798 抖动 140488 CPU抖动 75013 1400% 106419 1300-1550% 70581 1200% 79844
24物理核 188051 164757 1600-2100% 100841 1800-2000% 130815 1600-2100% 88204 1600% 115355
32物理核 195292 185171 2000-2500% 116071 1900-2400% 142746 1800-2400% 102089 1900% 143567
48物理核 19969l 195730 2100-2600% 128188 2100-2800% 149782 2000-2700% 116374 2500% 206055 4500%

tpcc并发到一定程度后主要是锁导致性能上不去,所以超多核意义不大。

如果在Hygon 7280 2.1GHz 麒麟上起两个MySQLD实例,每个实例各绑定32物理core,性能刚好翻倍:image-20210823082702539

测试过程CPU均跑满(未跑满的话会标注出来),IPC跑不起来性能就必然低,超线程虽然总性能好了但是会导致IPC降低(参考前面的公式)。可以看到对本来IPC比较低的场景,启用超线程后一般对性能会提升更大一些。

CPU核数增加到32核后,MySQL社区版性能追平xdb, 此时sysbench使用120线程压性能较好(AMD得240线程压)

32核的时候对比下MySQL 社区版在Hygon7280和Intel 8163下的表现:

image-20210817181752243

三款CPU的性能指标

测试项 AMD EPYC 7H12 2.5G Hygon 7280 2.1GHz Intel 8163 CPU @ 2.50GHz
内存带宽(MiB/s) 12190.50 6206.06 7474.45
内存延时(遍历很大一个数组) 0.334ms 0.336ms 0.429ms

在lmbench上的测试数据

stream主要用于测试带宽,对应的时延是在带宽跑满情况下的带宽。

lat_mem_rd用来测试操作不同数据大小的时延。总的来说带宽看stream、时延看lat_mem_rd

飞腾2500

用stream测试带宽和latency,可以看到带宽随着numa距离不断减少、对应的latency不断增加,到最近的numa node有10%的损耗,这个损耗和numactl给出的距离完全一致。跨socket访问内存latency是node内的3倍,带宽是三分之一,但是socket1性能和socket0性能完全一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
time for i in $(seq 7 8 128); do echo $i; numactl -C $i -m 0 ./bin/stream -W 5 -N 5 -M 64M; done

#numactl -C 7 -m 0 ./bin/stream -W 5 -N 5 -M 64M
STREAM copy latency: 2.84 nanoseconds
STREAM copy bandwidth: 5638.21 MB/sec
STREAM scale latency: 2.72 nanoseconds
STREAM scale bandwidth: 5885.97 MB/sec
STREAM add latency: 2.26 nanoseconds
STREAM add bandwidth: 10615.13 MB/sec
STREAM triad latency: 4.53 nanoseconds
STREAM triad bandwidth: 5297.93 MB/sec

#numactl -C 7 -m 1 ./bin/stream -W 5 -N 5 -M 64M
STREAM copy latency: 3.16 nanoseconds
STREAM copy bandwidth: 5058.71 MB/sec
STREAM scale latency: 3.15 nanoseconds
STREAM scale bandwidth: 5074.78 MB/sec
STREAM add latency: 2.35 nanoseconds
STREAM add bandwidth: 10197.36 MB/sec
STREAM triad latency: 5.12 nanoseconds
STREAM triad bandwidth: 4686.37 MB/sec

#numactl -C 7 -m 2 ./bin/stream -W 5 -N 5 -M 64M
STREAM copy latency: 3.85 nanoseconds
STREAM copy bandwidth: 4150.98 MB/sec
STREAM scale latency: 3.95 nanoseconds
STREAM scale bandwidth: 4054.30 MB/sec
STREAM add latency: 2.64 nanoseconds
STREAM add bandwidth: 9100.12 MB/sec
STREAM triad latency: 6.39 nanoseconds
STREAM triad bandwidth: 3757.70 MB/sec

#numactl -C 7 -m 3 ./bin/stream -W 5 -N 5 -M 64M
STREAM copy latency: 3.69 nanoseconds
STREAM copy bandwidth: 4340.24 MB/sec
STREAM scale latency: 3.62 nanoseconds
STREAM scale bandwidth: 4422.18 MB/sec
STREAM add latency: 2.47 nanoseconds
STREAM add bandwidth: 9704.82 MB/sec
STREAM triad latency: 5.74 nanoseconds
STREAM triad bandwidth: 4177.85 MB/sec

[root@101a05001.cloud.a05.am11 /root/lmbench3]
#numactl -C 7 -m 7 ./bin/stream -W 5 -N 5 -M 64M
STREAM copy latency: 3.95 nanoseconds
STREAM copy bandwidth: 4051.51 MB/sec
STREAM scale latency: 3.94 nanoseconds
STREAM scale bandwidth: 4060.63 MB/sec
STREAM add latency: 2.54 nanoseconds
STREAM add bandwidth: 9434.51 MB/sec
STREAM triad latency: 6.13 nanoseconds
STREAM triad bandwidth: 3913.36 MB/sec

[root@101a05001.cloud.a05.am11 /root/lmbench3]
#numactl -C 7 -m 10 ./bin/stream -W 5 -N 5 -M 64M
STREAM copy latency: 8.80 nanoseconds
STREAM copy bandwidth: 1817.78 MB/sec
STREAM scale latency: 8.59 nanoseconds
STREAM scale bandwidth: 1861.65 MB/sec
STREAM add latency: 5.55 nanoseconds
STREAM add bandwidth: 4320.68 MB/sec
STREAM triad latency: 13.94 nanoseconds
STREAM triad bandwidth: 1721.76 MB/sec

[root@101a05001.cloud.a05.am11 /root/lmbench3]
#numactl -C 7 -m 11 ./bin/stream -W 5 -N 5 -M 64M
STREAM copy latency: 9.27 nanoseconds
STREAM copy bandwidth: 1726.52 MB/sec
STREAM scale latency: 9.31 nanoseconds
STREAM scale bandwidth: 1718.10 MB/sec
STREAM add latency: 5.65 nanoseconds
STREAM add bandwidth: 4250.89 MB/sec
STREAM triad latency: 14.09 nanoseconds
STREAM triad bandwidth: 1703.66 MB/sec

[root@101a05001.cloud.a05.am11 /root/lmbench3]
#numactl -C 88 -m 11 ./bin/stream -W 5 -N 5 -M 64M //在另外一个socket上测试本numa,和node0性能完全一致
STREAM copy latency: 2.93 nanoseconds
STREAM copy bandwidth: 5454.67 MB/sec
STREAM scale latency: 2.96 nanoseconds
STREAM scale bandwidth: 5400.03 MB/sec
STREAM add latency: 2.28 nanoseconds
STREAM add bandwidth: 10543.42 MB/sec
STREAM triad latency: 4.52 nanoseconds
STREAM triad bandwidth: 5308.40 MB/sec

[root@101a05001.cloud.a05.am11 /root/lmbench3]
#numactl -C 7 -m 15 ./bin/stream -W 5 -N 5 -M 64M
STREAM copy latency: 8.73 nanoseconds
STREAM copy bandwidth: 1831.77 MB/sec
STREAM scale latency: 8.81 nanoseconds
STREAM scale bandwidth: 1815.13 MB/sec
STREAM add latency: 5.63 nanoseconds
STREAM add bandwidth: 4265.21 MB/sec
STREAM triad latency: 13.09 nanoseconds
STREAM triad bandwidth: 1833.68 MB/sec

Lat_mem_rd 用cpu7访问node0和node15对比结果,随着数据的加大,延时在加大,64M时能有3倍差距,和上面测试一致

下图 第一列 表示读写数据的大小(单位M),第二列表示访问延时(单位纳秒),一般可以看到在L1/L2/L3 cache大小的地方延时会有跳跃,远超过L3大小后,延时就是内存延时了

image-20210924185044090

1
numactl -C 7 -m 0 ./bin/lat_mem_rd -W 5 -N 5 -t 64M  //-C 7 cpu 7, -m 0 node0, -W 热身 -t stride

同样的机型,开关numa的测试结果,关numa 时延、带宽都差了几倍

image-20220323153507557

关闭numa的机器上测试结果随机性很强,这应该是和内存分配在那里有关系,不过如果机器一直保持这个状态反复测试的话,快的core一直快,慢的core一直慢,这是因为物理地址分配有一定的规律,在物理内存没怎么变化的情况下,快的core恰好分到的内存比较近。

同时不同机器状态(内存使用率)测试结果也不一样

鲲鹏920

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#for i in $(seq 0 15); do echo core:$i; numactl -N $i -m 7 ./bin/stream  -W 5 -N 5 -M 64M; done
STREAM copy latency: 1.84 nanoseconds
STREAM copy bandwidth: 8700.75 MB/sec
STREAM scale latency: 1.86 nanoseconds
STREAM scale bandwidth: 8623.60 MB/sec
STREAM add latency: 2.18 nanoseconds
STREAM add bandwidth: 10987.04 MB/sec
STREAM triad latency: 3.03 nanoseconds
STREAM triad bandwidth: 7926.87 MB/sec

#numactl -C 7 -m 1 ./bin/stream -W 5 -N 5 -M 64M
STREAM copy latency: 2.05 nanoseconds
STREAM copy bandwidth: 7802.45 MB/sec
STREAM scale latency: 2.08 nanoseconds
STREAM scale bandwidth: 7681.87 MB/sec
STREAM add latency: 2.19 nanoseconds
STREAM add bandwidth: 10954.76 MB/sec
STREAM triad latency: 3.17 nanoseconds
STREAM triad bandwidth: 7559.86 MB/sec

#numactl -C 7 -m 2 ./bin/stream -W 5 -N 5 -M 64M
STREAM copy latency: 3.51 nanoseconds
STREAM copy bandwidth: 4556.86 MB/sec
STREAM scale latency: 3.58 nanoseconds
STREAM scale bandwidth: 4463.66 MB/sec
STREAM add latency: 2.71 nanoseconds
STREAM add bandwidth: 8869.79 MB/sec
STREAM triad latency: 5.92 nanoseconds
STREAM triad bandwidth: 4057.12 MB/sec

#numactl -C 7 -m 3 ./bin/stream -W 5 -N 5 -M 64M
STREAM copy latency: 3.94 nanoseconds
STREAM copy bandwidth: 4064.25 MB/sec
STREAM scale latency: 3.82 nanoseconds
STREAM scale bandwidth: 4188.67 MB/sec
STREAM add latency: 2.86 nanoseconds
STREAM add bandwidth: 8390.70 MB/sec
STREAM triad latency: 4.78 nanoseconds
STREAM triad bandwidth: 5024.25 MB/sec

#numactl -C 24 -m 3 ./bin/stream -W 5 -N 5 -M 64M
STREAM copy latency: 4.10 nanoseconds
STREAM copy bandwidth: 3904.63 MB/sec
STREAM scale latency: 4.03 nanoseconds
STREAM scale bandwidth: 3969.41 MB/sec
STREAM add latency: 3.07 nanoseconds
STREAM add bandwidth: 7816.08 MB/sec
STREAM triad latency: 5.06 nanoseconds
STREAM triad bandwidth: 4738.66 MB/sec

海光7280

可以看到跨numa(一个numa也就是一个socket,等同于跨socket)RT从1.5上升到2.5,这个数据比鲲鹏920要好很多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
[root@hygon8 14:32 /root/lmbench-master]
#lscpu
架构: x86_64
CPU 运行模式: 32-bit, 64-bit
字节序: Little Endian
Address sizes: 43 bits physical, 48 bits virtual
CPU: 128
在线 CPU 列表: 0-127
每个核的线程数: 2
每个座的核数: 32
座: 2
NUMA 节点: 8
厂商 ID: HygonGenuine
CPU 系列: 24
型号: 1
型号名称: Hygon C86 7280 32-core Processor
步进: 1
CPU MHz: 2194.586
BogoMIPS: 3999.63
虚拟化: AMD-V
L1d 缓存: 2 MiB
L1i 缓存: 4 MiB
L2 缓存: 32 MiB
L3 缓存: 128 MiB
NUMA 节点0 CPU: 0-7,64-71
NUMA 节点1 CPU: 8-15,72-79
NUMA 节点2 CPU: 16-23,80-87
NUMA 节点3 CPU: 24-31,88-95
NUMA 节点4 CPU: 32-39,96-103
NUMA 节点5 CPU: 40-47,104-111
NUMA 节点6 CPU: 48-55,112-119
NUMA 节点7 CPU: 56-63,120-127

//可以看到7号core比15、23、31号core明显要快,就近访问node 0的内存,跨numa node(跨Die)没有内存交织分配
[root@hygon8 14:32 /root/lmbench-master]
#time for i in $(seq 7 8 64); do echo $i; numactl -C $i -m 0 ./bin/stream -W 5 -N 5 -M 64M; done
7
STREAM copy latency: 1.38 nanoseconds
STREAM copy bandwidth: 11559.53 MB/sec
STREAM scale latency: 1.16 nanoseconds
STREAM scale bandwidth: 13815.87 MB/sec
STREAM add latency: 1.40 nanoseconds
STREAM add bandwidth: 17145.85 MB/sec
STREAM triad latency: 1.44 nanoseconds
STREAM triad bandwidth: 16637.18 MB/sec
15
STREAM copy latency: 1.67 nanoseconds
STREAM copy bandwidth: 9591.77 MB/sec
STREAM scale latency: 1.56 nanoseconds
STREAM scale bandwidth: 10242.50 MB/sec
STREAM add latency: 1.45 nanoseconds
STREAM add bandwidth: 16581.00 MB/sec
STREAM triad latency: 2.00 nanoseconds
STREAM triad bandwidth: 12028.83 MB/sec
23
STREAM copy latency: 1.65 nanoseconds
STREAM copy bandwidth: 9701.49 MB/sec
STREAM scale latency: 1.53 nanoseconds
STREAM scale bandwidth: 10427.98 MB/sec
STREAM add latency: 1.42 nanoseconds
STREAM add bandwidth: 16846.10 MB/sec
STREAM triad latency: 1.97 nanoseconds
STREAM triad bandwidth: 12189.72 MB/sec
31
STREAM copy latency: 1.64 nanoseconds
STREAM copy bandwidth: 9742.86 MB/sec
STREAM scale latency: 1.52 nanoseconds
STREAM scale bandwidth: 10510.80 MB/sec
STREAM add latency: 1.45 nanoseconds
STREAM add bandwidth: 16559.86 MB/sec
STREAM triad latency: 1.92 nanoseconds
STREAM triad bandwidth: 12490.01 MB/sec
39
STREAM copy latency: 2.55 nanoseconds
STREAM copy bandwidth: 6286.25 MB/sec
STREAM scale latency: 2.51 nanoseconds
STREAM scale bandwidth: 6383.11 MB/sec
STREAM add latency: 1.76 nanoseconds
STREAM add bandwidth: 13660.83 MB/sec
STREAM triad latency: 3.68 nanoseconds
STREAM triad bandwidth: 6523.02 MB/sec

如果这种芯片在bios里设置Die interleaving,4块die当成一个numa node吐出来给OS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#lscpu
架构: x86_64
CPU 运行模式: 32-bit, 64-bit
字节序: Little Endian
Address sizes: 43 bits physical, 48 bits virtual
CPU: 128
在线 CPU 列表: 0-127
每个核的线程数: 2
每个座的核数: 32
座: 2
NUMA 节点: 2
厂商 ID: HygonGenuine
CPU 系列: 24
型号: 1
型号名称: Hygon C86 7280 32-core Processor
步进: 1
CPU MHz: 2108.234
BogoMIPS: 3999.45
虚拟化: AMD-V
L1d 缓存: 2 MiB
L1i 缓存: 4 MiB
L2 缓存: 32 MiB
L3 缓存: 128 MiB
//注意这里和真实物理架构不一致,bios配置了Die Interleaving Enable
//表示每路内多个Die内存交织分配,这样整个一路就是一个大Die
NUMA 节点0 CPU: 0-31,64-95
NUMA 节点1 CPU: 32-63,96-127


//enable die interleaving 后继续streaming测试
//最终测试结果表现就是7/15/23/31 core性能一致,因为默认一个numa内内存交织分配
//可以看到同一路下的四个die内存交织访问,所以4个node内存延时一样了(被平均),都不如就近快
[root@hygon3 16:09 /root/lmbench-master]
#time for i in $(seq 7 8 64); do echo $i; numactl -C $i -m 0 ./bin/stream -W 5 -N 5 -M 64M; done
7
STREAM copy latency: 1.48 nanoseconds
STREAM copy bandwidth: 10782.58 MB/sec
STREAM scale latency: 1.20 nanoseconds
STREAM scale bandwidth: 13364.38 MB/sec
STREAM add latency: 1.46 nanoseconds
STREAM add bandwidth: 16408.32 MB/sec
STREAM triad latency: 1.53 nanoseconds
STREAM triad bandwidth: 15696.00 MB/sec
15
STREAM copy latency: 1.51 nanoseconds
STREAM copy bandwidth: 10601.25 MB/sec
STREAM scale latency: 1.24 nanoseconds
STREAM scale bandwidth: 12855.87 MB/sec
STREAM add latency: 1.46 nanoseconds
STREAM add bandwidth: 16382.42 MB/sec
STREAM triad latency: 1.53 nanoseconds
STREAM triad bandwidth: 15691.48 MB/sec
23
STREAM copy latency: 1.50 nanoseconds
STREAM copy bandwidth: 10700.61 MB/sec
STREAM scale latency: 1.27 nanoseconds
STREAM scale bandwidth: 12634.63 MB/sec
STREAM add latency: 1.47 nanoseconds
STREAM add bandwidth: 16370.67 MB/sec
STREAM triad latency: 1.55 nanoseconds
STREAM triad bandwidth: 15455.75 MB/sec
31
STREAM copy latency: 1.50 nanoseconds
STREAM copy bandwidth: 10637.39 MB/sec
STREAM scale latency: 1.25 nanoseconds
STREAM scale bandwidth: 12778.99 MB/sec
STREAM add latency: 1.46 nanoseconds
STREAM add bandwidth: 16420.65 MB/sec
STREAM triad latency: 1.61 nanoseconds
STREAM triad bandwidth: 14946.80 MB/sec
39
STREAM copy latency: 2.35 nanoseconds
STREAM copy bandwidth: 6807.09 MB/sec
STREAM scale latency: 2.32 nanoseconds
STREAM scale bandwidth: 6906.93 MB/sec
STREAM add latency: 1.63 nanoseconds
STREAM add bandwidth: 14729.23 MB/sec
STREAM triad latency: 3.36 nanoseconds
STREAM triad bandwidth: 7151.67 MB/sec
47
STREAM copy latency: 2.31 nanoseconds
STREAM copy bandwidth: 6938.47 MB/sec

以华为泰山服务器(鲲鹏920芯片)配置为例image-20211228165542167

Die Interleaving 控制是否使能DIE交织。使能DIE交织能充分利用系统的DDR带宽,并尽量保证各DDR通道的带宽均衡,提升DDR的利用率

hygon5280测试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
-----hygon5280测试数据
[root@localhost lmbench-master]# for i in $(seq 0 8 24); do echo $i; numactl -C $i -m 0 ./bin/stream -W 5 -N 5 -M 64M; done
0
STREAM copy latency: 1.22 nanoseconds
STREAM copy bandwidth: 13166.34 MB/sec
STREAM scale latency: 1.13 nanoseconds
STREAM scale bandwidth: 14166.95 MB/sec
STREAM add latency: 1.15 nanoseconds
STREAM add bandwidth: 20818.63 MB/sec
STREAM triad latency: 1.39 nanoseconds
STREAM triad bandwidth: 17211.81 MB/sec
8
STREAM copy latency: 1.56 nanoseconds
STREAM copy bandwidth: 10273.07 MB/sec
STREAM scale latency: 1.50 nanoseconds
STREAM scale bandwidth: 10701.89 MB/sec
STREAM add latency: 1.20 nanoseconds
STREAM add bandwidth: 19996.68 MB/sec
STREAM triad latency: 1.93 nanoseconds
STREAM triad bandwidth: 12443.70 MB/sec
16
STREAM copy latency: 2.52 nanoseconds
STREAM copy bandwidth: 6357.71 MB/sec
STREAM scale latency: 2.48 nanoseconds
STREAM scale bandwidth: 6454.95 MB/sec
STREAM add latency: 1.67 nanoseconds
STREAM add bandwidth: 14362.51 MB/sec
STREAM triad latency: 3.65 nanoseconds
STREAM triad bandwidth: 6572.85 MB/sec
24
STREAM copy latency: 2.44 nanoseconds
STREAM copy bandwidth: 6554.24 MB/sec
STREAM scale latency: 2.41 nanoseconds
STREAM scale bandwidth: 6642.80 MB/sec
STREAM add latency: 1.44 nanoseconds
STREAM add bandwidth: 16695.82 MB/sec
STREAM triad latency: 3.61 nanoseconds
STREAM triad bandwidth: 6639.18 MB/sec
[root@localhost lmbench-master]# lscpu
架构: x86_64
CPU 运行模式: 32-bit, 64-bit
字节序: Little Endian
Address sizes: 43 bits physical, 48 bits virtual
CPU: 64
在线 CPU 列表: 0-63
每个核的线程数: 2
每个座的核数: 16
座: 2
NUMA 节点: 4
厂商 ID: HygonGenuine
CPU 系列: 24
型号: 1
型号名称: Hygon C86 5280 16-core Processor
步进: 1
Frequency boost: enabled
CPU MHz: 2799.311
CPU 最大 MHz: 2500.0000
CPU 最小 MHz: 1600.0000
BogoMIPS: 4999.36
虚拟化: AMD-V
L1d 缓存: 1 MiB
L1i 缓存: 2 MiB
L2 缓存: 16 MiB
L3 缓存: 64 MiB
NUMA 节点0 CPU: 0-7,32-39
NUMA 节点1 CPU: 8-15,40-47
NUMA 节点2 CPU: 16-23,48-55
NUMA 节点3 CPU: 24-31,56-63
Vulnerability Itlb multihit: Not affected
Vulnerability L1tf: Not affected
Vulnerability Mds: Not affected
Vulnerability Meltdown: Not affected
Vulnerability Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl and seccomp
Vulnerability Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization
Vulnerability Spectre v2: Mitigation; Full AMD retpoline, IBPB conditional, STIBP disabled, RSB
filling
Vulnerability Srbds: Not affected
Vulnerability Tsx async abort: Not affected
标记: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse3
6 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdts
cp lm constant_tsc rep_good nopl nonstop_tsc cpuid extd_apicid amd_dcm
aperfmperf pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 movbe p
opcnt xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy
abm sse4a misalignsse 3dnowprefetch osvw skinit wdt tce topoext perfct
r_core perfctr_nb bpext perfctr_llc mwaitx cpb hw_pstate sme ssbd sev
ibpb vmmcall fsgsbase bmi1 avx2 smep bmi2 rdseed adx smap clflushopt s
ha_ni xsaveopt xsavec xgetbv1 xsaves clzero irperf xsaveerptr arat npt
lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassist
s pausefilter pfthreshold avic v_vmsave_vmload vgif overflow_recov suc
cor smca

intel 8269CY

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 104
On-line CPU(s) list: 0-103
Thread(s) per core: 2
Core(s) per socket: 26
Socket(s): 2
NUMA node(s): 2
Vendor ID: GenuineIntel
CPU family: 6
Model: 85
Model name: Intel(R) Xeon(R) Platinum 8269CY CPU @ 2.50GHz
Stepping: 7
CPU MHz: 3200.000
CPU max MHz: 3800.0000
CPU min MHz: 1200.0000
BogoMIPS: 4998.89
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 1024K
L3 cache: 36608K
NUMA node0 CPU(s): 0-25,52-77
NUMA node1 CPU(s): 26-51,78-103

[root@numaopen.cloud.et93 /home/ren/lmbench3]
#time for i in $(seq 0 8 51); do echo $i; numactl -C $i -m 0 ./bin/stream -W 5 -N 5 -M 64M; done
0
STREAM copy latency: 1.15 nanoseconds
STREAM copy bandwidth: 13941.80 MB/sec
STREAM scale latency: 1.16 nanoseconds
STREAM scale bandwidth: 13799.89 MB/sec
STREAM add latency: 1.31 nanoseconds
STREAM add bandwidth: 18318.23 MB/sec
STREAM triad latency: 1.56 nanoseconds
STREAM triad bandwidth: 15356.72 MB/sec
16
STREAM copy latency: 1.12 nanoseconds
STREAM copy bandwidth: 14293.68 MB/sec
STREAM scale latency: 1.13 nanoseconds
STREAM scale bandwidth: 14162.47 MB/sec
STREAM add latency: 1.31 nanoseconds
STREAM add bandwidth: 18293.27 MB/sec
STREAM triad latency: 1.53 nanoseconds
STREAM triad bandwidth: 15692.47 MB/sec
32
STREAM copy latency: 1.52 nanoseconds
STREAM copy bandwidth: 10551.71 MB/sec
STREAM scale latency: 1.52 nanoseconds
STREAM scale bandwidth: 10508.33 MB/sec
STREAM add latency: 1.38 nanoseconds
STREAM add bandwidth: 17363.22 MB/sec
STREAM triad latency: 2.00 nanoseconds
STREAM triad bandwidth: 12024.52 MB/sec
40
STREAM copy latency: 1.49 nanoseconds
STREAM copy bandwidth: 10758.50 MB/sec
STREAM scale latency: 1.50 nanoseconds
STREAM scale bandwidth: 10680.17 MB/sec
STREAM add latency: 1.34 nanoseconds
STREAM add bandwidth: 17948.34 MB/sec
STREAM triad latency: 1.98 nanoseconds
STREAM triad bandwidth: 12133.22 MB/sec
48
STREAM copy latency: 1.49 nanoseconds
STREAM copy bandwidth: 10736.56 MB/sec
STREAM scale latency: 1.50 nanoseconds
STREAM scale bandwidth: 10692.93 MB/sec
STREAM add latency: 1.34 nanoseconds
STREAM add bandwidth: 17902.85 MB/sec
STREAM triad latency: 1.96 nanoseconds
STREAM triad bandwidth: 12239.44 MB/sec

Intel(R) Xeon(R) CPU E5-2682 v4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#time for i in $(seq 0 8 51); do echo $i; numactl -C $i -m 0 ./bin/stream -W 5 -N 5 -M 64M; done
0
STREAM copy latency: 1.59 nanoseconds
STREAM copy bandwidth: 10092.31 MB/sec
STREAM scale latency: 1.57 nanoseconds
STREAM scale bandwidth: 10169.16 MB/sec
STREAM add latency: 1.31 nanoseconds
STREAM add bandwidth: 18360.83 MB/sec
STREAM triad latency: 2.28 nanoseconds
STREAM triad bandwidth: 10503.81 MB/sec
8
STREAM copy latency: 1.55 nanoseconds
STREAM copy bandwidth: 10312.14 MB/sec
STREAM scale latency: 1.56 nanoseconds
STREAM scale bandwidth: 10283.70 MB/sec
STREAM add latency: 1.30 nanoseconds
STREAM add bandwidth: 18416.26 MB/sec
STREAM triad latency: 2.23 nanoseconds
STREAM triad bandwidth: 10777.08 MB/sec
16
STREAM copy latency: 2.02 nanoseconds
STREAM copy bandwidth: 7914.25 MB/sec
STREAM scale latency: 2.02 nanoseconds
STREAM scale bandwidth: 7919.85 MB/sec
STREAM add latency: 1.39 nanoseconds
STREAM add bandwidth: 17276.06 MB/sec
STREAM triad latency: 2.92 nanoseconds
STREAM triad bandwidth: 8231.18 MB/sec
24
STREAM copy latency: 1.99 nanoseconds
STREAM copy bandwidth: 8032.18 MB/sec
STREAM scale latency: 1.98 nanoseconds
STREAM scale bandwidth: 8061.12 MB/sec
STREAM add latency: 1.39 nanoseconds
STREAM add bandwidth: 17313.94 MB/sec
STREAM triad latency: 2.88 nanoseconds
STREAM triad bandwidth: 8318.93 MB/sec

#lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 64
On-line CPU(s) list: 0-63
Thread(s) per core: 2
Core(s) per socket: 16
Socket(s): 2
NUMA node(s): 2
Vendor ID: GenuineIntel
CPU family: 6
Model: 79
Model name: Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz
Stepping: 1
CPU MHz: 2500.000
CPU max MHz: 3000.0000
CPU min MHz: 1200.0000
BogoMIPS: 5000.06
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 40960K
NUMA node0 CPU(s): 0-15,32-47
NUMA node1 CPU(s): 16-31,48-63

AMD EPYC 7T83

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
#time for i in $(seq 0 8 255); do echo $i; numactl -C $i -m 0 ./bin/stream -W 5 -N 5 -M 64M; done
0
STREAM copy latency: 0.49 nanoseconds
STREAM copy bandwidth: 32561.30 MB/sec
STREAM scale latency: 0.49 nanoseconds
STREAM scale bandwidth: 32620.66 MB/sec
STREAM add latency: 0.87 nanoseconds
STREAM add bandwidth: 27575.20 MB/sec
STREAM triad latency: 0.70 nanoseconds
STREAM triad bandwidth: 34397.15 MB/sec
8
STREAM copy latency: 0.52 nanoseconds
STREAM copy bandwidth: 30764.47 MB/sec
STREAM scale latency: 0.53 nanoseconds
STREAM scale bandwidth: 30056.59 MB/sec
STREAM add latency: 0.87 nanoseconds
STREAM add bandwidth: 27575.20 MB/sec
STREAM triad latency: 0.69 nanoseconds
STREAM triad bandwidth: 34789.45 MB/sec
16
STREAM copy latency: 0.53 nanoseconds
STREAM copy bandwidth: 30173.15 MB/sec
STREAM scale latency: 0.54 nanoseconds
STREAM scale bandwidth: 29895.91 MB/sec
STREAM add latency: 0.87 nanoseconds
STREAM add bandwidth: 27496.11 MB/sec
STREAM triad latency: 0.70 nanoseconds
STREAM triad bandwidth: 34128.93 MB/sec
24
STREAM copy latency: 0.78 nanoseconds
STREAM copy bandwidth: 20417.69 MB/sec
STREAM scale latency: 0.51 nanoseconds
STREAM scale bandwidth: 31354.70 MB/sec
STREAM add latency: 0.87 nanoseconds
STREAM add bandwidth: 27548.79 MB/sec
STREAM triad latency: 0.69 nanoseconds
STREAM triad bandwidth: 34589.22 MB/sec
32
STREAM copy latency: 0.60 nanoseconds
STREAM copy bandwidth: 26862.34 MB/sec
STREAM scale latency: 0.58 nanoseconds
STREAM scale bandwidth: 27376.00 MB/sec
STREAM add latency: 0.87 nanoseconds
STREAM add bandwidth: 27518.66 MB/sec
STREAM triad latency: 0.78 nanoseconds
STREAM triad bandwidth: 30779.17 MB/sec
40
STREAM copy latency: 0.59 nanoseconds
STREAM copy bandwidth: 27230.21 MB/sec
STREAM scale latency: 0.59 nanoseconds
STREAM scale bandwidth: 27284.18 MB/sec
STREAM add latency: 0.87 nanoseconds
STREAM add bandwidth: 27503.63 MB/sec
STREAM triad latency: 0.77 nanoseconds
STREAM triad bandwidth: 31242.48 MB/sec
48
STREAM copy latency: 0.59 nanoseconds
STREAM copy bandwidth: 27102.37 MB/sec
STREAM scale latency: 0.59 nanoseconds
STREAM scale bandwidth: 27164.08 MB/sec
STREAM add latency: 0.87 nanoseconds
STREAM add bandwidth: 27503.63 MB/sec
STREAM triad latency: 0.76 nanoseconds
STREAM triad bandwidth: 31422.90 MB/sec
56
STREAM copy latency: 0.92 nanoseconds
STREAM copy bandwidth: 17453.54 MB/sec
STREAM scale latency: 0.59 nanoseconds
STREAM scale bandwidth: 27267.55 MB/sec
STREAM add latency: 0.87 nanoseconds
STREAM add bandwidth: 27488.61 MB/sec
STREAM triad latency: 0.77 nanoseconds
STREAM triad bandwidth: 31169.92 MB/sec
64
STREAM copy latency: 0.88 nanoseconds
STREAM copy bandwidth: 18231.15 MB/sec
STREAM scale latency: 0.84 nanoseconds
STREAM scale bandwidth: 18976.06 MB/sec
STREAM add latency: 0.91 nanoseconds
STREAM add bandwidth: 26413.87 MB/sec
STREAM triad latency: 1.08 nanoseconds
STREAM triad bandwidth: 22310.12 MB/sec
72
STREAM copy latency: 0.86 nanoseconds
STREAM copy bandwidth: 18552.45 MB/sec
STREAM scale latency: 0.84 nanoseconds
STREAM scale bandwidth: 19113.88 MB/sec
STREAM add latency: 0.91 nanoseconds
STREAM add bandwidth: 26375.81 MB/sec
STREAM triad latency: 1.08 nanoseconds
STREAM triad bandwidth: 22151.79 MB/sec
80
STREAM copy latency: 0.89 nanoseconds
STREAM copy bandwidth: 18037.59 MB/sec
STREAM scale latency: 0.87 nanoseconds
STREAM scale bandwidth: 18398.59 MB/sec
STREAM add latency: 0.92 nanoseconds
STREAM add bandwidth: 26142.91 MB/sec
STREAM triad latency: 1.08 nanoseconds
STREAM triad bandwidth: 22133.53 MB/sec
88
STREAM copy latency: 0.93 nanoseconds
STREAM copy bandwidth: 17119.60 MB/sec
STREAM scale latency: 0.94 nanoseconds
STREAM scale bandwidth: 17030.54 MB/sec
STREAM add latency: 0.92 nanoseconds
STREAM add bandwidth: 26146.30 MB/sec
STREAM triad latency: 1.08 nanoseconds
STREAM triad bandwidth: 22159.10 MB/sec
96
STREAM copy latency: 1.39 nanoseconds
STREAM copy bandwidth: 11512.93 MB/sec
STREAM scale latency: 0.87 nanoseconds
STREAM scale bandwidth: 18406.16 MB/sec
STREAM add latency: 0.92 nanoseconds
STREAM add bandwidth: 25991.03 MB/sec
STREAM triad latency: 1.09 nanoseconds
STREAM triad bandwidth: 22078.91 MB/sec
104
STREAM copy latency: 0.86 nanoseconds
STREAM copy bandwidth: 18546.04 MB/sec
STREAM scale latency: 1.39 nanoseconds
STREAM scale bandwidth: 11518.85 MB/sec
STREAM add latency: 0.91 nanoseconds
STREAM add bandwidth: 26300.01 MB/sec
STREAM triad latency: 1.06 nanoseconds
STREAM triad bandwidth: 22599.38 MB/sec
112
STREAM copy latency: 0.88 nanoseconds
STREAM copy bandwidth: 18253.46 MB/sec
STREAM scale latency: 0.85 nanoseconds
STREAM scale bandwidth: 18758.59 MB/sec
STREAM add latency: 0.91 nanoseconds
STREAM add bandwidth: 26413.87 MB/sec
STREAM triad latency: 1.06 nanoseconds
STREAM triad bandwidth: 22648.95 MB/sec
120
STREAM copy latency: 0.86 nanoseconds
STREAM copy bandwidth: 18607.75 MB/sec
STREAM scale latency: 0.84 nanoseconds
STREAM scale bandwidth: 18957.30 MB/sec
STREAM add latency: 0.91 nanoseconds
STREAM add bandwidth: 26427.74 MB/sec
STREAM triad latency: 1.08 nanoseconds
STREAM triad bandwidth: 22313.83 MB/sec
128
STREAM copy latency: 0.82 nanoseconds
STREAM copy bandwidth: 19432.13 MB/sec
STREAM scale latency: 0.87 nanoseconds
STREAM scale bandwidth: 18421.31 MB/sec
STREAM add latency: 0.98 nanoseconds
STREAM add bandwidth: 24546.03 MB/sec
STREAM triad latency: 1.06 nanoseconds
STREAM triad bandwidth: 22702.59 MB/sec
136
STREAM copy latency: 0.74 nanoseconds
STREAM copy bandwidth: 21568.01 MB/sec
STREAM scale latency: 0.74 nanoseconds
STREAM scale bandwidth: 21668.99 MB/sec
STREAM add latency: 0.90 nanoseconds
STREAM add bandwidth: 26697.59 MB/sec
STREAM triad latency: 0.91 nanoseconds
STREAM triad bandwidth: 26320.64 MB/sec
144
STREAM copy latency: 0.79 nanoseconds
STREAM copy bandwidth: 20268.45 MB/sec
STREAM scale latency: 0.66 nanoseconds
STREAM scale bandwidth: 24279.61 MB/sec
STREAM add latency: 0.89 nanoseconds
STREAM add bandwidth: 26822.08 MB/sec
STREAM triad latency: 0.84 nanoseconds
STREAM triad bandwidth: 28540.76 MB/sec
152
STREAM copy latency: 0.85 nanoseconds
STREAM copy bandwidth: 18903.90 MB/sec
STREAM scale latency: 0.56 nanoseconds
STREAM scale bandwidth: 28734.25 MB/sec
STREAM add latency: 0.88 nanoseconds
STREAM add bandwidth: 27335.58 MB/sec
STREAM triad latency: 0.75 nanoseconds
STREAM triad bandwidth: 31911.01 MB/sec
160
STREAM copy latency: 0.64 nanoseconds
STREAM copy bandwidth: 25068.68 MB/sec
STREAM scale latency: 0.63 nanoseconds
STREAM scale bandwidth: 25550.68 MB/sec
STREAM add latency: 0.88 nanoseconds
STREAM add bandwidth: 27313.33 MB/sec
STREAM triad latency: 0.82 nanoseconds
STREAM triad bandwidth: 29416.50 MB/sec
168
STREAM copy latency: 0.61 nanoseconds
STREAM copy bandwidth: 26232.33 MB/sec
STREAM scale latency: 0.60 nanoseconds
STREAM scale bandwidth: 26717.96 MB/sec
STREAM add latency: 0.88 nanoseconds
STREAM add bandwidth: 27398.82 MB/sec
STREAM triad latency: 0.79 nanoseconds
STREAM triad bandwidth: 30411.86 MB/sec
176
STREAM copy latency: 0.58 nanoseconds
STREAM copy bandwidth: 27380.19 MB/sec
STREAM scale latency: 0.58 nanoseconds
STREAM scale bandwidth: 27740.96 MB/sec
STREAM add latency: 0.94 nanoseconds
STREAM add bandwidth: 25666.31 MB/sec
STREAM triad latency: 0.77 nanoseconds
STREAM triad bandwidth: 31150.63 MB/sec
184
STREAM copy latency: 0.90 nanoseconds
STREAM copy bandwidth: 17730.21 MB/sec
STREAM scale latency: 0.57 nanoseconds
STREAM scale bandwidth: 27918.40 MB/sec
STREAM add latency: 0.87 nanoseconds
STREAM add bandwidth: 27458.61 MB/sec
STREAM triad latency: 0.76 nanoseconds
STREAM triad bandwidth: 31457.27 MB/sec
192
STREAM copy latency: 0.91 nanoseconds
STREAM copy bandwidth: 17558.57 MB/sec
STREAM scale latency: 0.88 nanoseconds
STREAM scale bandwidth: 18115.49 MB/sec
STREAM add latency: 0.92 nanoseconds
STREAM add bandwidth: 26031.36 MB/sec
STREAM triad latency: 1.12 nanoseconds
STREAM triad bandwidth: 21443.95 MB/sec
200
STREAM copy latency: 1.34 nanoseconds
STREAM copy bandwidth: 11911.40 MB/sec
STREAM scale latency: 0.85 nanoseconds
STREAM scale bandwidth: 18893.26 MB/sec
STREAM add latency: 0.91 nanoseconds
STREAM add bandwidth: 26306.88 MB/sec
STREAM triad latency: 1.09 nanoseconds
STREAM triad bandwidth: 22013.73 MB/sec
208
STREAM copy latency: 1.36 nanoseconds
STREAM copy bandwidth: 11724.12 MB/sec
STREAM scale latency: 0.86 nanoseconds
STREAM scale bandwidth: 18631.00 MB/sec
STREAM add latency: 0.92 nanoseconds
STREAM add bandwidth: 26166.69 MB/sec
STREAM triad latency: 1.10 nanoseconds
STREAM triad bandwidth: 21763.86 MB/sec
216
STREAM copy latency: 0.88 nanoseconds
STREAM copy bandwidth: 18270.85 MB/sec
STREAM scale latency: 0.85 nanoseconds
STREAM scale bandwidth: 18848.15 MB/sec
STREAM add latency: 0.92 nanoseconds
STREAM add bandwidth: 26176.90 MB/sec
STREAM triad latency: 1.10 nanoseconds
STREAM triad bandwidth: 21799.20 MB/sec
224
STREAM copy latency: 0.89 nanoseconds
STREAM copy bandwidth: 18047.29 MB/sec
STREAM scale latency: 0.86 nanoseconds
STREAM scale bandwidth: 18677.66 MB/sec
STREAM add latency: 0.92 nanoseconds
STREAM add bandwidth: 26112.39 MB/sec
STREAM triad latency: 1.09 nanoseconds
STREAM triad bandwidth: 21966.89 MB/sec
232
STREAM copy latency: 1.35 nanoseconds
STREAM copy bandwidth: 11818.58 MB/sec
STREAM scale latency: 0.82 nanoseconds
STREAM scale bandwidth: 19568.11 MB/sec
STREAM add latency: 0.91 nanoseconds
STREAM add bandwidth: 26469.44 MB/sec
STREAM triad latency: 1.06 nanoseconds
STREAM triad bandwidth: 22702.59 MB/sec
240
STREAM copy latency: 0.87 nanoseconds
STREAM copy bandwidth: 18325.74 MB/sec
STREAM scale latency: 0.83 nanoseconds
STREAM scale bandwidth: 19331.37 MB/sec
STREAM add latency: 0.91 nanoseconds
STREAM add bandwidth: 26455.52 MB/sec
STREAM triad latency: 1.06 nanoseconds
STREAM triad bandwidth: 22580.37 MB/sec
248
STREAM copy latency: 0.87 nanoseconds
STREAM copy bandwidth: 18418.79 MB/sec
STREAM scale latency: 0.84 nanoseconds
STREAM scale bandwidth: 19019.09 MB/sec
STREAM add latency: 0.91 nanoseconds
STREAM add bandwidth: 26483.37 MB/sec
STREAM triad latency: 1.08 nanoseconds
STREAM triad bandwidth: 22148.13 MB/sec

stream对比数据

总结下几个CPU用stream测试访问内存的RT以及抖动和带宽对比数据,重点关注带宽,这个测试中时延不重要

最小RT 最大RT 最大copy bandwidth 最小copy bandwidth
申威3231(2numa node) 7.09 8.75 2256.59 MB/sec 1827.88 MB/sec
飞腾2500(16 numa node) 2.84 10.34 5638.21 MB/sec 1546.68 MB/sec
鲲鹏920(4 numa node) 1.84 3.87 8700.75 MB/sec 4131.81 MB/sec
海光7280(8 numa node) 1.38 2.58 11591.48 MB/sec 6206.99 MB/sec
海光5280(4 numa node) 1.22 2.52 13166.34 MB/sec 6357.71 MB/sec
Intel8269CY(2 numa node) 1.12 1.52 14293.68 MB/sec 10551.71 MB/sec
Intel E5-2682(2 numa node) 1.58 2.02 10092.31 MB/sec 7914.25 MB/sec
AMD EPYC 7T83(4 numa node) 0.49 1.39 32561.30 MB/sec 11512.93 MB/sec
Y 1.83 3.48 8764.72 MB/sec 4593.25 MB/sec

从以上数据可以看出这5款CPU性能一款比一款好,飞腾2500慢的core上延时快到intel 8269的10倍了,平均延时5倍以上了。延时数据基本和单核上测试sysbench TPS一致。性能差不多就是:常数*主频/RT

lat_mem_rd对比数据

用不同的node上的core 跑lat_mem_rd测试访问node0内存的RT,只取最大64M的时延,时延和node距离完全一致

RT变化
飞腾2500(16 numa node) core:0 149.976
core:8 168.805
core:16 191.415
core:24 178.283
core:32 170.814
core:40 185.699
core:48 212.281
core:56 202.479
core:64 426.176
core:72 444.367
core:80 465.894
core:88 452.245
core:96 448.352
core:104 460.603
core:112 485.989
core:120 490.402
鲲鹏920(4 numa node) core:0 117.323
core:24 135.337
core:48 197.782
core:72 219.416
海光7280(8 numa node) numa0 106.839
numa1 168.583
numa2 163.925
numa3 163.690
numa4 289.628
numa5 288.632
numa6 236.615
numa7 291.880
分割行
enabled die interleaving
core:0 153.005
core:16 152.458
core:32 272.057
core:48 269.441
海光5280(4 numa node) core:0 102.574
core:8 160.989
core:16 286.850
core:24 231.197
海光7260(1 numa node) core:0 265
Intel 8269CY(2 numa node) core:0 69.792
core:26 93.107
Intel 8163(2 NUMA node) core:0 68.785
core:24 100.314
Intel 8163(1 NUMA node) core:0 100.652
core:24 67.925 //内存没有做交织
申威3231(2numa node) core:0 215.146
core:32 282.443
AMD EPYC 7T83(4 numa node) core:0 71.656
core:32 80.129
core:64 131.334
core:96 129.563
Y7(2Die,2node,1socket) core:8 42.395
core:40 36.434
core:104 105.745
core:88 124.384

core:24 62.979
core:8 69.324
core:64 137.233
core:88 127.250

133ns 205ns (待测)

测试命令:

1
for i in $(seq 0 8 127); do echo core:$i; numactl -C $i -m 0 ./bin/lat_mem_rd -W 5 -N 5 -t 64M; done >lat.log 2>&1

测试结果和numactl -H 看到的node distance完全一致,芯片厂家应该就是这样测试然后把这个延迟当做距离写进去了

AMD EPYC 7T83(4 numa node)的时延相对抖动有点大,这和架构多个小Die合并成一块CPU有关

1
2
3
4
5
6
7
8
9
10
11
12
13
#grep -E "core|64.00000" lat.log
core:0
64.00000 71.656
core:32
64.00000 80.129
core:64
64.00000 131.334
core:88
64.00000 136.774
core:96
64.00000 129.563
core:120
64.00000 140.151

AMD EPYC 7T83(4 numa node)比Intel 8269时延要大,但是带宽也高很多

bios numa on/off

NUMA 参数:

BIOS ON BIOS OFF
cmdline numa=on(默认值) NUMA 开启,内存在Node内做交织,就近有快慢之分 bios 关闭后numa后,OS层面完全不知道下层的结构,默认全局内存做交织,时延是个平均值
cmdline numa=off 交织关闭,效果同上 同上

测试在bios中开关numa,以及在OS 启动参数里设置 numa=on/off 这四种组合来对比内存时延的差异

测试CPU型号如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Model name:            Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz
CPU(s): 96
On-line CPU(s) list: 0-95
Thread(s) per core: 2
Core(s) per socket: 24
Socket(s): 2
NUMA node(s): 2
L1d cache: 32K
L1i cache: 32K
L2 cache: 1024K
L3 cache: 33792K
NUMA node0 CPU(s): 0-23,48-71 //bios on + cmdline on
NUMA node1 CPU(s): 24-47,72-95

#cat /proc/cmdline
BOOT_IMAGE=/vmlinuz-3.10.0-327.x86_64 ro crashkernel=auto vconsole.font=latarcyrheb-sun16 vconsole.keymap=us biosdevname=0 console=tty0 console=ttyS0,115200 scsi_mod.scan=sync intel_idle.max_cstate=0 pci=pcie_bus_perf ipv6.disable=1 rd.driver.pre=ahci numa=on nosmt=force

测试命令以及测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
for i in $(seq 0 24 95); do echo core:$i; numactl -C $i -m 0 ./bin/lat_mem_rd -W 5 -N 5 -t 64M; done >lat.log 2>&1

//从下面两种测试来看,bios层面 on后,不管OS 层面是否on,都不会跨node 做交织,抖动存在
//bios on 即使在OS层面关闭numa也不跨node做内存交织,抖动存在
#grep -E "core|64.00000" lat.log.biosON.cmdlineOff
core:0 //第0号核
64.00000 100.717 //64.0000为64MB, 100.717 是平均时延100.717ns, 即0号核访问node0 下的内存64MB的平均延时是100纳秒
core:24
64.00000 68.484
core:48
64.00000 101.070
core:72
64.00000 68.483
#grep -E "core|64.00000" lat.log.biosON.cmdlineON
core:0
64.00000 67.094
core:24
64.00000 100.237
core:48
64.00000 67.614
core:72
64.00000 101.096

//从下面两种测试来看只要bios off了内存就会跨node交织,大规模测试下latency是个平均值
#grep -E "core|64.00000" lat.log.biosOff.cmdlineOff //bios off 做内存交织,latency就是平均值
core:0
64.00000 85.657
core:24
64.00000 85.741
core:48
64.00000 85.977
core:72
64.00000 86.671

//bios 关闭后numa后,OS层面完全不知道下层的结构,默认一定是做交织
#grep -E "core|64.00000" lat.log.biosOff.cmdlineON
core:0
64.00000 89.123
core:24
64.00000 87.137
core:48
64.00000 87.239
core:72
64.00000 87.323

结论:在OS 启动引导参数里设置 numa=off 完全没有必要、也不能起作用,反而设置了 numa=off 只能是掩耳盗铃,让用户看不到numa结构

为什么是平均值,而不是短板效应的最慢值?

测试软件只能通过大规模数据的读写来测试获取一个平均值,所以当一大块内存读取时,虽然通过交织大块内存被切分到了快慢物理内存上,但是因为规模大慢的被平均掉了。

bios=on 同时 cmdline off时

再用Intel 的 mlc 验证下,这个结果有点意思,latency稳定在 145 而不是81 和 145两个值随机出现,应该是mlc默认选到了0核,对应这个测试数据:

1
2
3
4
5
6
7
8
9
10
11
//从下面两种测试来看,bios层面 on后,不管OS 层面是否on,都不会跨node 做交织,抖动存在
//bios on 即使在OS层面关闭numa也不跨node做内存交织,抖动存在
#grep -E "core|64.00000" lat.log.biosON.cmdlineOff
core:0
64.00000 100.717
core:24
64.00000 68.484
core:48
64.00000 101.070
core:72
64.00000 68.483

对应的mlc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#./mlc
Intel(R) Memory Latency Checker - v3.9
Measuring idle latencies (in ns)...
Numa node
Numa node 0
0 145.8

Measuring Peak Injection Memory Bandwidths for the system
Bandwidths are in MB/sec (1 MB/sec = 1,000,000 Bytes/sec)
Using all the threads from each core if Hyper-threading is enabled
Using traffic with the following read-write ratios
ALL Reads : 110598.7
3:1 Reads-Writes : 93408.5
2:1 Reads-Writes : 89249.5
1:1 Reads-Writes : 64137.3
Stream-triad like: 77310.4

Measuring Memory Bandwidths between nodes within system
Bandwidths are in MB/sec (1 MB/sec = 1,000,000 Bytes/sec)
Using all the threads from each core if Hyper-threading is enabled
Using Read-only traffic type
Numa node
Numa node 0
0 110598.4

Measuring Loaded Latencies for the system
Using all the threads from each core if Hyper-threading is enabled
Using Read-only traffic type
Inject Latency Bandwidth
Delay (ns) MB/sec
==========================
00000 506.00 111483.5
00002 505.74 112576.9
00008 505.87 112644.3
00015 508.96 112643.6
00050 574.36 112701.5
00100 501.32 112775.9
00200 475.47 112839.3
00300 224.52 91560.4
00400 194.54 70515.6
00500 185.13 57233.2
00700 178.71 41591.6
01000 170.46 29524.1
01300 165.43 22933.2
01700 164.33 17702.9
02500 164.14 12206.9

两个值都为on 时的mlc 测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#./mlc
Intel(R) Memory Latency Checker - v3.9
Measuring idle latencies (in ns)...
Numa node
Numa node 0 1
0 81.6 145.9
1 144.9 81.2

Measuring Peak Injection Memory Bandwidths for the system
Bandwidths are in MB/sec (1 MB/sec = 1,000,000 Bytes/sec)
Using all the threads from each core if Hyper-threading is enabled
Using traffic with the following read-write ratios
ALL Reads : 227204.2
3:1 Reads-Writes : 212432.5
2:1 Reads-Writes : 210423.3
1:1 Reads-Writes : 196677.2
Stream-triad like: 189691.4

说明:mlc和 lmbench 测试结果不一样,mlc 时81和145,lmbench测试是68和100,这是两种测试方法的差异而已,但是快慢差距基本是一致的

龙芯测试数据

3A5000为龙芯,执行的命令为./lat_mem_rd 128M 4096,其中 4096 参数为跳步大小。其基本原理是,通过按 给定间隔去循环读一定大小的内存区域,测量每个读平均的时间。如果区域大小小于 L1 Cache 大 小,时间应该接近 L1 的访问延迟;如果大于 L1 小于 L2,则接近 L2 访问延迟;依此类推。图中横坐 标为访问的字节数,纵坐标为访存的拍数(cycles)。

image-20220221113929547

基于跳步访问的 3A5000 和 Zen1、Skylake 各级延迟的比较(cycles)

image-20220221112527936

下图给出了 LMbench 测试得到的访存操作的并发性,执行的命令为./par_mem。访存操作的并 发性是各级 Cache 和内存所支持并发访问的能力。在 LMbench 中,访存操作并发性的测试是设计一 个链表,不断地遍历访问下一个链表中的元素,链表所跳的距离和需要测量的 Cache 容量相关,在 一段时间能并发的发起对链表的追逐操作,也就是同时很多链表在遍历,如果发现这一段时间内 能同时完成 N 个链表的追逐操作,就认为访存的并发操作是 N。

image-20220221112727377

下图列出了三款处理器的功能部件操作延迟数据,使用的命令是./lat_ops。

image-20220221112853404

龙芯stream数据

LMbench 包含了 STREAM 带宽测试工具,可以用来测试可持续的内存访问带宽情况。图表12.25列 出了三款处理器的 STREAM 带宽数据,其中 STREAM 数组大小设置为 1 亿个元素,采用 OpenMP 版本 同时运行四个线程来测试满载带宽;相应测试平台均为 CPU 的两个内存控制器各接一根内存条, 3A5000 和 Zen1 用 DDR4 3200 内存条,Skylake 用 DDR4 2400 内存条(它最高只支持这个规格)。

image-20220221113037332

从数据可以看到,虽然硬件上 3A5000 和 Zen1 都实现了 DDR4 3200,但 3A5000 的实测可持续带宽 还是有一定差距。用户程序看到的内存带宽不仅仅和内存的物理频率有关系,也和处理器内部的 各种访存队列、内存控制器的调度策略、预取器和内存时序参数设置等相关,需要进行更多分析 来定位具体的瓶颈点。像 STREAM 这样的软件测试工具,能够更好地反映某个子系统的综合能力, 因而被广泛采用。

对比结论

  • AMD单核跑分数据比较好
  • MySQL 查询场景下Intel的性能好很多
  • xdb比社区版性能要好
  • MySQL8.0比5.7在多核锁竞争场景下性能要好
  • intel最好,AMD接近Intel,海光差的比较远但是又比鲲鹏好很多,飞腾最差,尤其是跨socket简直是灾难
  • 麒麟OS性能也比CentOS略差一些
  • 从perf指标来看 鲲鹏920的L1d命中率高于8163是因为鲲鹏L1 size大;L2命中率低于8163,同样是因为鲲鹏 L2 size小;同样L1i 鲲鹏也大于8163,但是实际跑起来L1i Miss Rate更高,这说明 ARM对 L1d 使用效率低

整体来说AMD用领先了一代的工艺(7nm VS 14nm),在MySQL查询场景中终于可以接近Intel了,但是海光、鲲鹏、飞腾还是不给力。

附表

鲲鹏920 和 8163 在 MySQL 场景下的 perf 指标对比

整体对比
指标 X86 ARM 增加幅度
IPC 0.4979 0.495 -0.6%
Branchs 237606414772 415979894985 75.1%
Branch-misses 8104247620 28983836845 257.6%
Branch-missed rate 0.034 0.070 104.3%
内存读带宽(GB/S) 25.0 25.0 -0.2%
内存写带宽(GB/S) 24.6 67.8 175.5%
内存读写带宽(GB/S) 49.7 92.8 86.8%
UNALIGNED_ACCESS 1329146645 13686011901 929.7%
L1d_MISS_RATIO 0.06055 0.04281 -29.3%
L1d_MISS_RATE 0.01645 0.01711 4.0%
L2_MISS_RATIO 0.34824 0.47162 35.4%
L2_MISS_RATE 0.00577 0.03493 504.8%
L1_ITLB_MISS_RATE 0.0028 0.005 78.6%
L1_DTLB_MISS_RATE 0.0025 0.0102 308.0%
context-switchs 8407195 11614981 38.2%
Pagefault 228371 741189 224.6%

参考资料

CPU的制造和概念

[CPU 性能和Cache Line](/2021/05/16/CPU Cache Line 和性能/)

[Perf IPC以及CPU性能](/2021/05/16/Perf IPC以及CPU利用率/)

Intel、海光、鲲鹏920、飞腾2500 CPU性能对比

飞腾ARM芯片(FT2500)的性能测试

十年后数据库还是不敢拥抱NUMA?

一次海光物理机资源竞争压测的记录

[Intel PAUSE指令变化是如何影响自旋锁以及MySQL的性能的](/2019/12/16/Intel PAUSE指令变化是如何影响自旋锁以及MySQL的性能的/)

lmbench测试要考虑cache等

comment:

Intel 8163 IPC是0.67,和在PostgreSQL下测得数据基本一致。Oracle可以达到更高的IPC。从8163的perf结果中,看不出来访存在总周期中的占比。可以添加几个cycle_activity.cycles_l1d_miss、cycle_activity.stalls_mem_any,看看访存耗用的周期占比。

网络抓包常用命令

详细解析和Demo版本:就是要你懂抓包–WireShark之命令行版tshark

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
//抓取一个子网范围
tcpdump -i bond0 port 3001 and net 1.2.3.0/24 and host not 1.2.3.211 -nn -X

//抓取 DNAT 包,tcp options 里面的 246 代表 DNAT
tcpdump -nn –vvv -i eth0 tcp dst port 3306 and '(tcp[tcpflags] & (tcp-syn) != 0) and (tcp[20] =246) '

//在上面的基础上,抓取指定 vip:10.142.*.*
tcpdump -nn –vvv -i eth0 tcp dst port 3306 and '(tcp[tcpflags] & (tcp-syn) != 0) and tcp[20]=246 and tcp[24]=10 and tcp[25]=142'

//抓取 DNAT 包,tcp options 里面的 252 代表 DNAT
tcpdump -nn –vvv -i eth0 tcp dst port 3306 and '(tcp[tcpflags] & (tcp-ack) != 0) and (tcp[20] =252) '

//根据指定的VPC IP抓包,例如172.16.x.x
tcpdump -nn –vvv -i eth0 tcp dst port 3306 and '(tcp[tcpflags] & (tcp-ack) != 0) and (tcp[32] =172) and (tcp[33] =16)'

//根据客户端IP抓包FNAT的包,例如172.16.x.x
tcpdump -nn –vvv -i eth0 tcp dst port 3306 and '(tcp[tcpflags] & (tcp-ack) != 0) and(tcp[20]=252) and (tcp[24]=172) and (tcp[25]=16)'

用tcpdump抓取并保存包:
sudo tcpdump -i eth0 port 3306 -w plantegg.cap

抓到的包存储在plantegg.cap中,可以用作wireshark、tshark详细分析
如果明确知道目的ip、端口等可以通过指定条件来明确只抓取某个连接的包

只抓本机的8080端口:
tcpdump -i eth0 '(src port 8001 and src host 11.59.10.106) or (dst port 8001 and dst host 11.59.10.106)' -nn -X

//http 流量
// -f 抓取过滤条件 tcp port 80 and host 11.59.10.106
//-Y 展示过滤条件
tshark -i eth0 -f '(tcp src port 8080 and src host 11.59.10.106) or (tcp dst port 8080 and dst host 11.59.10.106)' -t a -Y " (http.request or http.response)" -T fields -e frame.number -e frame.time -e frame.time_delta_displayed -e ip.src -e tcp.srcport -e tcp.dstport -e ip.dst -e http.request.full_uri -e http.response.code -e http.response.phrase


抓取详细SQL语句:
sudo tshark -i eth0 -Y "mysql.command==3" -T fields -e mysql.query
sudo tshark -i eth0 -R mysql.query -T fields -e mysql.query

sudo tshark -i any -f 'port 8527' -s 0 -l -w - |strings

#parse 8507/4444 as mysql protocol, default only parse 3306 as mysql.
sudo tshark -i eth0 -d tcp.port==8507,mysql -T fields -e mysql.query 'port 8507'
sudo tshark -i any -c 50 -d tcp.port==4444,mysql -Y " ((tcp.port eq 4444 ) )" -o tcp.calculate_timestamps:true -T fields -e frame.number -e frame.time_epoch -e frame.time_delta_displayed -e ip.src -e tcp.srcport -e tcp.dstport -e ip.dst -e tcp.time_delta -e tcp.stream -e tcp.len -e mysql.query

sudo tshark -i eth0 -R "ip.addr==10.18.106.95" -d tcp.port==3306,mysql -T fields -e mysql.query 'port 3306'
sudo tshark -i eth0 -R "tcp.srcport==62877" -d tcp.port==3001,mysql -T fields -e tcp.srcport -e mysql.query 'port 3001'

sudo tshark -i br1.10 -Y tcp.port==4000,mysql -T fields -e tcp.srcport -e mysql.query 'port 4000'
tshark -i br1.10 -d tcp.port==4000,mysql -T fields -e tcp.srcport -e _ws.col.Info -e mysql.query

tshark -i eth0 -d tcp.port==4000,mysql -T fields -e tcp.srcport -e _ws.col.Info -e mysql.query

//将3307端口解析成MySQL 协议分析
tshark -i lo -d tcp.port==3307,mysql -T fields -e frame.number -e frame.time -e frame.time_delta -e tcp.srcport -e tcp.dstport -e tcp.len -e _ws.col.Info -e mysql.query

如果MySQL开启了SSL,那么抓包后的内容tshark/wireshark分析不到MySQL的具体内容,可以强制关闭:connectionProperties里加上useSSL=false

查看SQL具体内容
sudo tshark -r gege_plantegg.cap -Y "mysql.query or ( tcp.stream==1)" -o tcp.calculate_timestamps:true -T fields -e frame.number -e frame.time_epoch -e frame.time_delta_displayed -e ip.src -e tcp.srcport -e tcp.dstport -e ip.dst -e tcp.time_delta -e frame.time_delta_displayed -e tcp.stream -e tcp.len -e mysql.query


按mysql查询分析响应时间
对于rt分析,要注意一个query多个response情况(response结果多,分包了),分析这种rt的时候只看query之后的第一个response,其它连续response需要忽略掉。

以上抓包结果文件可以用tshark进行详细分析

对抓包按 stream 进行切分:
for i in {0..314};do tshark -r 11216253112_3055.pcap -Y "tcp.stream eq $i" -w $i.pcap; done
tshark -r 0.pcap "ip.src eq 11.216.253.112" -T fields -e frame.number -e frame.time_delta -e ip.src -e tcp.srcport -e tcp.dstport -e ip.dst -e tcp.time_delta -e tcp.stream -e tcp.len -e tcp.analysis.ack_rtt

分析MySQL rt,倒数第四列基本就是rt
tshark -r gege_plantegg.pcap -Y " ((tcp.srcport eq 3306 ) and tcp.len>0 )" -o tcp.calculate_timestamps:true -T fields -e frame.number -e frame.time_epoch -e frame.time_delta_displayed -e ip.src -e tcp.srcport -e tcp.dstport -e ip.dst -e tcp.time_delta -e tcp.stream -e tcp.len -e tcp.analysis.ack_rtt

或者排序一下
tshark -r 213_php.cap -Y "mysql.query or ( tcp.srcport==3306)" -o tcp.calculate_timestamps:true -T fields -e frame.number -e frame.time_epoch -e frame.time_delta_displayed -e ip.src -e tcp.srcport -e tcp.dstport -e ip.dst -e tcp.time_delta -e tcp.stream -e tcp.len -e mysql.query |sort -nk9 -nk1

MySQL响应时间直方图【第八列的含义-- Time since previous frame in this TCP stream: seconds】:
tshark -r gege_plantegg.pcap -Y "mysql.query or (tcp.srcport3306 and tcp.len>60)" -o tcp.calculate_timestamps:true -T fields -e frame.number -e frame.time_epoch -e frame.time_delta_displayed -e ip.src -e tcp.srcport -e tcp.dstport -e ip.dst -e tcp.time_delta -e tcp.stream -e tcp.len | awk 'BEGIN {sum0=0;sum3=0;sum10=0;sum30=0;sum50=0;sum100=0;sum300=0;sum500=0;sum1000=0;sumo=0;count=0;sum=0} {rt=$8; if(rt>=0.000) sum=sum+rt; count=count+1; if(rt<=0.000) sum0=sum0+1; else if(rt<0.003) sum3=sum3+1 ; else if(rt<0.01) sum10=sum10+1; else if(rt<0.03) sum30=sum30+1; else if(rt<0.05) sum50=sum50+1; else if(rt < 0.1) sum100=sum100+1; else if(rt < 0.3) sum300=sum300+1; else if(rt < 0.5) sum500=sum500+1; else if(rt < 1) sum1000=sum1000+1; else sum=sum+1 ;} END{printf "-------------\n3ms:\t%s \n10ms:\t%s \n30ms:\t%s \n50ms:\t%s \n100ms:\t%s \n300ms:\t%s \n500ms:\t%s \n1000ms:\t%s \n>1s:\t %s\n-------------\navg: %.6f \n" , sum3,sum10,sum30,sum50,sum100,sum300,sum500,sum1000,sumo,sum/count;}'

按http response分析响应时间
tshark -nr 213_php.cap -o tcp.calculate_timestamps:true -Y "http.request or http.response" -T fields -e frame.number -e frame.time_epoch -e frame.time_delta_displayed -e ip.src -e ip.dst -e tcp.stream -e http.request.full_uri -e http.response.code -e http.response.phrase | sort -nk6 -nk1

分析rtt、丢包、deplicate等等,可以得到整体网络状态
$ tshark -r retrans.cap -q -z io,stat,1,"AVG(tcp.analysis.ack_rtt)tcp.analysis.ack_rtt","COUNT(tcp.analysis.retransmission) tcp.analysis.retransmission","COUNT(tcp.analysis.fast_retransmission) tcp.analysis.fast_retransmission","COUNT(tcp.analysis.duplicate_ack) tcp.analysis.duplicate_ack","COUNT(tcp.analysis.lost_segment) tcp.analysis.lost_segment","MIN(tcp.window_size)tcp.window_size"

===================================================================================
| IO Statistics |
| |
| Duration: 89.892365 secs |
| Interval: 2 secs |
| |
| Col 1: AVG(tcp.analysis.ack_rtt)tcp.analysis.ack_rtt |
| 2: COUNT(tcp.analysis.retransmission) tcp.analysis.retransmission |
| 3: COUNT(tcp.analysis.fast_retransmission) tcp.analysis.fast_retransmission |
| 4: COUNT(tcp.analysis.duplicate_ack) tcp.analysis.duplicate_ack |
| 5: COUNT(tcp.analysis.lost_segment) tcp.analysis.lost_segment |
| 6: AVG(tcp.window_size)tcp.window_size |
|---------------------------------------------------------------------------------|
| |1 |2 |3 |4 |5 |6 | |
| Interval | AVG | COUNT | COUNT | COUNT | COUNT | AVG | |
|-------------------------------------------------------------| |
| 0 <> 2 | 0.001152 | 0 | 0 | 0 | 0 | 4206 | |
| 2 <> 4 | 0.002088 | 0 | 0 | 0 | 1 | 6931 | |
| 4 <> 6 | 0.001512 | 0 | 0 | 0 | 0 | 7099 | |
| 6 <> 8 | 0.002859 | 0 | 0 | 0 | 0 | 7171 | |
| 8 <> 10 | 0.001716 | 0 | 0 | 0 | 0 | 6472 | |
| 10 <> 12 | 0.000319 | 0 | 0 | 0 | 2 | 5575 | |
| 12 <> 14 | 0.002030 | 0 | 0 | 0 | 0 | 6922 | |
| 14 <> 16 | 0.003371 | 0 | 0 | 0 | 2 | 5884 | |
| 16 <> 18 | 0.000138 | 0 | 0 | 0 | 1 | 3480 | |
| 18 <> 20 | 0.000999 | 0 | 0 | 0 | 4 | 6665 | |
| 20 <> 22 | 0.000682 | 0 | 0 | 41 | 2 | 5484 | |
| 22 <> 24 | 0.002302 | 2 | 0 | 19 | 0 | 7127 | |
| 24 <> 26 | 0.000156 | 1 | 0 | 22 | 0 | 3042 | |
| 26 <> 28 | 0.000000 | 1 | 0 | 19 | 1 | 152 | |
| 28 <> 30 | 0.001498 | 1 | 0 | 24 | 0 | 5615 | |
| 30 <> 32 | 0.000235 | 0 | 0 | 44 | 0 | 1880 | |
1
===================================================================================
2
| IO Statistics |
3
| |
4
| Duration: 89.892365 secs |
5
| Interval: 2 secs |
6
| |
7
| Col 1: AVG(tcp.analysis.ack_rtt)tcp.analysis.ack_rtt |
8
| 2: COUNT(tcp.analysis.retransmission) tcp.analysis.retransmission |
9
| 3: COUNT(tcp.analysis.fast_retransmission) tcp.analysis.fast_retransmission |
10
| 4: COUNT(tcp.analysis.duplicate_ack) tcp.analysis.duplicate_ack |
11
| 5: COUNT(tcp.analysis.lost_segment) tcp.analysis.lost_segment |
12
| 6: AVG(tcp.window_size)tcp.window_size |
13
|---------------------------------------------------------------------------------|
14
| |1 |2 |3 |4 |5 |6 | |
15
| Interval | AVG | COUNT | COUNT | COUNT | COUNT | AVG | |
16
|-------------------------------------------------------------| |
17
| 0 <> 2 | 0.001152 | 0 | 0 | 0 | 0 | 4206 | |
18
| 2 <> 4 | 0.002088 | 0 | 0 | 0 | 1 | 6931 | |
19
| 4 <> 6 | 0.001512 | 0 | 0 | 0 | 0 | 7099 | |
20
| 6 <> 8 | 0.002859 | 0 | 0 | 0 | 0 | 7171 | |
21
| 8 <> 10 | 0.001716 | 0 | 0 | 0 | 0 | 6472 | |
22
| 10 <> 12 | 0.000319 | 0 | 0 | 0 | 2 | 5575 | |
23
| 12 <> 14 | 0.002030 | 0 | 0 | 0 | 0 | 6922 | |
24
| 14 <> 16 | 0.003371 | 0 | 0 | 0 | 2 | 5884 | |
25
| 16 <> 18 | 0.000138 | 0 | 0 | 0 | 1 | 3480 | |
26
| 18 <> 20 | 0.000999 | 0 | 0 | 0 | 4 | 6665 | |
27
| 20 <> 22 | 0.000682 | 0 | 0 | 41 | 2 | 5484 | |
28
| 22 <> 24 | 0.002302 | 2 | 0 | 19 | 0 | 7127 | |
29
| 24 <> 26 | 0.000156 | 1 | 0 | 22 | 0 | 3042 | |
30
| 26 <> 28 | 0.000000 | 1 | 0 | 19 | 1 | 152 | |
31
| 28 <> 30 | 0.001498 | 1 | 0 | 24 | 0 | 5615 | |
32
| 30 <> 32 | 0.000235 | 0 | 0 | 44 | 0 | 1880 | |


#tshark
tshark -r ./mysql-compress.cap -o tcp.calculate_timestamps:true -T fields -e mysql.caps.cp -e frame.number -e frame.time_epoch -e frame.time_delta_displayed -e ip.src -e tcp.srcport -e tcp.dstport -e ip.dst -e tcp.time_delta -e frame.time_delta_displayed -e tcp.stream -e tcp.len -e mysql.query

#用tcpdump抓取并保存包:
sudo tcpdump -i eth0 port 3306 -w plantegg.cap

#每隔3秒钟生成一个新文件,总共生成5个文件后(15秒后)终止抓包,然后包名也按时间规范好了
sudo tcpdump -t -s 0 tcp port 6379 -w 'dump_%Y-%m-%d_%H:%M:%S.pcap' -G 3 -W 5 -Z root

#每隔30分钟生成一个包并压缩,保留48个抓包,也就是24小的内的包
nohup sudo tcpdump -i eth0 -t -s 0 tcp and port 6379 -w 'dump_%Y-%m-%d_%H:%M:%S.pcap' -G 1800 -W 48 -Z root -z gzip &

#file size 512M 按文件大小不支持时间戳
nohup sudo tcpdump -i eth0 -t -s 0 tcp and port 3306 -w "dump_size.pcap" -C 1 -W 2 -Z root -z gzip &

#port range
sudo tcpdump -i eth0 -t -s 0 portrange 3000-3100 -w 'dump_%Y-%m-%d_%H:%M:%S.pcap' -G 60 -W 100 -Z root

#subnet
sudo tcpdump -i enp44s0f0 -t -s 0 net 192.168.0.1/28 -w 'dump_%Y-%m-%d_%H:%M:%S.pcap' -G 60 -W 100 -Z root

#抓取详细SQL语句, 快速确认client发过来的具体SQL内容:
sudo tshark -i any -f 'port 8527' -s 0 -l -w - |strings
sudo tshark -i eth0 -d tcp.port==3306,mysql -T fields -e mysql.query 'port 3306'
sudo tshark -i eth0 -R "ip.addr==11.163.182.137" -d tcp.port==3306,mysql -T fields -e mysql.query 'port 3306'
sudo tshark -i eth0 -R "tcp.srcport==62877" -d tcp.port==3001,mysql -T fields -e tcp.srcport -e mysql.query 'port 3001'

#query time
sudo tshark -i eth0 -Y " ((tcp.port eq 3306 ) and tcp.len>0 )" -o tcp.calculate_timestamps:true -T fields -e frame.number -e frame.time_epoch -e frame.time_delta_displayed -e ip.src -e tcp.srcport -e tcp.dstport -e ip.dst -e tcp.time_delta -e tcp.stream -e tcp.len -e mysql.query

#如果MySQL开启了SSL,那么抓包后的内容tshark/wireshark分析不到MySQL的具体内容,可以强制关闭:connectionProperties里加上useSSL=false

tshark -r ./manager.cap -o tcp.calculate_timestamps:true -Y " tcp.analysis.retransmission " -T fields -e tcp.stream -e frame.number -e frame.time -e ip.src -e tcp.srcport -e tcp.dstport -e ip.dst | sort

#MySQL响应时间直方图【第八列的含义-- Time since previous frame in this TCP stream: seconds】:
tshark -r gege_plantegg.pcap -Y "mysql.query or (tcp.srcport3306 and tcp.len>60)" -o tcp.calculate_timestamps:true -T fields -e frame.number -e frame.time_epoch -e frame.time_delta_displayed -e ip.src -e tcp.srcport -e tcp.dstport -e ip.dst -e tcp.time_delta -e tcp.stream -e tcp.len | awk 'BEGIN {sum0=0;sum3=0;sum10=0;sum30=0;sum50=0;sum100=0;sum300=0;sum500=0;sum1000=0;sumo=0;count=0;sum=0} {rt=$8; if(rt>=0.000) sum=sum+rt; count=count+1; if(rt<=0.000) sum0=sum0+1; else if(rt<0.003) sum3=sum3+1 ; else if(rt<0.01) sum10=sum10+1; else if(rt<0.03) sum30=sum30+1; else if(rt<0.05) sum50=sum50+1; else if(rt < 0.1) sum100=sum100+1; else if(rt < 0.3) sum300=sum300+1; else if(rt < 0.5) sum500=sum500+1; else if(rt < 1) sum1000=sum1000+1; else sum=sum+1 ;} END{printf "-------------\n3ms:\t%s \n10ms:\t%s \n30ms:\t%s \n50ms:\t%s \n100ms:\t%s \n300ms:\t%s \n500ms:\t%s \n1000ms:\t%s \n>1s:\t %s\n-------------\navg: %.6f \n" , sum3,sum10,sum30,sum50,sum100,sum300,sum500,sum1000,sumo,sum/count;}'

#分析MySQL rt,倒数第四列基本就是rt
tshark -r gege_plantegg.pcap -Y " ((tcp.srcport eq 3306 ) and tcp.len>0 )" -o tcp.calculate_timestamps:true -T fields -e frame.number -e frame.time_epoch -e frame.time_delta_displayed -e ip.src -e tcp.srcport -e tcp.dstport -e ip.dst -e tcp.time_delta -e tcp.stream -e tcp.len -e tcp.analysis.ack_rtt

#或者排序一下
tshark -r 213_php.cap -Y "mysql.query or ( tcp.srcport==3306)" -o tcp.calculate_timestamps:true -T fields -e frame.number -e frame.time_epoch -e frame.time_delta_displayed -e ip.src -e tcp.srcport -e tcp.dstport -e ip.dst -e tcp.time_delta -e tcp.stream -e tcp.len -e mysql.query |sort -nk9 -nk1

#将 tls key和抓包文件合并
editcap --inject-secrets tls,key.log in.pcap out.pcap
#把包长截掉,只保留前面54,可以脱敏包内容
editcap -s 54 old.pcap new.pcap

DNAT:

img

FNAT:

img

Apple M1 Pro 和 Intel I9-12900K到底谁强

主要比较 M1 Pro和 I9-12900K,从芯片的参数来分析他们的差异。不和M1Max、M1Ultra比是因为从成本看没有可比性,M1Max、M1Ultra应该比I9贵多了,比起来意义不大,M1Max、M1Ultra的场景不一样。结论在最后

网上很多拿I9-12900K和M1 Max比实际没有意义,CPU core方面M1 Max和M1 Pro是一样的(跑分结果一样),干嘛不挑个便宜的去比较!

The M1 Pro

The M1 Pro takes this higher, with:

  • 33.7 billion transistors on a 240mm squared die.
  • 8 performance cores, 24MB L2 Cache,每个core 3MB,cache跟不要钱一样的堆
  • 2 efficiency cores with 4MB L2 cache,每个core 2MB
  • 16 GPU Cores.
  • 32GB DDR5 memory at 200GB/s.

image-20220402101632476

从性能来看不推荐买M1,内存还是DDR4,M1Pro以上就都是DDR5了(文后有惊喜告诉你怎么用M1的价格买到M1 Pro)

image-20220402104020407

上图中PCPU就是高性能核,共8个,PCPU左边的是低频节能的2个ECPU,机器不忙的时候可以用ECPU,节能。一旦有复杂任务就可以用PCPU。至于M1 Max在狂堆 GPU, 然后M1 Ultra学习AMD把两块M1 Max封装在一起,有没有用就看你的应用场景了,比如搞程序编译、跑跑Idea用M1 Pro就够了,没必要多花几倍的钱用在GPU上,搞视频编辑、图片处理可以考虑Max、Ultra。

The M1 Max

The M1 Max provides:(相对M1 Pro主要是多堆了 16个GPU,CPU方面是一样的,大多数跑分是M1 Pro和Max几乎一样,多花钱买那16个GPU不一定值得)

  • 57 billion transistors on a 420mm squared die.
  • 8 performance cores, 24MB L2 Cache.
  • 2 efficiency cores with 4MB L2 cache.
  • 32 GPU Cores.
  • 64GB DDR5 memory at 400GB/s.

And the new M1 Ultra

The M1 Ultra brings you:(下面的数据完全是M1 Max的2倍,实际就是封装两块M1 Max)

  • 114 billion transistors on a 840mm squared die.
  • 16 performance cores, 48MB L2 Cache.
  • 4 efficiency cores with 4MB L2 cache.
  • 64 GPU Cores.
  • Up to 128GB DDR5 memory at 800GB/s.

M1 Pro主板拆解

image-20220506142049220

上图中,红框是 M1 Pro 芯片,黄框是三星 8GB 内存(共两块),绿框是铠侠的 128GB 闪存(共两块)。

Inel I9-12900K

对比下 i9-12900K,i9也有GPU只是没有说多少个,它的GPU频率在0.3到1.55GHz之间

alder lake die 2.png

ISA x86-64 (x86)
Microarchitecture Alder Lake, Golden Cove, Gracemont
Process Intel 7
Die 215.25 mm²” 20.5 mm × 10.5 mm
MCP No (1 dies)
Cores 16
Threads 24
l1$ size 0.75 MiB (768 KiB, 786,432 B, 7.324219e-4 GiB) + and 0.625 MiB (640 KiB, 655,360 B, 6.103516e-4 GiB) +
l1d$ size 0.25 MiB (256 KiB, 262,144 B, 2.441406e-4 GiB) + and 0.375 MiB (384 KiB, 393,216 B, 3.662109e-4 GiB) +
l1i$ size 0.5 MiB (512 KiB, 524,288 B, 4.882812e-4 GiB) + and 0.25 MiB (256 KiB, 262,144 B, 2.441406e-4 GiB) +
l2$ size 4 MiB (4,096 KiB, 4,194,304 B, 0.00391 GiB) + and 10 MiB (10,240 KiB, 10,485,760 B, 0.00977 GiB) + 共14Mb
l3$ size 6 MiB (6,144 KiB, 6,291,456 B, 0.00586 GiB) + and 24 MiB (24,576 KiB, 25,165,824 B, 0.0234 GiB) + 共30Mb
TDP 125 W

从下面的芯片分布图来看,绿色部分是8个高性能物理core,每个2 thread,绿色其右边的蓝色E Cores是8个低频节能core,没开超线程,所以24个threads就是2*8PCPU+8ECPU。真正打起仗来从蓝色部分的面积占比来看基本可以忽略,重点得靠绿色的PCPU。

img

性能比较

从上面分析来看 I9-12900K和M1 Pro的比较最终回到了各自8个PCPU的较量。Intel/X86的超线程在大部分场景下可以提升单核计算能力的1.5倍左右,所以这里就是Intel的12core打M1 Pro的,另外Intel主频也比M1 Pro要高,如果比较单core的计算能力Intel能睿频到5GHz以上,所以不考虑视频、图片、矩阵等简单计算场景,Intel的性能应该还是要强很多的。但是如果作为笔记本来说一定要考虑功耗,125W VS 45W,我的建议是买Apple(M1的软件兼容性也是个问题)。如果是当服务器工作站使用还是建议买I9. 价钱就不好比较了M1 Pro不单独卖没法估计价格。

I9弱在内存还是DDR4,而M1 Pro是DDR5了,另外就是M1 Pro的L2要大。当然I9也有DDR5的内存的。

笔记本领域M1整体来看应该优势明显,尤其是经过几年的生态发展能够把软件生态补上的话。

购买建议

如果想买苹果,推荐买这款:

image-20220402103153047

这种非标8核的M1(就是10核关闭了2核),便宜了2500,特别值。苹果从来没有发布过8核的M1 Pro芯片,但是这款售卖的CPU号称是M1 Pro,比正常的M1 Pro少了两个CPU core和两个GPU。这点差异是不会重新设计一个新的芯片多搞一条生产线的,一般是正常的M1 Pro生产线下来检测发现坏了个别的core,扔了太浪费,于是关掉坏core当低配的M1 Pro在卖,价钱便宜了快一半了,实际性能其实差得不多。

如果是买Intel i9的话,从性价比上来看如果能买到i5-12600K也是非常不错的,实际就是i9关掉(坏掉)了2个PCPU和4个ECPU,价钱是i9的一半不到,PCPU少了但是Base主频反而高了,因为总核少了,发热就能控制,所以单核能跑到的频率更高一些。

image 19

其实I9、I7、I5都是同一条生产线、同样的工艺下制造出来的,差别在于帮I9分摊成本,比如你看看i5-12600k的参数和i9-12900K基本是一样的,重点在215.25 mm² 的 Die Size:

ISA x86-64 (x86)
Microarchitecture Alder Lake, Golden Cove, Gracemont
Process Intel 7
Die 215.25 mm² 20.5 mm × 10.5 mm
Cores 10
Threads 16

即使把 i5-12600k拆开用放大镜看也是和i9-12900K 一样的:

img

总结

  • 笔记本建议买M1 Pro
  • M1和M1 Pro如果看重性能的话肯定要买M1 Pro了
  • M1 Pro 建议买8 core的,买到就是赚到
  • 集团内M1 Pro想要轻便就选14寸的,综合考虑我还是推荐14寸的
  • I9的笔记本建议买I7、I5,平时使用性能差得不多
  • 性能还是I9强,做服务器更合适

最后我手里头既没有I9也没有M1,结论靠键盘 :),买错了别找我。

参考资料

CPU的生产和概念

三个故事

故事一 无招胜有招

我有一个同事前是5Q(人人网的前身) 出来的,叫Z神,负责技术(所有解决不了的问题都找他),Z神从chinaren出道,跟着王兴一块创业做 5Q,5Q在学校靠鸡腿打下大片市场,最后被陈一舟的校内收购(据说被收购后5Q的好多技术都走了,最后王兴硬是呆在校内网把合约上的所有钱都拿到了)。

Z神让我最佩服的解决问题的能力,好多问题其实他也不一定就擅长,但是他就是有本事通过Help、Google不停地验证尝试就把一个不熟悉的问题给解决了,这是我最羡慕的能力,在后面的职业生涯中一直不停地往这个方面尝试。

应用刚启动连接到数据库的时候比较慢,但又不是慢查询

  1. Z神的解决办法是通过tcpdump来分析网络包,看网络包的时间戳和网络包的内容,然后找到了具体卡在了哪里。
  2. 如果是专业的DBA可能会通过show processlist 看具体连接在做什么,比如看到这些连接状态是 authentication 状态,然后再通过Google或者对这个状态的理解知道创建连接的时候MySQL需要反查IP、域名这里比较耗时,通过配置参数 skip-name-resolve 跳过去就好了。
  3. 如果是MySQL的老司机,一上来就知道连接慢的话跟 skip-name-resolve 关系最大。

在我眼里这三种方式都解决了问题,最后一种最快但是纯靠积累和经验,换个问题也许就不灵了;第一种方式是最牛逼和通用的,只需要最少的知识就把问题解决了。

我当时跟着Z神从sudo、ls等linux命令开始学起。当然我不会轻易去打搅他问他,每次碰到问题我尽量让他在我的电脑上来操作,解决后我再自己复盘,通过history调出他的所有操作记录,看他在我的电脑上用Google搜啥了,然后一个个去学习分析他每个动作,去想他为什么搜这个关键字,复盘完还有不懂的再到他面前跟他面对面的讨论他为什么要这么做,指导他这么做的知识和逻辑又是什么。

如果你学不会无招胜有招,那么history你总能学会吧!

这是当时的Z神用我的工作台(方方正正的显示器可见年代很久远了)

img

故事二 网络专家的机会

N年前我刚加入一家公司几个月,有一个客户购买了我们的产品上线后金额对不上(1类生产事故),于是经理带着我们几个技术去现场看看是什么原因,路上经理说你们不要有什么心理压力,我不懂技术但是我过去就是替你们挨骂的,我好好跪在客户那挨骂,你们好好安心解决问题。

问题大概就是客户有一段涉及交易的代码在事务中,但是提交到后端我们的服务上后钱对不上了,客户认为我们产品事务实现有问题。

到了现场客户不让下载他们代码,只能人肉趴在他们指定的机器上用眼睛看问题在哪里,看了三天自然是没找到为啥,大家非常沮丧地回来了,然后我们的产品被下线,客户直接把数据库换成了Oracle,换完后第一天没问题,我们是越发沮丧,大家都不敢提这个事情了,但是三天后一个振奋人心的消息传过来了:金额还是对不上 …… :))))))

于是我们再度派出技术人员帮他们看为什么(这次客户配合度高了很多),最后有个同事提了一嘴要不用 tcpdump 抓个包看看,到底应用代码有没有set autocommit=0, 半个小时后传来喜讯用户代码发出的就是autocommit=1,说明用户代码的事务配置没生效。

最后查出来配置文件中有中文注释,测试环境没有问题,但是生产环境机器不支持中文出现了乱码,中文注释后的配置文件没有被解析到,导致事务没有生效!

事情还没完,当我听到这个结果后恨不得实际抽自己,tcpdump咱也会用,怎么当时就没想到呢!于是后来我天天看tcpdump、分析网络包,有段时间最开心的是在酒店看书了。一个月后写了几篇文章放在公司内网,再然后公司内部各个团队开始拿着各种问题找过来,我的case也越来越多。

有一次产品调用是这样的 1->2->3->4->5->6 产品5是我们的,1说性能上不去,rt 是100太大,扯了两天皮,然后说5有问题,于是我到5上抓了个包,抓完包一分析,我心里有底了,明确告诉他们5的rt才2,压力还没有到5这里来,另外按照我抓包结果的rt分析,5的能力是20万,现在还不到1万,瓶颈在1-5之间,然后我上1/2/3/4用 netstat 分别看下网络状态发现1-2之间网络到了瓶颈(2回包给1的时候大量的包no ack),不要怀疑netstat真有这么强大,只是你不会看而已。如下图 2上的9108服务端口给1发回结果的时候1那边迟迟不给ack。其实这个case用好工具只是很小的一点,关键的是我能抓包分析出rt,然后从rt推断出系统的能力(别说全链路监控之类的,有时候还得拼刺刀),进而快速定位到瓶颈

image-20220611101850071

现在我们的产品文档必备一份tcpdump、tshark(wireshark命令行版本)救急命令箱,有时候让客户复制粘贴执行后给我们某个结果,好多问题不再是问题了

这个故事的结果是我成了公司的网络“专家”

故事三 Die是什么

2021年4月的时候,我们有个项目要在不同的硬件平台验收,那天傍晚7点正要回家的我被项目经理拽到了现场

系统性能不达标,现场都不知道为啥

我到现场看了下perf

img

然后处理了下,IPC从0.08提升到了0.22(IPC代表性能,越大越好),再细调下最终能到0.27,对应的业务测试QPS也是原来的4倍。

img

到这里谈不上任何故事性,我也很好奇为什么有这么好的效果,不信可以看这篇《十年后数据库还是不敢拥抱NUMA?》。

接下来的几天那个项目经理特批我拿他们的环境随便测试,于是我停下手头的工作,花了一周在这个环境做了很多验证和学习,并请教了公司CPU方面特别厉害的大佬,如下图(2021年我的水平就是这样,和所有程序员对CPU的了解一样,只是知道主频、核数,会看top)

img

大佬跟我说:两个Die的L3不互通。我就问了一句Die是啥意思,他回答一个晶圆。其实这时我还没有听懂,但是不好意思再问了– 这感觉你们平时都有吧,就是不在一个段位,差太远了,不好意思再问,到了该自己先去弄脏双手后再请教的时候了!

于是就Google各种概念、并收集各种资料和图,最后整理了一下(所以文章的连贯性其实不好),以个人笔记的形式存档下来了。

最后把这些笔记从多核、超线程、NUMA、睿频、功耗、GPU、大小核再到分支预测、cache_line失效、加锁代价、IPC等各种指标(都有对应的代码和测试数据)总结成了一系列文章。

image-20210802161410524

这个故事你觉得我想说啥,辛苦帮我在评论里总结下

其他想说的

看完故事升华一下方法论:如何在工作中学习

如果你觉得看完对你很有帮助可以通过如下方式找到我

find me on twitter: @plantegg

知识星球:https://t.zsxq.com/0cSFEUh2J

image-20230407232314969

开了一个星球,在里面讲解一些案例、知识、学习方法,肯定没法让大家称为顶尖程序员(我自己都不是),只是希望用我的方法、知识、经验、案例作为你的垫脚石,帮助你快速、早日成为一个基本合格的程序员。

争取在星球内:

  • 养成基本动手能力
  • 拥有起码的分析推理能力–按我接触的程序员,大多都是没有逻辑的
  • 知识上教会你几个关键的知识点

数据库计算向量化

前面我们通过一系列的CPU原理来学习了CPU的结构,以及怎么样让CPU跑得更快,那么我们有没有很好的案例来实战让CPU跑得更快呢。接下来我们通过数据库领域的向量化计算是如何利用CPU这些特性来让CPU更快地帮我们处理数据(SQL)

CPU的制造和概念

[Perf IPC以及CPU性能](/2021/05/16/Perf IPC以及CPU利用率/)

CPU性能和CACHE

[CPU 性能和Cache Line](/2021/05/16/CPU Cache Line 和性能/)

十年后数据库还是不敢拥抱NUMA?

[Intel PAUSE指令变化是如何影响自旋锁以及MySQL的性能的](/2019/12/16/Intel PAUSE指令变化是如何影响自旋锁以及MySQL的性能的/)

AMD Zen CPU 架构 以及 AMD、海光、Intel、鲲鹏的性能对比

Intel、海光、鲲鹏920、飞腾2500 CPU性能对比

一次海光物理机资源竞争压测的记录

飞腾ARM芯片(FT2500)的性能测试

在做向量化之前数据库一直用的是volcano模型来处理SQL

volcano火山模型

对于如下一条SQL, 数据库会将它解析成一颗树,这棵树每个节点就是一个operator(简单理解就是一个函数,进行一次计算处理)

1
2
3
4
SELECT pv.siteId, user.nickame
FROM pv JOIN user
ON pv.siteId = user.siteId AND pv.userId = user.id
WHERE pv.siteId = 123;

Relation Algebra

可以看到火山模型实现简单,只需要根据不同的计算提供一堆算子(operator)就可以了,然后根据不同的SQL只需要将operator进行组装(类似搭积木一样),就能得到一个递归调用结构(火山模型),每行数据按照这个调用逻辑经过每个operator进行嵌套处理就得到最终结果。

火山模型不但实现简单,框架结构性也非常好容易扩展。

但是火山模型效率不高:

  1. 每个operator拆分必须到最小粒度,导致嵌套调用过多过深;
  2. 嵌套都是虚函数无法内联;
  3. 这个处理逻辑整体对CPU流水线不友好,CPU希望你不停地给我数据我按一个固定的逻辑(流程)来处理,而不是在不同的算子中间跳来跳去。

向量化加速的CPU原理

向量化加速的CPU原理:

如下图,表示的是for循环每次跳K个int,在K小于16的时候虽然循环次数逐渐减少到原来的1/16, 但是总时间没变,因为一直是访问的同一个cache里面的数据。 到16个之后就会产生突变(跨了cache_line),再后面32、64、128的时间减少来源于循环次数的减少,因为如论如何每次循环都需要访问内存加载数据到cache_line中.

Cache_line大小是64,正好16个int,也就是存取1个或者16个int的代价基本是一样的。

1
for (int i = 0; i < arr.Length; i += K) arr[i] *= 3;

running times of this loop for different step values (/images/951413iMgBlog/image6.png)

另外 一个大家耳熟能详的案例是对一个二维数组逐行遍历逐列遍历的时间差异,循环次数一样,但是因为二维数组按行保存,所以逐行遍历对cache line 更友好,最终按行访问效率更高:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const int row = 1024;
const int col = 512
int matrix[row][col];
//逐行遍历耗时0.081ms
int sum_row=0;
for(int _r=0; _r<row; _r++) {
for(int _c=0; _c<col; _c++){
sum_row += matrix[_r][_c];
}
}
//逐列遍历耗时1.069ms
int sum_col=0;
for(int _c=0; _c<col; _c++) {
for(int _r=0; _r<row; _r++){
sum_col += matrix[_r][_c];
}
}

了解了以上CPU运算的原理我们再来看向量化就很简单了

向量化

向量化执行的思想就是不再像火山模型一样调用一个算子一次处理一行数据,而是一次处理一批数据来均摊开销:这个开销很明显会因为一次处理一个数据没用利用好cache_line以及局部性原理,导致CPU在切换算子的时候要stall在取数据上,表现出来的结果就是IPC很低,cache miss、branch prediction失败都会增加。

举例来说,对于一个实现两个 int 相加的 expression,在向量化之前,其实现可能是这样的:

1
2
3
4
5
6
7
class ExpressionIntAdd extends Expression {
Datum eval(Row input) {
int left = input.getInt(leftIndex);
int right = input.getInt(rightIndex);
return new Datum(left+right);
}
}

在向量化之后,其实现可能会变为这样:

1
2
3
4
5
6
7
8
9
10
class VectorExpressionIntAdd extends VectorExpression {
int[] eval(int[] left, int[] right) {
int[] ret = new int[input.length];
for(int i = 0; i < input.length; i++) {
//利用cache局部性原理一次取多个数据和取一个代价一样
ret[i] = new Datum(left[i] + right[i]);
}
return ret;
}
}

很明显对比向量化之前的版本,向量化之后的版本不再是每次只处理一条数据,而是每次能处理一批数据,而且这种向量化的计算模式在计算过程中也具有更好的数据局部性。

向量化–Vector、批量化(一次处理一批数据)。向量化核心是利用数据局部性原理,一次取一个和取一批的时延基本是同样的。volcanno模型每次都是取一个处理一个,跳转到别的算子;而向量化是取一批处理一批后再跳转。整个过程中最耗时是取数据(访问内存比CPU计算慢两个数量级)

如果把向量化计算改成批量化处理应该就好理解多了,但是low,向量化多玄乎啊

为了支持这种批量处理数据的需求,CPU设计厂家又搞出了SIMD这种大杀器

SIMD (Single Instruction Multiple Data,单指令多数据)

SIMD指令的作用是向量化执行(Vectorized Execution),中文通常翻译成向量化,但是这个词并不是很好,更好的翻译是数组化执行,表示一次指令操作数组中的多个数据,而不是一次处理一个数据;向量则代表有数值和方向,显然在这里的意义用数组更能准确的表达。

在操作SIMD指令时,一次性把多条数据从内存加载到宽寄存器中,通过一条并行指令同时完成多条数据的计算。例如一个操作32字节(256位)的指令,可以同时操作8个int类型,获得8倍的加速。同时利用SIMD减少循环次数,大大减少了循环跳转指令,也能获得加速。SIMD指令可以有0个参数、1个数组参数、2个数组参数。如果有一个数组参数,指令计算完数组中的每个元素后,分别把结果写入对应位置;如果是有两个参数,则两个参数对应的位置分别完成指定操作,写入到对应位置。

image-20220627165706516

如上图所示:SIMD指令同时操作A和B中4对数字,产生4个结果存放到C中

以如下代码为例,对4个float计算平方:

1
2
3
4
5
6
7
8
void squre( float* ptr )
{
for( int i = 0; i < 4; i++ )
{
const float f = ptr[ i ];
ptr[ i ] = f * f;
}
}

上述代码转写成SIMD指令,则可以删除循环,用三条指令即可完成计算,分别是加载到寄存器,计算平方,结果写回内存:

1
2
3
4
5
6
7
void squre(float * ptr)
{
__m128 f = _mm_loadu_ps( ptr );
f = _mm_mul_ps( f, f );
_mm_storeu_ps( ptr, f );
}

简单理解SIMD就是相对于之前一个指令(一般是一个时钟周期)操作一个数据,但现在有了SIMD就可以在一个时钟周期操作一批数据,这个批如果是64,那么性能就提升了64倍。

英特尔在1996年率先引入了MMX(Multi Media eXtensions)多媒体扩展指令集,也开创了SIMD(Single Instruction Multiple Data,单指令多数据)指令集之先河,即在一个周期内一个指令可以完成多个数据操作,MMX指令集的出现让当时的MMX Pentium处理器大出风头。

SSE(Streaming SIMD Extensions,流式单指令多数据扩展)指令集是1999年英特尔在Pentium III处理器中率先推出的,并将矢量处理能力从64位扩展到了128位。

AVX 所代表的单指令多数据(Single Instruction Multi Data,SIMD)指令集,是近年来 CPU 提升 IPC(每时钟周期指令数)上为数不多的重要革新。随着每次数据宽度的提升,CPU 的性能都会大幅提升,但同时晶体管数量和能耗也会有相应的提升。因此在对功耗有较高要求的场景,如笔记本电脑或服务器中,CPU 运行 AVX 应用时需要降低频率从而降低功耗。

向量化当然也非常希望利用SIMD(跟GPU为什么挖矿比CPU快是一样的道理)

这里可以参考为什么这20年CPU主频基本都在2G-3G附近不再提升但是性能仍然遵循摩尔定律在提升。

如何生成SIMD指令呢?

有几种方式:

  1. 编译器自动向量化:
    • 静态编译(代码满足一定的范;编译选项 -O3 or -mavx2 -march=native -ftree-vectorize)
    • 即时编译(JIT)
  2. 可以手写SIMD指令,比如JDK17 开始提供Vector API,也就是应用Java 代码中可以通过这个API 直接调用 SIMD 指令

向量化的代码要求

  • 循环次数可计算
  • 简单计算,不包含函数调用、switch/if/return 等
  • 在循环在内层
  • 访问连续的内存空间(才可以通过simd指令从内存加载数据到寄存器)
  • 数据无依赖
  • 使用数组而不是指针

向量化的问题

向量化的前提是L3 cache够用,在L3不够用的时候,向量化的收益是负的,国内大部分文章都是为了PR而讲向量化。并发稍微高点,向量化立马就没足够的加速效果了。L2的一次miss就足够让向量化收益清零了,都轮不到 L3 Miss。

比如 avx512,向量化基本是用8倍的带宽,换取2-3倍的延迟,还要降频(指令复杂了)。所以 skylake 开始,intel砍了L3,加了L2。

大部分向量化引擎的收益是来自向量化后被迫做了列存(或者说列存做向量化更加简单,所以大家工程上会选择向量化),这天然带来了数据密度更高,不是向量化导致了性能好。

SIMD 的代码对流水线要求很高的,如何写出流水线层面不stall的代码很难,主要问题是大部分SIMD都不是编译器生成的,需要开发者自己去做指令的调度,但是大部分开发者并没有微架构的知识,所以这玩意很难写好。

SIMD 适合解决计算瓶颈的问题,而不是数据库的内存瓶颈。计算瓶颈和内存瓶颈是完全的2个概念,只是大部分时候,我们会把内存瓶颈和计算瓶颈合起来叫做 CPU 瓶颈,但是db 90%以上场景,确实是内存而不是计算瓶颈…尤其是AP领域对同一份数据多次重复运算的, 那才叫做计算瓶颈。

向量化的本质不是 SIMD,是内存密度,SIMD 从头到尾就是一个骗局,用来PR的。

向量化最成功的Case 是字符大小写转换(可惜这个场景不多),有几十倍的性能提升,因为原来一个个字符处理,现在如果128 的SIMD 指令一次可以出来 16个 Char,性能简单理解就是能提升16倍

参考资料

CPU的制造和概念

[Perf IPC以及CPU性能](/2021/05/16/Perf IPC以及CPU利用率/)

CPU性能和CACHE

[CPU 性能和Cache Line](/2021/05/16/CPU Cache Line 和性能/)

十年后数据库还是不敢拥抱NUMA?

[Intel PAUSE指令变化是如何影响自旋锁以及MySQL的性能的](/2019/12/16/Intel PAUSE指令变化是如何影响自旋锁以及MySQL的性能的/)

AMD Zen CPU 架构 以及 AMD、海光、Intel、鲲鹏的性能对比

Intel、海光、鲲鹏920、飞腾2500 CPU性能对比

一次海光物理机资源竞争压测的记录

飞腾ARM芯片(FT2500)的性能测试

AMD Zen CPU 架构

前言

本文先介绍AMD Zen 架构,结合前一篇文章《CPU的生产和概念》一起来看效果会更好,在CPU的生产和概念中主要是以Intel方案来介绍,CPU的生产和概念中的 多核和多个CPU方案2 就是指的AMD Zen2架构。

Zen1 和 Intel 还比较像,只是一个CPU会封装多个小的Die来得到多核能力,导致NUMA node比较多。

AMD 从Zen2开始架构有了比较大的变化,Zen2架构改动比较大,将IO从Core Die中抽离出来,形成一个专门的IO Die,这个IO Die可以用上一代的工艺实现来提升成品率降低成本。剩下的core Die 专注在core和cache的实现上,同时可以通过最新一代的工艺来提升性能。并且在一个CPU上封装一个 IO Die + 8个 core Die这样一块CPU做到像Intel一样就是一个大NUMA,但是成本低了很多,也许在云计算时代这么搞比较合适。当然会被大家笑话为胶水核(用胶水把多个Die拼在一起),性能肯定是不如一个大Die好,但是挡不住便宜啊。这估计就是大家所说的 **AMD YES!**吧

比如Core Die用7nm工艺,IO Die用14nm工艺,一块CPU封装8个Core Die+1个IO Die的话既能得到一个多核的CPU成本有非常低,参考 《CPU的生产和概念》中的良品率和成品部分。

介绍完AMD架构后,会拿海光7280这块CPU(实际是OEM的AMD Zen1 架构,一块芯片封装4个die)和 Intel的CPU用MySQL 来对比一下实际性能。

网上Intel CPU架构、技术参数等各种资料还是很丰富的,但是AMD EPYC就比较少了,所以先来学习一下EPYC的架构特点。

image-20220331120118117

AMD EPYC CPU演进路线

img

后面会针对 第二代的 EPYC来做一个对比测试。

AMD Accelerated Computing FAD 2020

AMD EPYC CPU Families:

Family Name AMD EPYC Naples AMD EPYC Rome AMD EPYC Milan AMD EPYC Genoa
Family Branding EPYC 7001 EPYC 7002 EPYC 7003 EPYC 7004?
Family Launch 2017 2019 2021 2022
CPU Architecture Zen 1 Zen 2 Zen 3 Zen 4
Process Node 14nm GloFo 7nm TSMC 7nm TSMC 5nm TSMC
Platform Name SP3 SP3 SP3 SP5
Socket LGA 4094 LGA 4094 LGA 4094 LGA 6096
Max Core Count 32 64 64 96
Max Thread Count 64 128 128 192
Max L3 Cache 64 MB 256 MB 256 MB 384 MB?
Chiplet Design 4 CCD’s (2 CCX’s per CCD),4 Die 8 CCD’s (2 CCX’s per CCD) + 1 IOD ,9 Die 8 CCD’s (1 CCX per CCD) + 1 IOD 12 CCD’s (1 CCX per CCD) + 1 IOD
Memory Support DDR4-2666 DDR4-3200 DDR4-3200 DDR5-5200
Memory Channels 8 Channel 8 Channel 8 Channel 12 Channel
PCIe Gen Support 64 Gen 3 128 Gen 4 128 Gen 4 128 Gen 5
TDP Range 200W 280W 280W 320W (cTDP 400W)

命名规范:

image-20220721174306194

Zen1

hygon 5280封装后类似下图(一块CPU封装了2个Die,还有封装4个Die的,core更多更贵而已)

image-20210812204437220

或者4个Die封装在一起

image-20210813085044786

Zen1 Die

下面这块Die集成了两个CCX(每个CCX四个物理core), 同时还有IO接口

Блоки CCX

img

Quad-Zeppelin Configuration, as found in EPYC.

img

Zen CPU Complex(CCX)

hygon 5280使用这个结构, There are 4 cores per CCX and 2 CCXs per die for 8 cores.

  • 44 mm² area
  • L3 8 MiB; 16 mm²
  • 1,400,000,000 transistors

amd zen ccx.png

amd zen ccx 2

封装后的Zen1(4Die)

image-20210813085044786

4个Die的内部关系

AMD Naples SoC.svg

详实数据和结构

Топология процессора

Zen2 Rome

Zen2开始最大的变化就是将IO从Core Die中抽离出来,形成一个专门的IO Die。封装后如下图:

AMD Rome package with card

以上结构的CPU在2路服务器下的内部结构:

img

跨socket的内存访问的数据流跟互联有关,如上图标示,比如从左边的CCD0到右边的CCD0的内存,大概需要经过10跳。

node0 node1 node2 node3 node4 node5 node6 node7
node0 89.67 99.357 108.11 110.54 181.85 187.71 179.507 179.463
node1 90.983 111.65 106.11 188.77 194.7 188.179 189.512
node2 91.2 98.272 180.95 190.53 184.865 186.088
node3 89.971 186.81 193.43 192.459 192.615
node4 89.566 97.943 108.19 109.942
node5 90.927 111.123 108.046
node6 91.212 103.719
node7 89.692

上面表格是3 xGMI互联的情况下,测试出来的访存时延,可以看到在某些node间访存时延会有一些的突增,不够均匀,比如node1到node 5、node2到node5;上述latency跨socket如果用默认BIOS值在280左右

以下表格是厂商默认值和优化值对比(用优化值能将latency从280下降到180左右):

参数 可选项 默认值 (milan:V260 rome:V26.02) 优化值 备注
xGMI Link Width Control Manual/Auto Auto Manual
xGMI Force Link Width Control Unforce/Force Unforce Force
xGMI Force Link Width 0/1/2 2 2 2 = Force xGMI link width to x16
3-link xGMI max speed [00]6.4Gbps …… [0A]16Gbps ……[13]25Gbps *[FF]Auto Auto 16Gbps IEC的rome和milan都是16Gbs,其他产品要与硬件确认

另外发现启用透明大页后测试内存时延能降低20%(通过perf发现没开THP的tlb miss很高)

AMD Rome layout

img

Zen2 Core Complex Die

  • TSMC 7-nanometer process
  • 13 metal layers[1]
  • 3,800,000,000 transistors[2]
  • Die size: 74 mm²
  • CCX size: 31.3 mm², 4core per CCX // 16M L3 perf CCX
  • 2 × 16 MiB L3 cache: 2 × 16.8 mm² (estimated) // 中间蓝色部分是L3 16M,一个Die封装两个CCX的情况下

AMD Zen 2 CCD.jpg

img

在Zen2/Rome架构中,一个CCD由两个CCX构成,一个CCX包含4个物理核,共享16MB的L3 cache。

Zen3

img

在Zen3/Milan架构中,抛弃了两个CCX组成一个CCD的概念,一个CCD直接由8个物理核构成,共享整个Die上的32MB L3 cache。

再就是可以选择增加 v-cache,3D封装更大的L3 cache,如下图,一个CCD 默认是32M L3,但是 v-cache 可以增加一块 64 MB的L3进去(TSMC的SOIC封装在一起),这块 L3 Die 可以单独生产

image-20220923162521398

AMD 3D V-Cache

img

img

Milan-X芯片面积及定价策略

TDP (W) Cores Base Freq (GHz) Max. Freq (GHz) L3(MB) Channels DDR Max DDR Freq PCIeLane
7763 280 64 2.45 3.5 256 8 3200 x128
7773X 280 64 2.2 3.5 768 8 3200 x128

比如上表中 7773X 相对 7763 封装了更大的L3,同时降低了主频来控制发热

下表为标品的芯片面积和售价数据,对比可以看出,扩容2倍L3的芯片整体硅面积增加了31%,售价提升了12%

area mm^2 price 1KU($)
7763 IOD 416+CCD 81*8=1064 7890
7773x +add L3D 41*8=1392 8800

AMD PPOG文档中摘录的关于CPU的micro-bench相关的数据:

1,访存时延上, Vcache普遍有2~6ns的延迟优化;访存带宽上二者基本一致;

2,spec CPU上,整形跑分基本持平,vcache的容量增加部分被主频的降低抵消;浮点跑分提升10%,mem-intensive类型的HPC/AI类应用,将得到比较明显的提升;

3,spec JBB上,vcache的改善明显,critical和max jOPS均得到了10%以上的提升;

Workloads 7763 7773X vcache
NPS4 Core0 Node0 (ns) 85 83
NPS4 Core0 Node1 (ns) 97 92
NPS4 Core0 Node2 (ns) 106 100
NPS4 Core0 Node3 (ns) 109 104
STREAM Add (GBps) 100% 99.9%
STREAM Copy(GBps) 100% 99.9%
STREAM Scale(GBps) 100% 100.1%
STREAM Triad(GBps) 100% 99.8%
SPEC CPU2017 FP Rate Base 100% 109.8%
SPEC CPU2017 Int Rate Base 100% 100.9%
SPECjbb2015-MultiJVM Critical-Jops 100% 111.6%
SPECjbb2015-MultiJVM Max-jOPS 100% 116.7%

Zen1 VS Zen2

Here is what the Naples and Rome packages look like from the outside:

img

numa

image-20210813091455662

zen1 numa distance:

img

hygon numa distance:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# numactl -H  //Zen1 hygon 7280  2 socket enable die interleaving
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
node 0 size: 257578 MB
node 0 free: 115387 MB
node 1 cpus: 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
node 1 size: 257005 MB
node 1 free: 221031 MB
node distances:
node 0 1
0: 10 22
1: 22 10

#numactl -H //Zen1 hygon 5280 2 socket disable die interleaving
available: 4 nodes (0-3)
node 0 cpus: 0 1 2 3 4 5 6 7 32 33 34 35 36 37 38 39
node 0 size: 128854 MB
node 0 free: 89350 MB
node 1 cpus: 8 9 10 11 12 13 14 15 40 41 42 43 44 45 46 47
node 1 size: 129019 MB
node 1 free: 89326 MB
node 2 cpus: 16 17 18 19 20 21 22 23 48 49 50 51 52 53 54 55
node 2 size: 128965 MB
node 2 free: 86542 MB
node 3 cpus: 24 25 26 27 28 29 30 31 56 57 58 59 60 61 62 63
node 3 size: 129020 MB
node 3 free: 98227 MB
node distances:
node 0 1 2 3
0: 10 16 28 22
1: 16 10 22 28
2: 28 22 10 16
3: 22 28 16 10

看完这些结构上的原理,让我们实际来看看AMD的性能怎么样。

hygon 7280 PCM数据

hygon pcm(performance counter monitor) 工具由芯片公司提供

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
[root@hygon3 16:58 /root/PCM]
#./pcm.x -r -topdown -i=1 -nc -ns -l2

Processor Counter Monitor (2019-08-21 17:07:31 +0800 ID=378f2fc)

Number of physical cores: 64
Number of logical cores: 128
Number of online logical cores: 128
Threads (logical cores) per physical core: 2
Num sockets: 2
Physical cores per socket: 32
Core PMU (perfmon) version: 3
Number of core PMU generic (programmable) counters: 6
Width of generic (programmable) counters: 64 bits
Ccxs per Node: 8
Logical cores per Ccx: 8
Physical Cores per Ccx: 4
Nodes per socket: 4
Number of core PMU fixed counters: 0
Width of fixed counters: 0 bits
Nominal core frequency: 2000000000 Hz
Package thermal spec power: -1 Watt; Package minimum power: -1 Watt; Package maximum power: -1 Watt;

Resetting PMU configuration
Zeroed PMU registers

Detected Hygon C86 7280 32-core Processor "Hygon(r) microarchitecture codename DHYANA" stepping 1

EXEC : instructions per nominal CPU cycle
IPC : instructions per CPU cycle
FREQ : relation to nominal CPU frequency='unhalted clock ticks'/'invariant timer ticks' (includes Intel Turbo Boost)
AFREQ : relation to nominal CPU frequency while in active state (not in power-saving C state)='unhalted clock ticks'/'invariant timer ticks while in C0-state' (includes Intel Turbo Boost)
L3MISS: L3 (read) cache misses
L3MPKI: L3 misses per kilo instructions
L3HIT : L3 (read) cache hit ratio (0.00-1.00)
L2DMISS:L2 data cache misses
L2DHIT :L2 data cache hit ratio (0.00-1.00)
L2DMPKI:number of L2 data cache misses per kilo instruction
L2IMISS:L2 instruction cache misses
L2IHIT :L2 instructoon cache hit ratio (0.00-1.00)
L2IMPKI:number of L2 instruction cache misses per kilo instruction
L2MPKI :number of both L2 instruction and data cache misses per kilo instruction

Core (SKT) | EXEC | IPC | FREQ | AFREQ | L2DMISS| L2DHIT | L2DMPKI| L2IMISS| L2IHIT | L2IMPKI| L2MPKI | L3MISS | L3MPKI | L3HIT | TEMP

---------------------------------------------------------------------------------------------------------------
TOTAL * 1.29 1.20 1.08 1.00 12 M 0.73 0.04 10 M 0.87 0.03 0.07 19 M 0.00 0.55 N/A

Instructions retired: 336 G ; Active cycles: 281 G ; Time (TSC): 2082 Mticks ; C0 (active,non-halted) core residency: 107.90 %


PHYSICAL CORE IPC : 2.39 => corresponds to 34.14 % utilization for cores in active state
Instructions per nominal CPU cycle: 2.58 => corresponds to 36.84 % core utilization over time interval
---------------------------------------------------------------------------------------------------------------

Cleaning up
Zeroed PMU registers

在本地启动benchmarksql压力,并将进程绑定到0-8core,然后采集到数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#./pcm.x -r -topdown -i=1 -l2

Processor Counter Monitor (2019-08-21 17:07:31 +0800 ID=378f2fc)

Number of physical cores: 64
Number of logical cores: 128
Number of online logical cores: 128
Threads (logical cores) per physical core: 2
Num sockets: 2
Physical cores per socket: 32
Core PMU (perfmon) version: 3
Number of core PMU generic (programmable) counters: 6
Width of generic (programmable) counters: 64 bits
Ccxs per Node: 8
Logical cores per Ccx: 8
Physical Cores per Ccx: 4
Nodes per socket: 4
Number of core PMU fixed counters: 0
Width of fixed counters: 0 bits
Nominal core frequency: 2000000000 Hz
Package thermal spec power: -1 Watt; Package minimum power: -1 Watt; Package maximum power: -1 Watt;

Resetting PMU configuration
Zeroed PMU registers

Detected Hygon C86 7280 32-core Processor "Hygon(r) microarchitecture codename DHYANA" stepping 1

EXEC : instructions per nominal CPU cycle
IPC : instructions per CPU cycle
FREQ : relation to nominal CPU frequency='unhalted clock ticks'/'invariant timer ticks' (includes Intel Turbo Boost)
AFREQ : relation to nominal CPU frequency while in active state (not in power-saving C state)='unhalted clock ticks'/'invariant timer ticks while in C0-state' (includes Intel Turbo Boost)
L3MISS: L3 (read) cache misses
L3MPKI: L3 misses per kilo instructions
L3HIT : L3 (read) cache hit ratio (0.00-1.00)
L2DMISS:L2 data cache misses
L2DHIT :L2 data cache hit ratio (0.00-1.00)
L2DMPKI:number of L2 data cache misses per kilo instruction
L2IMISS:L2 instruction cache misses
L2IHIT :L2 instructoon cache hit ratio (0.00-1.00)
L2IMPKI:number of L2 instruction cache misses per kilo instruction
L2MPKI :number of both L2 instruction and data cache misses per kilo instruction

Core (SKT) | EXEC | IPC | FREQ | AFREQ | L2DMISS| L2DHIT | L2DMPKI| L2IMISS| L2IHIT | L2IMPKI| L2MPKI | L3MISS | L3MPKI | L3HIT | TEMP

0 0 1.34 1.26 1.06 1.00 8901 K 0.72 3.15 15 M 0.68 5.43 8.58 71 M 4.00 0.60 N/A
1 0 1.42 1.33 1.06 1.00 8491 K 0.73 2.83 14 M 0.68 4.67 7.50 71 M 4.00 0.60 N/A
2 0 1.41 1.33 1.06 1.00 8206 K 0.74 2.75 12 M 0.72 4.25 7.00 71 M 4.00 0.60 N/A
3 0 1.46 1.38 1.06 1.00 7464 K 0.75 2.40 11 M 0.68 3.81 6.21 71 M 4.00 0.60 N/A
4 0 1.31 1.24 1.06 1.00 9118 K 0.71 3.28 15 M 0.69 5.61 8.88 70 M 4.00 0.61 N/A
5 0 1.41 1.33 1.06 1.00 8700 K 0.74 2.92 13 M 0.69 4.66 7.57 70 M 4.00 0.61 N/A
6 0 1.41 1.33 1.06 1.00 8094 K 0.74 2.79 12 M 0.70 4.40 7.18 70 M 4.00 0.61 N/A
7 0 1.43 1.35 1.06 1.00 7873 K 0.74 2.68 12 M 0.71 4.13 6.81 70 M 4.00 0.61 N/A
8 0 1.44 1.36 1.06 1.00 8544 K 0.73 2.79 14 M 0.67 4.87 7.66 20 M 1.00 0.61 N/A
9 0 1.24 1.16 1.06 1.00 524 K 0.51 0.21 86 K 0.94 0.03 0.24 20 M 1.00 0.61 N/A
10 0 1.26 1.18 1.07 1.00 379 K 0.50 0.15 60 K 0.95 0.02 0.17 20 M 1.00 0.61 N/A
11 0 1.24 1.16 1.07 1.00 533 K 0.50 0.20 96 K 0.94 0.04 0.24 20 M 1.00 0.61 N/A
12 0 1.22 1.14 1.07 1.00 1180 K 0.34 0.47 98 K 0.94 0.04 0.51 3872 K 0.12 0.46 N/A
13 0 1.24 1.16 1.07 1.00 409 K 0.49 0.16 64 K 0.94 0.03 0.19 3872 K 0.12 0.46 N/A

---------------------------------------------------------------------------------------------------------------
SKT 0 1.18 1.11 1.06 1.00 113 M 0.67 0.73 139 M 0.71 0.89 1.62 186 M 1.12 0.59 N/A
SKT 1 1.23 1.14 1.08 1.00 33 M 0.53 0.21 11 M 0.89 0.07 0.28 38 M 0.12 0.45 N/A
---------------------------------------------------------------------------------------------------------------
TOTAL * 1.21 1.13 1.07 1.00 147 M 0.65 0.46 150 M 0.74 0.47 0.93 224 M 0.62 0.57 N/A

Instructions retired: 319 G ; Active cycles: 283 G ; Time (TSC): 2108 Mticks ; C0 (active,non-halted) core residency: 107.12 %


PHYSICAL CORE IPC : 2.25 => corresponds to 32.18 % utilization for cores in active state
Instructions per nominal CPU cycle: 2.41 => corresponds to 34.48 % core utilization over time interval
---------------------------------------------------------------------------------------------------------------

Cleaning up
Zeroed PMU registers

Apple M1

M1, M1 Pro, and M1 Max chips are shown next to each other.

The M1

The critically-acclaimed M1 processor delivers:

  • 16 billion transistors and a 119mm squared-die size.
  • 4 performance cores, 12MB L2 Cache.
  • 4 efficiency cores ith 4MB L2 cache.
  • 8 GPU Cores.
  • 16GB DDR4x memory at 68GB/s.

The M1 Pro

The M1 Pro takes this higher, with:

  • 33.7 billion transistors on a 240mm squared die.
  • 8 performance cores, 24MB L2 Cache.
  • 2 efficiency cores with 4MB L2 cache.
  • 16 GPU Cores.
  • 32GB DDR5 memory at 200GB/s.

对比下 i9-12000,i9也有GPU只是没有说多少个,它的GPU频率在0.3到1.55GHz之间

alder lake die 2.png

ISA x86-64 (x86)
Microarchitecture Alder Lake, Golden Cove, Gracemont
Process Intel 7
Die 215.25 mm²” 20.5 mm × 10.5 mm
MCP No (1 dies)
Cores 16
Threads 24
l1$ size 0.75 MiB (768 KiB, 786,432 B, 7.324219e-4 GiB) + and 0.625 MiB (640 KiB, 655,360 B, 6.103516e-4 GiB) +
l1d$ size 0.25 MiB (256 KiB, 262,144 B, 2.441406e-4 GiB) + and 0.375 MiB (384 KiB, 393,216 B, 3.662109e-4 GiB) +
l1i$ size 0.5 MiB (512 KiB, 524,288 B, 4.882812e-4 GiB) + and 0.25 MiB (256 KiB, 262,144 B, 2.441406e-4 GiB) +
l2$ size 4 MiB (4,096 KiB, 4,194,304 B, 0.00391 GiB) + and 10 MiB (10,240 KiB, 10,485,760 B, 0.00977 GiB) +
l3$ size 6 MiB (6,144 KiB, 6,291,456 B, 0.00586 GiB) + and 24 MiB (24,576 KiB, 25,165,824 B, 0.0234 GiB) +

The M1 Max

The M1 Max provides:

  • 57 billion transistors on a 420mm squared die.
  • 8 performance cores, 24MB L2 Cache.
  • 2 efficiency cores with 4MB L2 cache.
  • 32 GPU Cores.
  • 64GB DDR5 memory at 400GB/s.

And the new M1 Ultra

The M1 Ultra brings you:

  • 114 billion transistors on a 840mm squared die.
  • 16 performance cores, 48MB L2 Cache.
  • 4 efficiency cores with 4MB L2 cache.
  • 64 GPU Cores.
  • Up to 128GB DDR5 memory at 800GB/s.

倚天710

一个die有64core,每两个core是一个cluster,一块cpu封装两个die

一个die大小是314平方毫米,600亿晶体管

image-20211205130348832

平头哥的几款芯片:

preview

总结

AMD和Intel在服务器领域CPU设计上走了两个不同的方向,Intel通过RingBus、Mesh等方案在一块Die上集成多个core,成本高,在多核场景下性能好。

AMD则是通过设计小的Die来降低成本,然后将多个Die封装到一块CPU上来售卖,Zen1架构的多个Die之间延迟高,于是Zen2将IO抽离出来用一块单独的IO Die来负责IO,这样多核之间的时延比Zen1好了很多。

而在云计算场景下AMD的设计非常有竞争优势,因为云计算大部分时候是要把一块大的CPU分拆售卖,从架构上AMD对分拆售卖非常友好。

整体来说AMD用领先了一代的工艺(7nm VS 14nm),在MySQL查询场景中终于可以接近Intel了,但是海光、鲲鹏、飞腾还是不给力。

参考资料

CPU的制造和概念

[CPU 性能和Cache Line](/2021/05/16/CPU Cache Line 和性能/)

[Perf IPC以及CPU性能](/2021/05/16/Perf IPC以及CPU利用率/)

Intel、海光、鲲鹏920、飞腾2500 CPU性能对比

飞腾ARM芯片(FT2500)的性能测试

十年后数据库还是不敢拥抱NUMA?

一次海光物理机资源竞争压测的记录

[Intel PAUSE指令变化是如何影响自旋锁以及MySQL的性能的](/2019/12/16/Intel PAUSE指令变化是如何影响自旋锁以及MySQL的性能的/)

lmbench测试要考虑cache等

CPU性能和CACHE

为了让程序能快点,特意了解了CPU的各种原理,比如多核、超线程、NUMA、睿频、功耗、GPU、大小核再到分支预测、cache_line失效、加锁代价、IPC等各种指标(都有对应的代码和测试数据)都会在这系列文章中得到答案。当然一定会有程序员最关心的分支预测案例、Disruptor无锁案例、cache_line伪共享案例等等。

这次让我们从最底层的沙子开始用8篇文章来回答各种疑问以及大量的实验对比案例和测试数据。

大的方面主要是从这几个疑问来写这些文章:

  • 同样程序为什么CPU跑到800%还不如CPU跑到200%快?
  • IPC背后的原理和和程序效率的关系?
  • 为什么数据库领域都爱把NUMA关了,这对吗?
  • 几个国产芯片的性能到底怎么样?

系列文章

CPU的制造和概念

[Perf IPC以及CPU性能](/2021/05/16/Perf IPC以及CPU利用率/)

[CPU 性能和Cache Line](/2021/05/16/CPU Cache Line 和性能/)

十年后数据库还是不敢拥抱NUMA?

[Intel PAUSE指令变化是如何影响自旋锁以及MySQL的性能的](/2019/12/16/Intel PAUSE指令变化是如何影响自旋锁以及MySQL的性能的/)

Intel、海光、鲲鹏920、飞腾2500 CPU性能对比

一次海光物理机资源竞争压测的记录

飞腾ARM芯片(FT2500)的性能测试

image-20210802161558248

CPU中为什么要L1/L2等各级cache

因为CPU的速度和访问内存速度差异太大,导致CPU在计算的时候90%以上的时间花在等待从内存中取数据、写数据而此时CPU处于闲置状态,也就导致了所谓的 内存墙

cpu的速度大概50-60%每年的增长率,内存只有7%每年增长率:

A 1000× Improvement of the Processor-Memory Gap | SpringerLink

CPU访问内存慢的案例参考:Gallery of Processor Cache Effects

在数据使用前加载到CPU内更快的缓存中,最快的一级缓存等待时间是1~3个时钟周期。限制在于对于不在缓存中的数据,还是要等待数十上百个周期——按50周期算的话,不考虑并发和指令执行时间,缓存命中率达到98%,才能发挥一半的理论性能。然而实际情况中,大部分应用都无法达到这个命中率。

image-20211110174606037

CPU中的cache变迁历史

80486(1989), 8K的L1 cache第一次被集成在CPU中:

486 motherboard with CPU location and 2nd level cache marked

80686(1995) ,L2被放入到CPU的Package上,但是是一个独立的Die,可以看到L2大小和一个Die差不多:

Picture of a pentium Pro CPU, 256KB cache model

以酷睿为例,现在的CPU集成了L1/L2/L3等各级CACHE,CACHE面积能占到CPU的一半:

modernCPUwithL3.png

从上图可以看到L3的大小快到die的一半,L1/L2由每个core独享,L3是所有core共享,3级CACHE总面积跟所有core差不多大了。

image-20211110174810752

下图是目前一个主流的Die中CACHE的构成:

img

cache对速度的影响:

  • 一个方面是物理速度,如果要更大的容量就需要更多的晶体管,除了芯片的体积会变大,更重要的是大量的晶体管会导致速度下降,因为访问速度和要访问的晶体管所在的位置成反比,也就是当信号路径变长时,通信速度会变慢。这部分是物理问题。
  • 另外一个问题是,多核技术中,数据的状态需要在多个CPU中进行同步,并且,我们可以看到,cache和RAM的速度差距太大,所以,多级不同尺寸的缓存有利于提高整体的性能。

cache 大小查看

1
2
3
4
5
6
7
8
9
[root@bugu88 cpu0]# cd /sys/devices/system/
[root@bugu88 cpu0]# cat cache/index0/size
32K
[root@bugu88 cpu0]# cat cache/index1/size
32K
[root@bugu88 cpu0]# cat cache/index2/size
512K
[root@bugu88 cpu0]# cat cache/index3/size
32768K

不同型号CPU的cache、内存时延

测试命令:

1
numactl --membind=0 --cpunodebind=0 ./bin/lat_mem_rd 2000 64 //从结果看L3/memory latency不符合常识

image-20220304104859770

调整测试参数,增加 -t 参数

1
numactl -C 0 -m 0 ./bin/lat_mem_rd -W 5 -N 5 -t 2000M

内存基准测试命令 lat_mem_rd 的 -t 参数指定测试集以制造 TLB miss, Cache miss的压力场景,以测试 TLB miss,Cache miss对内存访问延迟的影响

image-20220304152056740

从上图可以看到的一些测试结论

  • 添加 -t 后(第二组测试),L2和L3的延时比较正常了
  • 倒数第三图hygon 7280 2node VS 8node(橙色) , 可以看到8node 内存延时降低了25%
  • 飞腾没开numa内存延时抖动非常大(倒数图二,灰色线),基本不可用,整体延时也比其它CPU高很多
  • hygon L3大小比较特殊,一个socket下多个Die之间没有共享
  • intel E5时延表现很优秀,intel E5 CPU开启numa后内存延时有30%以上的减少(图三)
  • 鲲鹏数据比较中规中矩,接近intel
  • stride参数、-t参数对整体数据影响比较大,x86、arm不同参数下也不一样

E5机器内存速度为2133 MT/S, 8163和8269则是2666 MT/S, 所以说E5的时延表现很优秀

矩阵乘法案例

不做任何处理,最直白的矩阵乘法运算,在Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz 运行情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#cat simple.c
#include <stdlib.h>
#include <stdio.h>
#include <emmintrin.h>
#define N 2000
double res[N][N] __attribute__ ((aligned (64)));
double mul1[N][N] __attribute__ ((aligned (64)));
double mul2[N][N] __attribute__ ((aligned (64)));
#define SM (CLS / sizeof (double))

//compile:gcc -o simd -DCLS=$(getconf LEVEL1_DCACHE_LINESIZE) ./simd.c
//
int main (void)
{
// ... Initialize mul1 and mul2
int i, i2, j, j2, k, k2;

for (i = 0; i < N; ++i)
for (j = 0; j < N; ++j)
for (k = 0; k < N; ++k)
res[i][j] += mul1[i][k] * mul2[k][j]; //mul2[k][j]是先列后行,对cache不友好;

// ... use res matrix
return 0;
}

如果现将矩阵转置一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdlib.h>
#include <stdio.h>
#include <emmintrin.h>
#define N 2000
double res[N][N] __attribute__ ((aligned (64)));
double mul1[N][N] __attribute__ ((aligned (64)));
double mul2[N][N] __attribute__ ((aligned (64)));
double tmp[N][N] __attribute__ ((aligned (64)));
#define SM (CLS / sizeof (double))

//compile:gcc -o simd -DCLS=$(getconf LEVEL1_DCACHE_LINESIZE) ./simd.c
//
int main (void)
{
// ... Initialize mul1 and mul2
int i, i2, j, j2, k, k2;

for (i = 0; i < N; ++i)
for (j = 0; j < N; ++j)
tmp[i][j] = mul2[j][i]; //先转置
for (i = 0; i < N; ++i)
for (j = 0; j < N; ++j)
for (k = 0; k < N; ++k)
res[i][j] += mul1[i][k] * tmp[j][k]; //转置后按行访问,对内存友好

// ... use res matrix
return 0;
}

执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//未做任何优化,直接矩阵乘法
#taskset -c 1 perf stat ./simple
47192.640339 task-clock (msec) # 1.001 CPUs utilized
88 context-switches # 0.002 K/sec
1 cpu-migrations # 0.000 K/sec
31,392 page-faults # 0.665 K/sec
117,866,224,774 cycles # 2.498 GHz
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
264,254,238,724 instructions # 2.24 insns per cycle
8,052,145,218 branches # 170.623 M/sec
4,573,572 branch-misses # 0.06% of all branches

47.151498977 seconds time elapsed

//转置后都是按行取数据,但是需要额外的空间
#taskset -c 0 perf stat ./simp2
30457.259168 task-clock (msec) # 1.001 CPUs utilized
137 context-switches # 0.004 K/sec
7 cpu-migrations # 0.000 K/sec
86,081 page-faults # 0.003 M/sec
76,068,232,551 cycles # 2.498 GHz
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
264,385,818,470 instructions # 3.48 insns per cycle
8,072,001,639 branches # 265.027 M/sec
4,414,867 branch-misses # 0.05% of all branches

30.437018792 seconds time elapsed

//按cache line 运算
#taskset -c 1 perf stat ./s3
29767.847109 task-clock (msec) # 1.001 CPUs utilized
41 context-switches # 0.001 K/sec
1 cpu-migrations # 0.000 K/sec
31,454 page-faults # 0.001 M/sec
74,346,857,277 cycles # 2.498 GHz
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
253,099,702,393 instructions # 3.40 insns per cycle
11,450,804,877 branches # 384.670 M/sec
16,043,642 branch-misses # 0.14% of all branches

29.742025067 seconds time elapsed

//使用simd指令,按理应该最快,实际效果很差 :(
#taskset -c 1 perf stat ./simd
140224.550539 task-clock (msec) # 1.001 CPUs utilized
243 context-switches # 0.002 K/sec
2 cpu-migrations # 0.000 K/sec
70,569 page-faults # 0.503 K/sec
350,218,614,852 cycles # 2.498 GHz
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
717,191,577,191 instructions # 2.05 insns per cycle
25,161,922,136 branches # 179.440 M/sec
54,411,349 branch-misses # 0.22% of all branches

140.101635085 seconds time elapsed

On ARM Kunpeng 920-4826:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#taskset -c 1 perf stat ./simple
150,242.52 msec task-clock # 1.000 CPUs utilized
943 context-switches # 0.006 K/sec
0 cpu-migrations # 0.000 K/sec
31,289 page-faults # 0.208 K/sec
390,626,613,178 cycles # 2.600 GHz
432,396,482,134 instructions # 1.11 insn per cycle
<not supported> branches
11,348,599 branch-misses

150.249408485 seconds time elapsed

#taskset -c 1 perf stat ./simp2
69,008.66 msec task-clock # 1.000 CPUs utilized
426 context-switches # 0.006 K/sec
0 cpu-migrations # 0.000 K/sec
39,104 page-faults # 0.567 K/sec
179,417,225,187 cycles # 2.600 GHz
432,409,078,894 instructions # 2.41 insn per cycle
<not supported> branches
11,122,131 branch-misses

69.014491453 seconds time elapsed

#taskset -c 1 perf stat ./s3
50,251.34 msec task-clock # 1.000 CPUs utilized
315 context-switches # 0.006 K/sec
0 cpu-migrations # 0.000 K/sec
31,289 page-faults # 0.623 K/sec
130,652,187,736 cycles # 2.600 GHz
291,261,746,765 instructions # 2.23 insn per cycle
<not supported> branches
160,585,583 branch-misses

50.254025852 seconds time elapsed

如果在aarch编译开启gcc -O3 优化选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//aarch gcc -O3 on
#taskset -c 1 perf stat ./simple //开O3后 优化器走了simd指令
67,897.93 msec task-clock # 1.000 CPUs utilized
414 context-switches # 0.006 K/sec
0 cpu-migrations # 0.000 K/sec
31,289 page-faults # 0.461 K/sec
176,532,812,062 cycles # 2.600 GHz
28,214,139,367 instructions # 0.16 insn per cycle
<not supported> branches
3,250,598 branch-misses


#perf stat ./s2 //s2代码直接按行访问mul2,不考虑结果对错,运算量一样,相当于整体转置
15,963.30 msec task-clock # 1.000 CPUs utilized
20 context-switches # 0.001 K/sec
0 cpu-migrations # 0.000 K/sec
31,288 page-faults # 0.002 M/sec
41,504,239,031 cycles # 2.600 GHz
56,108,176,644 instructions # 1.35 insn per cycle
<not supported> branches
4,586,197 branch-misses


#taskset -c 1 perf stat ./s3
5,695.85 msec task-clock # 1.000 CPUs utilized
35 context-switches # 0.006 K/sec
0 cpu-migrations # 0.000 K/sec
31,289 page-faults # 0.005 M/sec
14,808,977,314 cycles # 2.600 GHz
24,281,358,553 instructions # 1.64 insn per cycle
<not supported> branches
2,006,221 branch-misses


s3.c反编译后的汇编:
bc: 913a0060 add x0, x3, #0xe80
c0: eb04001f cmp x0, x4
c4: 1f584010 fmadd d16, d0, d24, d16
c8: 1f571c07 fmadd d7, d0, d23, d7 //参数 d7-精度,d0
cc: 1f561806 fmadd d6, d0, d22, d6
d0: 1f551405 fmadd d5, d0, d21, d5
d4: 1f541004 fmadd d4, d0, d20, d4
d8: 1f530c03 fmadd d3, d0, d19, d3
dc: 1f520802 fmadd d2, d0, d18, d2
e0: 1f510401 fmadd d1, d0, d17, d1
e4: 54fffd81 b.ne 94 <main+0x94>
e8: 91400c22 add x2, x1, #0x3, lsl #12
ec: fd000030 str d16, [x1]

FMADD指令

Floating-point fused Multiply-Add (scalar). This instruction multiplies the values of the first two SIMD&FP source registers, adds the product to the value of the third SIMD&FP source register, and writes the result to the SIMD&FP destination register.

一些对比解释:

编译优化选项设置-O2 级别及以上时,Kunpeng 处理器将对连续的浮点数乘法、加法融 合为乘加运算,以提升性能和精度。在-O2 级以上编译选项,x86 处理器不会将乘法和 加法做融合乘加运算,因此两种处理器在连续的浮点数乘法、加法运算后,小数点后 16 位存在差异。

cache对CPU性能的影响

CPU访问内存是非常慢的,所以我们在CPU中增加了多级缓存来匹配CPU和内存的速度。主频这20年基本都没怎么做高了,但是工艺提升了两个数量级,也就是集成的晶体管数量提升了2个数量级,工艺提升的能力主要给了cache,从而整体CPU性能提升了很多。

缓存对Oceanbase ,MySQL, ODPS的性能影响

以下测试数据主要来源于真实的业务场景:OB/MySQL/ODPS

img

x86 Skylake之前,L1 I/D 32KB, L2 256KB, L3 2.5MB/core, 2.5MB/core的L3(LLC)芯片面积相当于1/2 CPU core 的尺寸

  1. 关闭L3(2.5MB),关闭L2(256KB),此时性能CPI(越小越好)是4.25
  2. 关闭L3,打开L2(256KB),此时性能CPI为2.23
  3. 关闭L3,打开L2同时增加256KB,L2尺寸到512KB,性能CPI为1.38
  4. 打开L3(2.5MB),打开L2(256KB),性能为1.28 ,该状态就是intel CPU出厂的状态
  5. 打开L3,增加到16MB,打开L2(256KB),性能为1.25

上面的数据显示当L3关闭之后,从case 3 开始,L2仅仅增加256KB,L2芯片面积相对于CPU core 增加 5%(0.5 /2.5M * 025M),性能相对于case 2 提升1.61倍(2.23/1.38),而使用case 4 ,L3 2.5MB打开,相对于case 3,增加2.3MB(2.5MB - 256KB),芯片面积相对于CPU core 增加 46%(0.5/2.5M * 2.3M), 而性能仅仅提升 1.07倍(1.38/1.28),所以14年给Intel提议需要增加L2尺寸降低L3尺寸,这些数据促使Intel开始重新考虑对于数据中心缓存新的设计。

2014年的 Broadwell 的第五代智能酷睿处理器,是 Haswell 的 14nm 升级版($1745.00 - $1749.00):

image-20210719102039296

E5一个Die有16个物理core(上面截图是两个Socket, 每个Socket一个Die,每个物理core两个超线程),所以每core的L3大小:40M/16=2.5M/core

2015年则推出 SkyLake 架构的Platinum 8269CY($4702.00), 每core的L3大小:36M/26=1.38M/core:

image-20210719102112331

Intel 2015年 发表论文《High Performing Cache Hierarchies for Server Workloads》证明了阿里提出的建议的正确性,从Skylake架构开始将L2 cache 由 256KB 升级到 1MB, L3由2.5MB /core 压缩到 1.375MB / core, Intel之所以没有完全去掉L3的原因是希望这样设计的CPU对于 使用 CPU2006的workload性能仍然能够做到不受影响。

image-20210716102624566

上图是不同业务场景下,CPI 随cache大小的变化,可以看到随着cache增加性能基本不增加了。

CPU L2, Last Level Cache (LLC) 缓存的演变

Last Level Cache(L3) 在2016年之前都是2MB/core 或者 2.5MB/core, 这个原因取决于在此之前行业都是使用CPU2006作为设计CPU的benchmark,如下图所示:

img

根据上图中CPU2006的MPKI数据显示如果LLC在4MB的时候非常好,LLC在2.5MB之后MKPI提升10%性能只有1~3%的提升,2.5MB LLC cache是 CPU core 1/2 的芯片面积,因此若将LLC 由2.5MB升级到4MB,换算成CPU core的芯片面积是增长30%(1/2 * 1.5M/2.5M),但性能仅仅提升最多3%,这就是为什么基于CPU2006的benchmark条件下,intel将LLC设定为2~2.5MB的原因。

Cache的缺点

缓存有两大缺点:

  • 当数据集非常大的时候,时间空间局部性较低时缓存的工作效率很低;
  • 当缓存工作效率高的时候,局部性非常高,这意味着,根据定义,大多数缓存在大多数时间都处于空闲状态。

Hardware Memory Models 顺序一致性

对存储在内存中数据更改的可见性和一致性,所以这个契约被称为内存一致性模型(memory consistency model)或仅仅是内存模型(memory model)

r1/r2是线程本地变量,如下代码的可能结果是哪些?

1
2
3
4
5
6
Litmus Test: Message Passing
Can this program see r1 = 1, r2 = 0?

// Thread 1 // Thread 2
x = 1 r1 = y
y = 1 r2 = x

如果该litmus test的执行顺序一致,则只有六种可能的交替:

img

因为没有交替执行的结果会产生r1 = 1, r2 = 0,所以这个结果是不允许的。也就是说,在顺序执行的硬件上,litmus test执行结果出现r1 = 1, r2 = 0是不可能的。

顺序一致性的一个很好的思维模型是想象所有处理器直接连接到同一个共享内存,它可以一次处理一个线程的读或写请求。 不涉及缓存,因此每次处理器需要读取或写入内存时,该请求都会转到共享内存。 一次使用一次的共享内存对所有内存访问的执行施加了顺序顺序:顺序一致性。

img

x86 Total Store Order (x86-TSO) 总存储有序

所有处理器仍然连接到一个共享内存,但是每个处理器都将对该内存的写入(write)放入到本地写入队列中。处理器继续执行新指令,同时写操作(write)会更新到这个共享内存。一个处理器上的内存读取在查询主内存之前会查询本地写队列,但它看不到其他处理器上的写队列。其效果就是当前处理器比其他处理器会先看到自己的写操作。但是——这一点非常重要——==所有处理器都保证写入(存储store)到共享内存的(总)顺序,所以给这个模型起了个名字:总存储有序,或TSO==。当一个写操作到达共享内存时,任何处理器上的任何未来读操作都将看到它并使用该值(直到它被以后的写操作覆盖,或者可能被另一个处理器的缓冲写操作覆盖)。

img

针对前文的litmus test案例,写队列保证线程1在y之前将x写入内存,关于内存写入顺序(总存储有序)的系统级协议保证线程2在读y的新值之前读x的新值。因此,r1 = yr2 = x看不到新的x之前不可能看到新的y。存储顺序至关重要:线程1在写入y之前先写入x,因此线程2在看到x的写入之前不可能看到y的写入。

但是对于TSO系统下,以下case能看到r1 = 0, r2 = 0, 如果在顺序一致性的协议下这是不可能发生的

1
2
3
4
5
6
7
8
Litmus Test: Write Queue (also called Store Buffer)
Can this program see r1 = 0, r2 = 0?

// Thread 1 // Thread 2
x = 1 y = 1
r1 = y r2 = x
On sequentially consistent hardware: no.
On x86 (or other TSO): yes!

为了让TSO和顺序一致性协议保持一致,我们需要依赖于更强的内存排序,非顺序一致的硬件提供了称为内存屏障(或栅栏)的显式指令,可用于控制排序。我们可以添加一个内存屏障,以确保每个线程在开始读取之前都会刷新其先前对内存的写入:

1
2
3
4
// Thread 1           // Thread 2
x = 1 y = 1
barrier barrier
r1 = y r2 = x

加上正确的障碍,r1 = 0,r2 = 0也是不可能的了。内存屏障有很多种,它的存在给了程序员或语言实现者一种在程序的关键时刻强制顺序一致行为的方法。

ARM/POWER Relaxed Memory Model

ARM和POWER系统的概念模型是,每个处理器从其自己的完整内存副本中读取和向其写入,每个写入独立地传播到其他处理器,随着写入的传播,允许重新排序。

img

这里没有总存储顺序。虽然没有描述,但是每个处理器都被允许推迟读取(read),直到它等到它需要结果:读取(read)可以被延迟到稍后的写入(write)之后。在这个宽松的(relaxed)模型中,我们迄今为止所看到的每一个litmus test的答案都是“yes,这真的可能发生。”

在这个内存模型下,对于前文中的 Litmus Test: Message Passing case是可以看到r1=1,r2=0的(TSO保证不会),但是可以保证 Litmus Test: Store Buffering case 和TSO一致。

最后再附加几个Latency数据,让大家比较起来更有体感一些

各级IO延迟数字

Cache、内存、磁盘、网络的延迟比较

假设主频2.6G的CPU,每个指令只需要 0.38ns

每次内存寻址需要 100ns

一次 CPU 上下文切换(系统调用)需要大约 1500ns,也就是 1.5us(这个数字参考了这篇文章,采用的是单核 CPU 线程平均时间)

SSD 随机读取耗时为 150us

从内存中读取 1MB 的连续数据,耗时大约为 250us

同一个数据中心网络上跑一个来回需要 0.5ms

从 SSD 读取 1MB 的顺序数据,大约需要 1ms (是内存速度的四分之一)

磁盘寻址时间为 10ms

从磁盘读取 1MB 连续数据需要 20ms

如果 CPU 访问 L1 缓存需要 1 秒,那么访问主存需要 3 分钟、从 SSD 中随机读取数据需要 3.4 天、磁盘寻道需要 2 个月,网络传输可能需要 1 年多的时间。

内存和cache的latency对比

latency

各级cache的Latency

Cycle times

2012 年延迟数字对比表:

Work Latency
L1 cache reference 0.5 ns
Branch mispredict 5 ns
L2 cache reference 7 ns
Mutex lock/unlock 25 ns
Main memory reference 100 ns
持久内存 300 ns
Compress 1K bytes with Zippy 3,000 ns
Send 1K bytes over 1 Gbps network 10,000 ns
Read 4K randomly from SSD* 150,000 ns
Read 1 MB sequentially from memory 250,000 ns
Round trip within same datacenter 500,000 ns
Read 1 MB sequentially from SSD* 1,000,000 ns
Disk seek 10,000,000 ns
Read 1 MB sequentially from disk 20,000,000 ns
Send packet CA->Netherlands->CA 150,000,000 ns

一个比较有体感的比较:如果 CPU 访问 寄存器需要 1 秒,那么访问主存需要 3 分钟、从 SSD 中随机读取数据需要 3.4 天、磁盘寻道需要 2 个月,网络传输可能需要 1 年多的时间。

img

当然更古老一点的年代给出来的数据可能又不一样一点,但是基本比例差异还是差不多的:

Memory Hierarchy

img

测试Inte E5 L1 、L2、L3的cache延时图来加深印象,可以看到在每级cache大小附近时延有个跳跃(纵坐标是纳秒,横坐标是大小 M):

image-20220321172431647

推荐从这里看延时,拖动时间轴可以看到随着技术、工艺的改变Latency每一年的变化

image-20210613123006681

查看cpu cache数据

cat /proc/cpuinfo |grep -i cache
image.png

L1C、L2C、L3C、DDR 的Latency测试数据

下图从左至右响应时间分别是L1C、L2C、L3C、DDR,可以看出这四个Latency变化还是非常明显的,泾渭分明。

img

image-20210511160107225

image.png

测试memory latency

memory latency逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <unistd.h>

#define ONE p = (char **)*p;
#define FIVE ONE ONE ONE ONE ONE
#define TEN FIVE FIVE
#define FIFTY TEN TEN TEN TEN TEN
#define HUNDRED FIFTY FIFTY

static void usage()
{
printf("Usage: ./mem-lat -b xxx -n xxx -s xxx\n");
printf(" -b buffer size in KB\n");
printf(" -n number of read\n\n");
printf(" -s stride skipped before the next access\n\n");
printf("Please don't use non-decimal based number\n");
}


int main(int argc, char* argv[])
{
unsigned long i, j, size, tmp;
unsigned long memsize = 0x800000; /* 1/4 LLC size of skylake, 1/5 of broadwell */
unsigned long count = 1048576; /* memsize / 64 * 8 */
unsigned int stride = 64; /* skipped amount of memory before the next access */
unsigned long sec, usec;
struct timeval tv1, tv2;
struct timezone tz;
unsigned int *indices;

while (argc-- > 0) {
if ((*argv)[0] == '-') { /* look at first char of next */
switch ((*argv)[1]) { /* look at second */
case 'b':
argv++;
argc--;
memsize = atoi(*argv) * 1024;
break;

case 'n':
argv++;
argc--;
count = atoi(*argv);
break;

case 's':
argv++;
argc--;
stride = atoi(*argv);
break;

default:
usage();
exit(1);
break;
}
}
argv++;
}

char* mem = mmap(NULL, memsize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
// trick3: init pointer chasing, per stride=8 byte
size = memsize / stride;
indices = malloc(size * sizeof(int));

for (i = 0; i < size; i++)
indices[i] = i;

// trick 2: fill mem with pointer references
for (i = 0; i < size - 1; i++)
*(char **)&mem[indices[i]*stride]= (char*)&mem[indices[i+1]*stride];
*(char **)&mem[indices[size-1]*stride]= (char*)&mem[indices[0]*stride];

register char **p = (char **) mem;
//char **p = (char **) mem;
tmp = count / 100;

gettimeofday (&tv1, &tz);
for (i = 0; i < tmp; ++i) {
HUNDRED; //trick 1
}
gettimeofday (&tv2, &tz);
char **touch = p;
if (tv2.tv_usec < tv1.tv_usec) {
usec = 1000000 + tv2.tv_usec - tv1.tv_usec;
sec = tv2.tv_sec - tv1.tv_sec - 1;
} else {
usec = tv2.tv_usec - tv1.tv_usec;
sec = tv2.tv_sec - tv1.tv_sec;
}

printf("Buffer size: %ld KB, stride %d, time %d.%06d s, latency %.2f ns\n",
memsize/1024, stride, sec, usec, (sec * 1000000 + usec) * 1000.0 / (tmp *100));
munmap(mem, memsize);
free(indices);
}

分别在intel 8163和arm 鲲鹏920上执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
$cat run_mem_lat.sh
#!/bin/sh
#set -x

work=./mem-lat
buffer_size=1
node=$1
mem=$2

for i in `seq 1 15`; do
#echo $i
#echo $buffer_size
taskset -ac 1 $work -b $buffer_size -s 64
buffer_size=$(($buffer_size*2))
done

#sh run_mem_lat.sh
Buffer size: 1 KB, stride 64, time 0.001682 s, latency 1.60 ns
Buffer size: 2 KB, stride 64, time 0.001685 s, latency 1.61 ns
Buffer size: 4 KB, stride 64, time 0.001687 s, latency 1.61 ns
Buffer size: 8 KB, stride 64, time 0.001682 s, latency 1.60 ns
Buffer size: 16 KB, stride 64, time 0.001688 s, latency 1.61 ns
Buffer size: 32 KB, stride 64, time 0.001817 s, latency 1.73 ns
Buffer size: 64 KB, stride 64, time 0.005842 s, latency 5.57 ns
Buffer size: 128 KB, stride 64, time 0.005838 s, latency 5.57 ns
Buffer size: 256 KB, stride 64, time 0.005838 s, latency 5.57 ns
Buffer size: 512 KB, stride 64, time 0.005841 s, latency 5.57 ns
Buffer size: 1024 KB, stride 64, time 0.006056 s, latency 5.78 ns
Buffer size: 2048 KB, stride 64, time 0.006175 s, latency 5.89 ns
Buffer size: 4096 KB, stride 64, time 0.006203 s, latency 5.92 ns
Buffer size: 8192 KB, stride 64, time 0.006383 s, latency 6.09 ns
Buffer size: 16384 KB, stride 64, time 0.007345 s, latency 7.01 ns

[root@x86.170 /root]
#lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 96
On-line CPU(s) list: 0-95
Thread(s) per core: 2
Core(s) per socket: 24
Socket(s): 2
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 85
Model name: Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz
Stepping: 4
CPU MHz: 2500.390
CPU max MHz: 3100.0000
CPU min MHz: 1000.0000
BogoMIPS: 4998.87
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 1024K
L3 cache: 33792K
NUMA node0 CPU(s): 0-95

//鲲鹏920
#sh run_mem_lat.sh
Buffer size: 1 KB, stride 64, time 0.001628 s, latency 1.55 ns
Buffer size: 2 KB, stride 64, time 0.001623 s, latency 1.55 ns
Buffer size: 4 KB, stride 64, time 0.001613 s, latency 1.54 ns
Buffer size: 8 KB, stride 64, time 0.001613 s, latency 1.54 ns
Buffer size: 16 KB, stride 64, time 0.001622 s, latency 1.55 ns
Buffer size: 32 KB, stride 64, time 0.001613 s, latency 1.54 ns
Buffer size: 64 KB, stride 64, time 0.001637 s, latency 1.56 ns
Buffer size: 128 KB, stride 64, time 0.003749 s, latency 3.58 ns
Buffer size: 256 KB, stride 64, time 0.003320 s, latency 3.17 ns
Buffer size: 512 KB, stride 64, time 0.003779 s, latency 3.60 ns
Buffer size: 1024 KB, stride 64, time 0.004310 s, latency 4.11 ns
Buffer size: 2048 KB, stride 64, time 0.004655 s, latency 4.44 ns
Buffer size: 4096 KB, stride 64, time 0.005032 s, latency 4.80 ns
Buffer size: 8192 KB, stride 64, time 0.005721 s, latency 5.46 ns
Buffer size: 16384 KB, stride 64, time 0.006470 s, latency 6.17 ns

[root@ARM 15:58 /root]
#lscpu
Architecture: aarch64
Byte Order: Little Endian
CPU(s): 96
On-line CPU(s) list: 0-95
Thread(s) per core: 1
Core(s) per socket: 48
Socket(s): 2
NUMA node(s): 4
Model: 0
CPU max MHz: 2600.0000
CPU min MHz: 200.0000
BogoMIPS: 200.00
L1d cache: 64K
L1i cache: 64K
L2 cache: 512K
L3 cache: 24576K
NUMA node0 CPU(s): 0-23
NUMA node1 CPU(s): 24-47
NUMA node2 CPU(s): 48-71
NUMA node3 CPU(s): 72-95

为什么CACHE比内存快?

首先肯定是距离的原因,另外这两种存储结构的制造工艺不同导致的速度差异也很大,从上面可以看到一块4000刀的CPU有一半的面积是cache,也就是40M CACHE花了2000刀,如果用来买内存条能卖一大堆吧。

接下来说下CACHE(SRAM) 和内存(DRAM)制造的工艺差异

SRAM(Static Random-Access Memory,静态随机存取存储器)的芯片

CPU Cache 用的是一种叫作 SRAM(Static Random-Access Memory,静态随机存取存储器)的芯片。

SRAM 之所以被称为”静态”存储器,是因为只要处在通电状态,里面的数据就可以保持存在。而一旦断电,里面的数据就会丢失了。在 SRAM 里面,一个比特的数据,需要 6~8 个晶体管。所以 SRAM 的存储密度不高。同样的物理空间下,能够存储的数据有限。不过,因为 SRAM 的电路简单,所以访问速度非常快。

L1和L2一般是SRAM, L1的容量通常比L2小,容量大的SRAM访问时间就越长,同样制程和设计的情况下,访问延时与容量的开方大致是成正比的。

另外工作原理不同速度差异也不一样,L1就是讲究快,比如L1是N路组相联,N路阻相联的意思就是N个Cache单元同时读取数据(有点类似RAID0)。

L3用的还是SRAM,但是在考虑换成STT-MRAM,这样容量更大。

DRAM(Dynamic Random Access Memory,动态随机存取存储器)的芯片

为磁芯存储器画上句号的是集成电路随机存储器件。1966年,IBM Thomas J. Watson研究中心的Dr. Robert H. Dennard开发出了单个单元的动态随机存储器DRAM,DRAM每个单元包含一个开关晶体管和一个电容,利用电容中的电荷存储数据。因为电容中的电荷会泄露,需要每个周期都进行刷新重新补充电量,所以称其为动态随机存储器。

内存用的芯片和 Cache 有所不同,它用的是一种叫作 DRAM(Dynamic Random Access Memory,动态随机存取存储器)的芯片,比起 SRAM 来说,它的密度更高,有更大的容量,而且它也比 SRAM 芯片便宜不少。

动态随机存取存储器(DRAM)是一种半导体存储器,主要的作用原理是利用电容内存储电荷的多寡来代表一个二进制比特(bit)是1还是0。由于在现实中晶体管会有漏电电流的现象,导致电容上所存储的电荷数量并不足以正确的判别数据,而导致数据毁损。因此对于DRAM来说,周期性地充电是一个无可避免的要件。由于这种需要定时刷新的特性,因此被称为“动态”存储器。相对来说,静态存储器(SRAM)只要存入数据后,纵使不刷新也不会丢失记忆。

DRAM 的一个比特,只需要一个晶体管和一个电容就能存储。所以,DRAM 在同样的物理空间下,能够存储的数据也就更多,也就是存储的”密度”更大。DRAM 的数据访问电路和刷新电路都比 SRAM 更复杂,所以访问延时也就更长。

img

SRAM是比DRAM更为昂贵,但更为快速、非常低功耗(特别是在空闲状态)。 因此SRAM首选用于带宽要求高,或者功耗要求低,或者二者兼而有之。 SRAM比起DRAM更为容易控制,也更是随机访问。 由于复杂的内部结构,SRAMDRAM的占用面积更大,因而不适合用于更高储存密度低成本的应用,如PC内存。

SRAM和DRAM原理比较

简单说DRAM只有一个晶体管和一个电容,SRAM就复杂多了,需要6个晶体管

What is the difference between SRAM and DRAM

图左边的 DRAM 的状态是保持在电容器C中。晶体管M用来控制访问。如果要读取状态,拉升访问线AL,这时,可能会有电流流到数据线DL上,也可能没有,取决于电容器是否有电。如果要写入状态,先设置DL,然后升起AL一段时间,直到电容器充电或放电完毕。

由于读取状态时需要对电容器放电,所以这一过程不能无限重复,不得不在某个点上对它重新充电。更糟糕的是,为了容纳大量单元(现在一般在单个芯片上容纳109以上的RAM单元),电容器的容量必须很小(0.000000000000001法拉以下)。这样,完整充电后大约持有几万个电子。即使电容器的电阻很大(若干兆欧姆),仍然只需很短的时间就会耗光电荷,称为「泄漏」。

这种泄露就是现在的大部分DRAM芯片每隔64ms就必须进行一次刷新的原因。在刷新期间,对于该芯片的访问是不可能的,这甚至会造成半数任务的延宕。(相关内容请察看【highperfdram】一章)

这个问题的另一个后果就是无法直接读取芯片单元中的信息,而必须通过信号放大器将0和1两种信号间的电势差增大,才能分辨出来。

DRAM 主要靠电容充放电来识别0和1,但是充放电是一个持续过程,需要耗时,这也是导致内存延时大的主要原因

image-20220730161825538

不像SRAM可以即刻读取数据,当要读取DRAM的时候,必须花一点时间来等待电容的冲放电完全。这一点点的时间最终限制了DRAM的速度。

SRAM 需要注意以下问题:

  • 一个单元需要6个晶体管。也有采用4个晶体管的SRAM,体积大、贵、结构复杂。
  • 维持状态需要恒定的电源。
  • 升起WL后立即可以读取状态。信号与其它晶体管控制的信号一样,是直角的(快速在两个状态间变化)。
  • 状态稳定,不需要刷新循环。

SRAM也有其它形式,不那么费电,但比较慢。由于我们需要的是快速RAM,因此不在关注范围内。这些较慢的SRAM的主要优点在于接口简单,比动态RAM更容易使用。

详细比较:

Difference Between SRAM and DRAM - YouTube

SRAM 也有其它形式,不那么费电,但比较慢。由于我们需要的是快速RAM,因此其它形式的 SRAM 不在关注范围内。这些较慢的SRAM的主要优点在于接口简单,比动态RAM更容易使用。CPU cache用的是快速 SRAM,本文提到的 SRAM 都是指快速 SRAM

DRAM 刷新

DRAM内存内部使用电容来存储数据,由于电容有漏电现象,经过一段时间电荷会泄放,导致数据不能长时间存储。因此需要不断充电,这个充电的动作叫做刷新。自动刷新是以“行”为单位进行刷新,刷新操作与读写访问无法同时进行,即刷新时会对内存的性能造成影响。同时温度越高电容泄放越快,器件手册通常要求芯片表面温度在0℃-85℃时,内存需要按照64ms的周期刷新数据,在85℃~95℃时,按照32ms的周期刷新数据。

BIOS中内存刷新速率选项提供了auto选项,可以根据工作温度自动调节内存刷新速率。相比默认32ms配置可以提升内存性能,同时确保工作温度在85℃~95℃时内存数据的可靠性。

DRAM 频率

内存实际有3种频率:

  • 核心频率
  • 时钟频率(IO控制器频率)
  • 等效频率(有效数据传输频率)

核心频率就是内存的Cell阵列(内存电容)的刷新频率,只与内存本身物理特性有关,目前频率基本都在133MHz~200MH之间

我们俗称DDR4-2666实际指的是等效频率,是通过上升下降沿进行数据预取放大后的实际数据传输频率,DDR4 prefetch是8,通过bank group提升到核心频率的16倍,所以DDR4的最低起频是133.333MHz*16=2133MHz。DDR(Double Data Rate)因为是在一个时钟周期的上升沿和下降沿个执行预取,所以时钟频率=等效频率/2

Persistence memory

左边是在32G物理内存的基础上挂了128G pmem, 然后系统通过free能看到 154G内存,用 lat_mem_rd 实际测试速度可以看到左边的机器抖动比较大

image-20220607154156826

系列文章

CPU的制造和概念

[CPU 性能和Cache Line](/2021/05/16/CPU Cache Line 和性能/)

[Perf IPC以及CPU性能](/2021/05/16/Perf IPC以及CPU利用率/)

Intel、海光、鲲鹏920、飞腾2500 CPU性能对比

飞腾ARM芯片(FT2500)的性能测试

十年后数据库还是不敢拥抱NUMA?

一次海光物理机资源竞争压测的记录

[Intel PAUSE指令变化是如何影响自旋锁以及MySQL的性能的](/2019/12/16/Intel PAUSE指令变化是如何影响自旋锁以及MySQL的性能的/)

参考资料

Gallery of Processor Cache Effects

7个示例科普CPU CACHE

与程序员相关的CPU缓存知识

45-year CPU evolution: one law and two equations

揭秘 cache 访问延迟背后的计算机原理

业务与芯片垂直整合的一点思考

What Every Programmer Should Know About Main Memory by Ulrich Drepper 中文版:https://zhuanlan.zhihu.com/p/611133924

做了一道数学几何题

John Hattie的visible learning,这本书集成了两亿多学生的数据,然后得到了哪些品质能够决定一个人学习好坏,想通过一道几何题目来验证下。先看下John Hattie的结论

哪些品质能够决定一个人学习好坏

排在第一名的品质是复盘、总结能力

简单的说,这个能力就是这个孩子心里是否有个“小教练”,能够每次跳脱出当前任务,帮助自己分析,失败在哪里,成功在哪里,如何进阶,如何训练等等。

举几个例子:

  1. 如果写不出作文,这个“小教练”能告诉孩子,是没有素材,还是文字能力不强。

    如果是文字能力不强,应该如何训练(是造句,还是拆段落)

  2. 如果数学题做不出来,这个“小教练”能告诉孩子,我的弱点在哪里,哪个类型题我有重大问题,是因为哪里没有理解和打通。

有内化的“自我教练”,这个能力系数是1.67。也就是其他能力相当,学习效果可以翻1.67倍。

排在第二名的是建构能力。简单的说是逻辑推理,做事顺序等等

在内心“小教练”能把问题进行拆解之后,建构能力能把这些问题进行排序,应该怎么做更合理。

最终怎么把训练步骤整合。遇见一个问题,先干什么,再干什么等等。

有很强的顺序能力,系数为1.44。也就是其他能力相当,学习效果可以翻1.44倍。

排在第三名的能力是智商和过去成绩

这个毋庸置疑,聪明做事就会简单一些。平均效应系数为0.67。

也就是说,其他能力相当,智商高和过去成绩好,学习效果提升67%

智商重要程度应该比这里更高,但是实际高智商的太少,大多都是因为基础知识好给人产生了智商高的误解!

排在第四名的能力是自我驱动力

简单的说,知道自己为什么学习,能够自我鼓励,遇见失败能抗挫,有很强的心理驱动力。

平均效应系数为0.48。也就是说,其他能力相当,有自我驱动力的人,学习效果提升48%。

排在第五名的才是集中注意力

也就是说,注意力强。注意力对学习影响,并没有很多家长想象的那么大。

注意力的平均效应系数为0.44

也就是说,其他能力相当,注意力好的孩子,效果能提升44%

———总结一下——-

学习提升的个人因素:

自我分析,自我教练的元认知能力 》 逻辑排序与制定计划的建构能力》 智商和过去成绩 》自我驱动力》 集中注意力。

很多家长痴迷于“专注力”。当然专注力是一个效应量很强的学习力,但是从整体数据看,对学习的提升效果,仅仅排到第五名。

帮助孩子建立元认知能力和建构能力的培训,才能给他们对终身学习有帮助的技能包

以上缺少了对方法的落地执行能力的评估,实际这是影响最大的

案例数学几何题

题目如下(图中红色、绿色线是我绘上去的辅助线)

image-20210623121704887

目标分解

求得四个阴影部分的面积相加;

求得白色部分面积即可以得到三角形内部两块阴影部分面积;

求得红色三角形面积记得通过圆面积减得三角形面积得到三角形外部阴影面积。红色辅助线、绿色辅助线

题目给出的条件

大三角形是等腰直角三角形(两个角都是45度,直角90度);

两圆相切:圆心连线经过切点,连线长度为两圆半径相加。大圆半径为2

这里关于两圆相切我完全不记得有啥特性了,所以去Google了一把得到如下两圆相切的特性:

image-20210623130815151

想了一下在我丢开课本几十年后我看到等要直接三角形我能得到:45度、两条边相等这两个结论;但是看到两圆相切我完全想不起来这是什么东西了。相信这两个知识点在我中学的时候肯定无比熟练。

但是两圆相切完全不会出现在我的生活和应用中,但是等要直接三角形太常见了,它反复出现在我的生活中,所以我只要没得老年痴呆应该会一直记得。

解题关键

  1. 如何求得小圆半径;
  2. 如何求红色辅助线构成的右下角三角形的面积(是否是个等腰直角三角形?),求得这个三角形的面积就能算出右下角阴影面积
  3. 从1的小圆半径和2的方法同理可得左上角的外部阴影面积
  4. 从2、3的阴影面积可以求得大三角形内部两块白色区域面积
  5. 这样的到了全部阴影的面积

详细步骤

  1. 求解小圆半径:相切是切入点,两圆圆心连线经过想切点(连线长=大圆半径+小圆半径)(绿线)
  2. 利用等腰直接三角形得到左下角绿色新直角三角形由勾股定理算出小圆半径(绿线)
  3. 大圆交点到圆心作辅助线得到右下角等腰三角形,由一个角是45度,和等腰三角形的特点退出另外一个角也为45度,从而得到右下角红色三角形是等腰直角三角形(红线)
  4. 通过大圆四分之一面积减掉右下角红线等腰三角形面积得到右下阴影部分面积,同理可得右上阴影面积。
  5. 通过半圆面积减掉阴影面积可分别得到两个半圆内部的白色部分面积
  6. 大三角形面积减掉6中的两个白色面积得到两个小阴影面积
  7. 到此分别得到了四个阴影部分面积

复盘求解过程

教练在哪里?

教练就是日益自我训练的过程,教练在事后复盘上述过程,解题过程中没有教练参与

如果做不出来,那么教练就来问:

是题目没读懂得到的信息不够(仔细多读题,提高阅读能力);

还是由题目中的已知条件得不到相关的直接推理(比如两圆相切因为不了解特性,所以得不到连线就是两个圆半径之和—-这种只能多看书);

或者推不出来右下角的直角等腰三角形(对等腰三角形理解不够)……

如果做得过于绕,那么教练就来问:

还能简化(这个简化不是要奇技淫巧的快解),而是要既解决问题又不啰嗦,同时又是自己掌握知识的恰切运用!

教练就是复盘上述过程,得到方法进步或者知识缺陷或者理解缺陷,而不是得到答案。

逻辑推理,做事顺序

先求什么再求什么,怎么样从已知条件得到简单结论,然后再分步得到阶段性小结论(各个小块面积)到最终目标,这就是John Hattie提到的逻辑推理做事顺序能力

基础知识的运用

勾股定理、等腰三角形、三角形三角之和、相切特性、圆面积、等腰三角形面积计算

没有任何一个复杂的基础知识,也没有任何需要几次的推导,全是定理带来的直接特性,对智力要求极低

自我驱动和集中注意力是个长期过程,自我驱动决定了之前的学习欲望和基本知识点的掌握

其他总结

你有更巧妙的解法?对不起,我不需要,我要的就是这种学渣也完全能掌握,只需要简单地1+1+1+1就得到4的方法,我不需要2*2得到4,因为1+1+1+1对不会乘法的学渣也能掌握,我要的就是这种普适的方法。

学渣也能掌握这个方法,然后用这个方法训练自己解决其他类似题目。对学渣来说考80分就很开心了,等他们有了考80分的能力就有野心向90进发。

学渣更重要的是追求容易的全部解决,复杂的直接战略性放弃,只有在容易的全部掌握后才能慢慢挑战复杂题目。容易的基础题都解决不好只是追求奇技淫巧的解法或者复杂题目容易迷失自己和快速遗忘。

受过训练的中学生如何解这题

做如下BD和HE两条辅助线,梯形BDEH的面积就是要求的面积。

也就是ABC三角形的面积减掉AEH和BCD两个三角形的面积。

那么需要证明 BD=CD,因为DF=CF,角FCD为45度,所以CDF为等腰三角形,接下来证明过程和上面一样。同时同理可证AEH也是等腰直角三角形。

可以看到受过训练的中学生的解题方法更为犀利一些,但是前面文章的方法最为朴素和直接。训练过的方法效率更高,当然两个方法基本知识的运用没有差别。

比如受过训练的方法还是需要下图红色、绿色两条辅助线。这就是职业和业余的差距。

image-20210625175006683

0%