Perf是Linux kernel中的系统性能优化工具,perf基本原理的话是在CPU的PMU register中Get/Set performance counters来获得诸如instructions executed,cache-missed suffered,branches mispredicted等信息。
perf本身的工具有很多,这里主要介绍个人在查询程序性能问题时使用的一些工具
包括perf list、perf stat、perf record、perf report
使用perf之前肯定要知道perf能监控哪些性能指标吧?那么就要使用perf list进行查看,通常使用的指标是cpu-clock/task-clock等,具体要根据需要来判断
$ perf list
List of pre-defined events (to be used in -e):
cpu-cycles OR cycles [Hardware event]
instructions [Hardware event]
…
cpu-clock [Software event]
task-clock [Software event]
context-switches OR cs [Software event]
…
ext4:ext4_allocate_inode [Tracepoint event]
kmem:kmalloc [Tracepoint event]
module:module_load [Tracepoint event]
workqueue:workqueue_execution [Tracepoint event]
sched:sched_{wakeup,switch} [Tracepoint event]
syscalls:sys_{enter,exit}_epoll_wait [Tracepoint event]
…
不同内核版本列出的结果不一样多…不过基本是够用的,但是无论多少,我们可以基本将其分为三类
具体监控哪个变量的话,譬如使用后面的perf report工具,则加-e 监控指标,如
perf report -e cpu-clock ls
监控运行ls命令时的cpu时钟占用监控
刚刚知道了可以监控哪些事件,但是事件这么多,该如何下手呢?
解决问题的时候有条理才解决的更快,所以面对一个性能问题的时候,最好采用自顶向下的策略。先整体看看该程序运行时各种统计事件的大概,再针对某些方向深入细节。而不要一下子扎进琐碎细节,会一叶障目的。
整体监测代码性能就需要使用perf stat这个工具,该工具主要是从全局上监控,可以看到程序导致性能瓶颈主要是什么原因。因为不同的程序导致其性能瓶颈的原因不同,譬如有些程序慢是由于计算量大,而有些程序是由于频繁的I/O导致性能瓶颈,他们的优化方式不同。perf stat通过概括精简的方式提供被调试程序运行的整体情况和汇总数据。
使用方法
perf stats 程序
譬如
perf stat ./gw --gtpu-ip 172.31.24.58 --sgw-s11-ip 172.31.24.250 --zmq-ip 172.31.31.174 --sgi-if eth1 --teid 1 --mysql 172.31.20.157 -cgw
程序运行完之后,然后使用ctrl+c来终止程序(若程序自动终止则不用),之后,perf便会打印出监控事件结果,类似结果如下:
Performance counter stats for './gw --gtpu-ip 172.31.24.58 --sgw-s11-ip 172.31.24.250 --zmq-ip 172.31.31.174 --sgi-if eth1 --teid 1 --mysql 172.31.20.157 -cgw':
1773.651816 task-clock (msec) # 0.016 CPUs utilized
79,054 context-switches # 0.045 M/sec
757 cpu-migrations # 0.427 K/sec
16,368 page-faults # 0.009 M/sec
<not supported> cycles
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
<not supported> instructions
<not supported> branches
<not supported> branch-misses
109.795527410 seconds time elapsed
前面通过perf stat获得了程序性能瓶颈类型,之后,假设你已经知道哪个进程需要优化(若不知道则需要使用perf top进行进一步监控,这里由于个人没有使用过,所以不作介绍),那么下一步就是对该进程进行细粒度的分析,分析在长长的程序代码中究竟是哪几段代码、哪几个函数需要修改呢?这便需要使用 perf record 记录单个函数级别的统计信息,并使用 perf report 来显示统计结果。
调优应该将注意力集中到百分比高的热点代码片段上,假如一段代码只占用整个程序运行时间的 0.1%,就算将其优化到仅剩一条机器指令,恐怕也只能将整体的程序性能提高 0.1%。
好钢用在刀刃上
仍以之前的gw程序为例,假设要监控的指标为cpu-clock
perf record -e cpu-clock -g ./gw --gtpu-ip 172.31.24.58 --sgw-s11-ip 172.31.24.250 --zmq-ip 172.31.31.174 --sgi-if eth1 --teid 1 --mysql 172.31.20.157 -cgw
前面通过perf record
工具获得了某一进程的指标监控数据perf.data,下面就需要使用perf report工具查看该文件
使用方法
perf report -i perf-report生成的文件
譬如
perf report -i perf.data
上面使用perf record
获得的数据的结果如下
+ 4.93% gw libcurl-gnutls.so.4.3.0 [.] 0x000000000001e1e0
+ 4.93% gw [kernel.kallsyms] [k] eventfd_write + 2.96% gw [kernel.kallsyms] [k] ipt_do_table + 2.46% gw [kernel.kallsyms] [k] xen_hypercall_event_channel_op ? 1.97% gw libc-2.19.so [.] _int_malloc + 1.97% gw libc-2.19.so [.] __clock_gettime ? 1.97% gw gw [.] nwGtpv2cHandleInitialReq(NwGtpv2cStack*, unsigned int, MqPackage&) ? 1.97% gw [kernel.kallsyms] [k] pvclock_clocksource_read ? 1.97% gw [kernel.kallsyms] [k] ip_finish_output ? 1.97% gw [kernel.kallsyms] [k] ixgbevf_xmit_frame
+ 1.48% gw [kernel.kallsyms] [k] kmem_cache_alloc_trace ? 1.48% gw [kernel.kallsyms] [k] sk_run_filter
这里使用我在实验中程序在某一场景CPU占用率飙升的问题作为示例
CPU飙升场景与正常场景使用perf stat对比差异
执行
perf stat ./gw --gtpu-ip 172.31.24.58 --sgw-s11-ip 172.31.24.250 --zmq-ip 172.31.31.174 --sgi-if eth1 --teid 1 --mysql 172.31.20.157 -cgw
CPU飙升场景
Performance counter stats for './gw --gtpu-ip 172.31.24.58 --sgw-s11-ip 172.31.24.250 --zmq-ip 172.31.31.174 --sgi-if eth1 --teid 1 --mysql 172.31.20.157 -cgw':
1773.651816 task-clock (msec) # 0.016 CPUs utilized
79,054 context-switches # 0.045 M/sec
757 cpu-migrations # 0.427 K/sec
16,368 page-faults # 0.009 M/sec
<not supported> cycles
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
<not supported> instructions
<not supported> branches
<not supported> branch-misses
109.795527410 seconds time elapsed
正常场景
Performance counter stats for './gw --gtpu-ip 172.31.24.58 --sgw-s11-ip 172.31.24.250 --zmq-ip 172.31.31.174 --sgi-if eth1 --teid 1 --mysql 172.31.20.157 -cgw':
1186.728996 task-clock (msec) # 0.018 CPUs utilized
78,284 context-switches # 0.066 M/sec
69 cpu-migrations # 0.058 K/sec
16,368 page-faults # 0.014 M/sec
<not supported> cycles
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
<not supported> instructions
<not supported> branches
<not supported> branch-misses
64.456003339 seconds time elapsed
通过对比可以发现:
通过perf stat整体上监控进程性能问题之后,使用perf record等对进程本身进行监控
执行
perf record -e task-clock -g ./gw --gtpu-ip 172.31.24.58 --sgw-s11-ip 172.31.24.250 --zmq-ip 172.31.31.174 --sgi-if eth1 --teid 1 --mysql 172.31.20.157 -cgw
perf report -i perf.data
结果如下
root@ip-172-31-24-250:/home/ubuntu/EPC/gw#
+ 4.93% gw libcurl-gnutls.so.4.3.0 [.] 0x000000000001e1e0
+ 4.93% gw [kernel.kallsyms] [k] eventfd_write + 2.96% gw [kernel.kallsyms] [k] ipt_do_table + 2.46% gw [kernel.kallsyms] [k] xen_hypercall_event_channel_op ? 1.97% gw libc-2.19.so [.] _int_malloc + 1.97% gw libc-2.19.so [.] __clock_gettime ? 1.97% gw gw [.] nwGtpv2cHandleInitialReq(NwGtpv2cStack*, unsigned int, MqPackage&) ? 1.97% gw [kernel.kallsyms] [k] pvclock_clocksource_read ? 1.97% gw [kernel.kallsyms] [k] ip_finish_output ? 1.97% gw [kernel.kallsyms] [k] ixgbevf_xmit_frame
+ 1.48% gw [kernel.kallsyms] [k] kmem_cache_alloc_trace ? 1.48% gw [kernel.kallsyms] [k] sk_run_filter ? 1.48% gw [kernel.kallsyms] [k] fib_table_lookup ? 1.48% gw [kernel.kallsyms] [k] _raw_spin_unlock_irqrestore ? 0.99% gw libpthread-2.19.so [.] __libc_fcntl
+ 0.99% gw libc-2.19.so [.] vfprintf ? 0.99% gw libc-2.19.so [.] malloc ? 0.99% gw libc-2.19.so [.] free ? 0.99% gw libc-2.19.so [.] inet_ntop ? 0.99% gw libc-2.19.so [.] inet_pton
+ 0.99% gw [vdso] [.] 0x0000000000000ca1 ? 0.99% gw [kernel.kallsyms] [k] __do_softirq ? 0.99% gw [kernel.kallsyms] [k] ksize ? 0.99% gw [kernel.kallsyms] [k] kfree
+ 0.99% gw [kernel.kallsyms] [k] fput ? 0.99% gw [kernel.kallsyms] [k] d_alloc_pseudo ? 0.99% gw [kernel.kallsyms] [k] sys_socket ? 0.99% gw [kernel.kallsyms] [k] datagram_poll ? 0.99% gw [kernel.kallsyms] [k] skb_network_protocol
+ 0.99% gw [kernel.kallsyms] [k] __dev_queue_xmit