Samples: 123K of event 'cycles', Event count (approx.): 36930701307
Overhead Command Shared Object Symbol
18.91% swapper [kernel.kallsyms] [k] intel_idle
5.18% dev_ui libQt5lxxx [.] 0x00000000013044c7
3.20% dev_ui libc-2.19.so [.] _int_malloc
1.03% dev_ui libc-2.19.so [.] __clock_gettime
3.04% todesk libpixman-1.so.0.38.4 [.] 0x000000000008cac0
1.20% todesk [JIT] tid 126593 [.] 0x0000000001307c7a
0.84% todesk [JIT] tid 126593 [.] 0x000000000143c3f4
0.73% Xorg i965_dri.so [.] 0x00000000007cefe0
0.65% todesk libsciter-gtk.so [.] tool::tslice<gool::argb>::xcopy
0.58% Xorg i965_dri.so [.] 0x00000000007cf00e
0.53% Xorg i965_dri.so [.] 0x00000000007cf03c
0.49% todesk [JIT] tid 126593 [.] 0x0000000001307cb2
0.48% Xorg i965_dri.so [.] 0x00000000007cf06a
0.44% todesk [JIT] tid 126593 [.] 0x0000000001307cb6
0.41% todesk [JIT] tid 126593 [.] 0x0000000001307cc0
0.40% x-terminal-emul libz.so.1.2.11 [.] adler32_z
0.40% todesk [JIT] tid 126593 [.] 0x0000000001307c83
0.38% todesk [JIT] tid 126593 [.] 0x0000000001307cbb
0.33% swapper [kernel.kallsyms] [k] menu_select
0.32% gnome-shell libmutter-clutter-6.so.0.0.0 [.] clutter_actor_paint
0.31% gnome-shell libgobject-2.0.so.0.6400.6 [.] g_type_check_instance_is_a
0.31% swapper [kernel.kallsyms] [k] psi_group_change
0.24% SDK_Timer-8 [kernel.kallsyms] [k] psi_group_change
0.24% todesk libc-2.31.so [.] __memset_avx2_unaligned_erms
0.18% todesk [JIT] tid 126593 [.] 0x00000000013044c7
0.18% todesk [JIT] tid 126593 [.] 0x000000000143c3f0
0.17% gnome-shell libglib-2.0.so.0.6400.6 [.] g_hash_table_lookup
0.17% todesk [JIT] tid 126593 [.] 0x000000000143c426
0.17% todesk [JIT] tid 126593 [.] 0x000000000143c3dd
0.16% todesk [JIT] tid 126593 [.] 0x000000000143c3d9
0.16% swapper [kernel.kallsyms] [k] cpuidle_enter_state
0.16% SDK_Timer-8 [kernel.kallsyms] [k] syscall_exit_to_user_mode
0.16% swapper [kernel.kallsyms] [k] __sched_text_start
在第二行我们发现一个dev_ui ,占用了5.18%,使用了libQt5lxxx库, 它本身功能UI显示,,但是占用较高的CPU,说明调用该库存在问题(代码本身问题),需要对调用该库的代码进行检查。
第三行libc-2.19.so [.] _int_malloc 这是常用的malloc操作,由于对代码比较熟悉,在这个过程中不应该有这么多次申请内存,说明代码本身有问题,需要对申请动态内存的代码进行检查
第四行行 __clock_gettime 这个是由于计时需要,需要频繁获取时间,通常是指 gettimeofday()函数
整个统计显示有很多task-clock占用是由于kernel.kallsyms导致,同时也验证了对perf stat获得的数据的初步判断,即CPU飙升也与频繁的CPU迁移(内核态中断用户态操作)导致,解决办法是采用CPU绑核
也许有的人会奇怪为什么自己完全是一个用户态的程序为什么还会统计到内核态的指标?一是用户态程序运行时会受到内核态的影响,若内核态对用户态影响较大,统计内核态信息可以了解到是内核中的哪些行为导致对用户态产生影响;二则是有些用户态程序也需要依赖内核的某些操作,譬如I/O操作+ 4.93% dev_ui libcurl-gnutls.so.4.3.0 [.] 0x000000000001e1e0 ,左边的加号代表perf已经记录了该调用关系,按enter键可以查看调用关系,perf监控该进程结果记录到很多内核调用,说明该进程在运行过程中,有可能被内核态任务频繁中断,应尽量避免这种情况,对于这个问题我的解决办法是采用绑核,譬如机器有8个CPU,那么我就绑定内核操作、中断等主要在0-5CPU,GW由于有两个线程,分别绑定到6、7CPU上。
注意:调优应该将注意力集中到百分比高的热点代码片段上,假如一段代码只占用整个程序运行时间的 0.1%,即使您将其优化到仅剩一条机器指令,恐怕也只能将整体的程序性能提高 0.1%。俗话说,好钢用在刀刃上.
参考文章:链接
也可以用关键词筛选
使用了sudo perf report 可以查看当前perf.data的数据,但是当你代码调用很多时候不好进行分析查看,这个时候我们就可以选择我们需要关注的重点信息查看,提高效率。例如以下的futex_wait:
选中之后,使用 --call-graph ,,,,callee --symbol-filter = 后面增加你需要筛选监控的类型就可以单独显示了。
sudo perf report --call-graph ,,,,callee --symbol-filter=futex_wait
这篇文章可以更多的帮助你理解filter:链接
perf diff进行两次record对比
我们多次perf record之后,当前路径下会有两个perf.data 和perf.data.old文件,分别是本次和上次record的记录,这个时候我们可以通过perf diff进行对比优化的结果。
sudo perf diff perf.data perf.data.old
介绍一些了perf细节使用的描绘,再给大家分享一个perf详细使用介绍的网址,大家对于perf介绍中有需要继续深入探索的部分,可以点击以下链接进行学习。
perf Examples 详细的使用介绍
链接:https://www.brendangregg.com/perf.html
Linux Perf commands命令介绍使用
链接:https://linuxhint.com/linux-perf-commands/
火焰图的制作
CPU 的性能,它可以将消耗 CPU 时间比较大的用户程序调用栈打印出来,并生成火焰图。通过分析火焰图的顶层的显示,我们就可以很直观的查看我们函数的性能情况了。
这个是自己ubuntu20系统做捕获的火焰图显示
x轴表示采样次数或者频率,如果一个函数在 x 轴占据的宽度越宽,就表示它被抽到的次数多,即执行的时间长。注意,x 轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的。
y 轴表示调用栈,每一层都是一个函数。调用栈越深,火焰就越高,顶部就是正在执行的函数,下方都是它的父函数。
火焰图就是看顶层的哪个函数占据的宽度最大。只要有"平顶"(plateaus),就表示该函数可能存在性能问题。
参考:链接1; 链接2
具体步骤:
1 首先,在 Ubuntu 安装 perf 工具:
2 再从github下载分析脚本
git clone https://github.com/brendangregg/FlameGraph.git
3 使用perf script工具对perf.data进行解析
perf script -i perf.data &> perf.unfold
生成火焰图通常的做法是将 perf.unfold 拷贝到本地机器,在本地生成火焰图
4 将perf.unfold中的符号进行折叠
FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded
5 最后生成svg图
FlameGraph/flamegraph.pl perf.folded > perf.svg
生成火焰图可以指定参数,–width 可以指定图片宽度,–height 指定每一个调用栈的高度,生成的火焰图,宽度越大就表示CPU耗时越多。
注 :如果svg图出现unknown函数,使用如下命令
sudo perf record -e cpu-clock --call-graph dwarf -p pid
范例:perf record -e cpu-clock -g -p 29713 --call-graph dwarf 使用--call-graph dwarf 之后record生成的perf.data很大,大家生成的时候要时刻注意设备剩余空间是否足够
实际测试范例
如图一段代码
main -> do_main -> foo -> bar
其中 foo 函数和 bar 各有一个for循环,用来表示代码时间运行消耗的cpu
#include <iostream>
#include <vector>
#include <string>
#include <unistd.h>
using namespace std;
void bar(){
// usleep(40*1000);
/* do something here */
for(int i=0;i< 4000;i++)
void foo(){
// usleep(60*1000);
for(int i=0;i< 5700;i++)
bar();
void do_main() {
foo();
int main(int argc,char** argv){
while(1)
do_main();
运行代码之后进行 top实时查看(因为我的设备默认都是sudo权限,所以以下命令都不用前缀sudo)
ps -xu | grep target
perf top -e cpu-clock -p 29713
发现 foo 占用 60%cpu时间,而bar占用40%时间,和for循环展示的大致一样
perf record -e cpu-clock -g -p 29713
ctrl + c停止记录,发现当前目录下保存了文件perf.data
使用report查看
perf report -i perf.data
对比两者差异,因为只是单纯记录两次,代码没有修改,所以没有差异
perf diff perf.data perf.data
perf script -i perf.data &> perf.unfold
FlameGraph/stackcollapse-perf.pl test_data/perf.unfold &> test_data/perf.folded
拷贝到主机端进行转换成火焰图
FlameGraph/flamegraph.pl test_data/perf.folded > test_data/perf.svg
大家可以看到这个cpu占用关系,火焰图的顶层是个大平层,说明这段代码cpu单个函数foo和bar占用率太高,这段代码优化空间很大。
这就是我自己的一些perf使用分享。如果大家有更好的想法和需求,也欢迎大家加我好友交流分享哈。
作者:良知犹存,白天努力工作,晚上原创公号号主。公众号内容除了技术还有些人生感悟,一个认真输出内容的职场老司机,也是一个技术之外丰富生活的人,摄影、音乐 and 篮球。关注我,与我一起同行。
‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧
【1】jetson nano开发使用的基础详细分享
【2】Linux开发coredump文件分析实战分享
【3】CPU中的程序是怎么运行起来的 必读
【4】cartographer环境建立以及建图测试
【5】设计模式之简单工厂模式、工厂模式、抽象工厂模式的对比
本公众号全部原创干货已整理成一个目录,回复[ 资源 ]即可获得。