pprof性能调优实战|青训营笔记

简介: 本次课程讲解大体分为两个部分:讲述高质量编码的规范和性能调优实战,这篇文章侧重于复现使用pprof工具性能调优的实践过程,而第一部分则希望大家各自归纳总结。

这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记

前言

本次课程讲解大体分为两个部分:讲述高质量编码的规范和性能调优实战,这篇文章侧重于复现使用pprof工具性能调优的实践过程,而第一部分则希望大家各自总结归纳。

pprof性能调优实战

项目地址:https://github.com/wolfogre/go-pprof-practice

这个项目中作者埋藏了很多5个性能炸弹,故意降低了项目的性能,提高资源占用量,我们将使用pprof工具定位这些问题,并完成性能调优。

克隆项目之后,使用:go mod init go mod init github.com/wolfogre/go-pprof-practice,初始化go mod,之后就可以运行程序。

pprof预览

程序运行之后,浏览器输入http://localhost:6060/debug/pprof/,可以查看pprof监控页面。

image-20220511175314706

allocs表示分配了19次内存,block表示有3次阻塞,goroutine表示有55个协程正在运行,heap为堆上内存使用,mutex表示有1个锁竞争,threadcreate表示有7个线程创建。

通过这个面板可以先大致了解程序运行情况,并定位可能出现性能问题的位置,下面将具体排查各个问题。

优化CPU占用

我们先来看一下在活动监视器中该程序CPU占用情况:45.3%恐怖!

image-20220511180228867

在Terminal窗口输入:go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10",先pprof工具先采集10秒的数据到文件中,然后再开始接受命令去展示性能分析的结果。

image-20220511131445474

输入top命令,查看占用CPU资源较多的调用。

image-20220511131727662

  • flat:当前函数本身的执行耗时(flat == 0 则函数中只有其他函数的调用)
  • flat%:flat占CPU总时间的比例
  • sum%:上面每一行的flat%的总和
  • cum:指当前函数本身加上其调用函数的总耗时(flat == cum 则函数中没有调用其他函数)
  • cum%:cum占CPU总时间的比例

很明显github.com/wolfogre/go-pprof-practice/animal/felidae/tiger.(*Tiger).Eat的调用占用了绝大部分CPU。

定位到Eat方法占用资源最多之后,使用list Eat命令排查看出问题的代码具体是什么内容:

这里看到for循环执行了3.8s的时间,是问题所在。

image-20220511181114147

接着输入web命令,可以实现调用关系可视化,这个命令前提是需要下载graphviz,一个图形化显示调用链的工具(在后续的性能调优过程中graphviz被多次使用,这里建议先完成安装~)。

image-20220511162732237

image-20220511181314573

定位到问题之后,注释掉Tiger的Eat方法的耗时操作。

image-20220511133531958

然后查看mac活动监视器,发现CPU的占用明显降低了到了0.2%。

image-20220511181651918

在终端输入exit退出pprof的工作模式。

优化内存占用

CPU占用问题解决了,但是程序内存占用依旧过高。

image-20220511181755800

再次在终端输入go tool pprof http://localhost:6060/debug/pprof/heap命令,这次结尾是heap,表示查看堆内存的占用情况,然后依旧配合输入toplist

image-20220511182459963

可以看到,这个for循环不断给m.buffer追加1MB的内容,并设定上限1GB,暂未看到内存的释放,所以推测这就是程序占用高内存的原因。

image-20220511182531692

接着输入web,依旧会弹出一个可视化页面展示内存占用情况。

image-20220511182905185

和上面解决CPU占用问题一样,这里注释掉无效分配内存的代码。

image-20220511183302163

重启项目再看一下活动监视器,看到内存占用过大的问题也解决了。

image-20220511183406034

排查协程问题

在最上面那张pprof状态监听图上,你可能还记得协程数量有55,而此刻再刷新一下,竟然有105,所以怀疑出现了问题,下面排查一下。

image-20220511184207065

终端输入exit退出内存的监测,重新输入go tool pprof http://localhost:6060/debug/pprof/goroutine,开始监测协程的分配情况,输入top、找到占用最高的方法调用github.com/wolfogre/go-pprof-practice/animal/canidae/wolf.(*Wolf).Drink.func1

image-20220511184646802

执行list Drink,定位到了Drink方法中每调用一次Drink()都会创建10个协程,然后睡眠30秒结束。

image-20220511184736309

再照常使用一下web命令,可以看到可视化的协程的创建情况。

image-20220511185309333

依旧是将这部分循环创建协程的无意义代码注释掉,重启程序,再次查看pprof监测面板,协程的数量降低到了6,优化完成。

image-20220511190112763

排查锁争用

这是排查的第四个性能炸弹,终端输入go tool pprof http://localhost:6060/debug/pprof/mutex,接着输入top、list XXX、web定位到出现锁争用的代码,web图我就不放了。

image-20220511191012488

