【青训营】关于开发场景中使用pprof性能调优的个人理解 | 青训营笔记

160 阅读6分钟

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

上篇讲到了有关开发场景中的一些代码测试的环节,在确认了程序基本功能的正常使用以及能够顶住压力测试之后,就需要考虑到代码的运行性能了,这就是对于过去所写代码的二次开发,追求性能上的提高,这其中也会有很多涉及到的点可以分享,那么接下来就开始吧

开始交易 (2).gif

在正式介绍性能调优之前,有一个不得不提的点,就是在开发中,一个良好的代码规范以及良好的代码注释,可以非常有效地提高代码地后期可维护性,所以注释是必不可少的,在保证阐述清晰的前提下还应该做到注释精简,所以一般代码的注释中只需要给出关于方法、变量或者接口的作用、实现方式以及代码在什么样的情况会出错,这样在后续出问题的时候也可以很快的定位到出错的点。

调优原则

性能调优也需要一定的前提,在不违反这些原则的情况下,代码的调优才称得上是合格的,比如:

  1. 一切调优源于数据: 从数据出发而不是从理论出发,这样才能更精准快速地找到急需解决地性能问题
  2. 定位最大瓶颈: 从最大瓶颈处开始调优,这样的边际效应是最明显的并且感知也是最明显的
  3. 不要过早优化: 在前期很多的代码都还是开发阶段,需求的变更很容易导致代码的变化,这个时候的优化就会变得徒劳无功
  4. 不要过度优化: 这就是一个泛化的思想,过度的优化对于后续的功能增加调整是不友好的,所以这也是一种权衡

性能分析

关于性能调优,第一点就要先准确的追踪到性能降低的点上,Go中通过pprof工具可以帮助我们快速定位瓶颈,而性能分析主要围绕那么几个点展开:

  1. CPU、内存
  2. GoRoutine
  3. Mutex
  4. block

以Web开发为例,项目导入了pprof包之后就可以通过相应端口服务的/debug/pprof查询到相应的服务页面,也可以通过在终端输入:go tool pprof http://localhost:6060/debug/pprof/profile?seconds=10 来查看情况,seconds表示设定收集信息的时间区间,在执行了这个命令之后,就会收集到10秒内服务的运行相关信息,我们就从上面的几点来看,如何进行瓶颈定位即修改:

  1. CPU 类似Linux中的任务管理器查看方式,直接输入top即可获得相关信息

image.png 以这张图为例,其中的flat表示函数执行时间,cum表示当前函数以及调用函数的总用时,这里可以发现cum和flat是相等的,说明在函数执行过程中并没有发生函数调用,全部是执行的业务代码,而如果flat为0则表示这些方法本身没有什么业务代码,都是在调用其他的函数,我们可以输入list 方法名来获得方法的具体内容,看到方法每一步的执行耗时,定位到方法中最占用CPU资源的代码,然后进行调优

  1. 内存 有关内存方面最主要是查看堆中的资源分配情况,所以在终端通过以下代码执行:go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"会打开网页显示内存资源情况

image.png

view中给出了一系列查看性能情况的图形化界面,热力图和top是比较常用的两个view,通过热力图就能很明显的看到整个执行流程上,每一个方法的执行时间和先后次序,top就能看到最占资源的几项方法,后续定位到资源占用频繁的地方进行修改即可

这里的Sample中有四类参数,分别是申请的对象数量和大小以及正在使用的对象和大小,我们可以通过这些参数来看程序中是否存在一些对象的频繁创建和删除或者对象的大小过大等问题

  1. 协程、Mutex、block问题

剩余的问题和内存问题大同小异,所以类似于内存的查看方式,只需要将heap换成相应名称即可查看图形化界面进行定位

通过以上的排查基本可以找到最大的几个问题,但是也许还会存在一些问题,因为在pprof中统计时会忽略掉一些node,就需要我们在终端以CPU资源的查询方式来查看情况

以上就是pprof工具提供的性能测试方法,接下来我们来聊一聊pprof的实现原理:

pprof实现原理

  1. CPU CPU的监测比较简单,只需要在程序开始的时候向操作系统申请一个计时器,每隔一段时间,让操作系统向进程发送一个信号来记录CPU的情况,然后每隔一段时间,就将这些信息写到输出流中,进程结束后,就能获得相应的CPU资源信息

  2. 内存 内存信息的采样通过内存分配器在堆上的分配和释放内存来记录相应的大小和数量,一般根据每分配固定的大小就会记录一次,采样的标准就是上面提到的sample中给出的四类信息

  3. 线程、协程 关于协程的采样,则需要每隔一定的时间stop the world然后类似gc但是比gc快得多地遍历记录列表记录相关信息然后再start the world

  4. 阻塞和锁 在程序执行过程中,会记录下超过某一时长的阻塞信息以及抢夺锁的信息,并且还可以自定义固定的采样频率

以上就是pprof提供的代码性能测试工具,但再真正的开发场景中,仅针对自身代码的调优是远远不够的,我们还能从很多角度着手来进行优化,比如代码编译的时候能否对编译器进行优化,对于使用的Go的原生库能否进行性能优化,运行过程中的GC线程能否优化,如果再往上升,那就要考虑到整个服务架构是否还有值得提升的点,所以性能调优工作绝对不会像本文列举的那么简单,后续也会整理一些有关的信息,来完善整个性能调优的流程