这里主协程调用了Lock获得锁,然后开启子协程在一秒后释放锁,主协程第二次调用Lock方法的功能是如果子协程没有调用unLock方法,则主协程依旧会阻塞在这里,等待m锁的释放,贴一段Lock方法的源码,这里注释给出了详细解释。

image-20220511191806185

关于互斥锁其实很常用,之前写作业一二都用到了,我们无法完全断定用到了锁就会被捕获为锁竞争情况,需要额外分析,这里就当作是确实发生了锁竞争,注释掉这段代码,重启项目,然后查看pprof监测面板。

image-20220511193701227

排查阻塞问题

第五个性能优化的点,因为上图中block的数量是2,表示存在阻塞,那么让我们来排查一下,在终端输入命令go tool pprof http://localhost:6060/debug/pprof/block,然后输入top、list Pee、web命令,这里web的监测图依旧没有给出。

image-20220511192726943

这里阻塞的原因是第39行代码,<-time.After实现了延时一秒的功能,那么就会因为这个人为的设定,每次多阻塞一秒,这里注释掉这部分代码,重启项目,观察pprof监测面板。

image-20220511193812023

这里看到block还有1,而且刚才toplist也只是定位到了一个block,那这个1是阻塞在哪里了?我们直接在pprof面板上点击block,可以看到这部分block是被隐藏起来的,通过观察发现是和http/server相关,推测是程序正常工作需要的阻塞。

image-20220511194229117

火焰图展示

此刻这个项目的5个性能炸弹都被我们修复了,但是课程中提到的Flame-Graph让人十分好奇,这里体验一下。依旧是在终端输入go tool pprof http://localhost:6060/debug/pprof/profile命令。

image-20220511200319042

前面说了,pprof监测工具是将数据存入一个文件再进行性能分析,这里定位到这个文件后,exit退出终端,然后重新执行go tool pprof -http localhost:3030 /Users/yucong/pprof/pprof.samples.cpu.004.pb.gz命令,这里的localhost:3030是你自己指定的网页监测工具访问端口。

然后就能访问在线的监测工具了,也可以切换各种展示方式。关于火焰图,方块的大小代表了占用 CPU 使用的长短,纵向表示调用链,颜色好像没特别的意义,只是配色很像火焰🔥。

经过测试,下面的命令可以一步直接打开在线监测平台:(将block替换成mutex、goroutine等等都可以)

`go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/block"
`

image-20220511200622003

结束语

image-20220511194514333

pprof的实战通过监测一个包含5个性能炸弹的项目,逐步修复5个性能漏洞,学习这部分的知识将为后续编写可靠的项目代码打下坚实基础~

关注微信公众号【程序员白泽】,将同步青训营期间每一课的笔记和作业。

相关文章
|
7月前
|
SQL 分布式计算 监控
Sqoop数据迁移工具使用与优化技巧:面试经验与必备知识点解析
【4月更文挑战第9天】本文深入解析Sqoop的使用、优化及面试策略。内容涵盖Sqoop基础,包括安装配置、命令行操作、与Hadoop生态集成和连接器配置。讨论数据迁移优化技巧,如数据切分、压缩编码、转换过滤及性能监控。此外,还涉及面试中对Sqoop与其他ETL工具的对比、实际项目挑战及未来发展趋势的讨论。通过代码示例展示了从MySQL到HDFS的数据迁移。本文旨在帮助读者在面试中展现Sqoop技术实力。
521 2
|
2月前
|
Java 应用服务中间件 程序员
JVM知识体系学习八:OOM的案例(承接上篇博文,可以作为面试中的案例)
这篇文章通过多个案例深入探讨了Java虚拟机(JVM)中的内存溢出问题,涵盖了堆内存、方法区、直接内存和栈内存溢出的原因、诊断方法和解决方案,并讨论了不同JDK版本垃圾回收器的变化。
33 4
|
7月前
|
IDE 测试技术 Go
【字节跳动青训营】后端笔记整理-3 | Go语言工程实践之测试
用于验证已经修改或新增功能后,软件的既有功能是否受到影响。
125 2
|
存储 安全 编译器
【C++】C++入门必备知识详细讲解
【C++】C++入门必备知识详细讲解
216 0
|
7月前
|
机器学习/深度学习 图计算 异构计算
|
安全 编译器 C语言
C++入门必备知识
C++入门必备知识
59 0
|
小程序 Java 容器
“马士兵老师”纯手写2022版多线程与高并发PDF学习手册
这份2022版多线程与高并发的学习手册,是基于同学们2022上半年面试遇到的问题呀以及当下最流行的技术进行的更新升级的版本讲解,从最基础的线程、纤程、协程等等开始讲起,其中还有高并发调优的经验讲解,从基础到精通一站式为大家解决完。
|
存储 安全 编译器
【C++入门必备知识】
库里的名称我们无法改变,那我们可以将自己定义的名称放在命名空间里,当需要时指令访问即可。 那怎么定义命名空间呢?
87 0