Go性能优化工具pprof | 青训营笔记

907 阅读5分钟

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

在生产环境中,偶尔会发生 Go 程序 CPU 暴增的现象,排除某时段并发大的场景外,通过监控面板看不到程序是因为什么原因导致的,Go 语言原生就提供了工具 pprof,Google 对于 pprof 的解释就是一个用于可视化和分析数据的工具。

通过使用 Go pprof 可以对程序的 CPU性能、内存占用、Goroutine wait share resource、mutex lock 做剖面分析,我们可以使用该工具收集运行时的程序性能指标,从而分析出程序中是否由于代码编写不合理导致存在不合理的资源占用情况,从而对程序进行优化用来提升其性能。

一、Go性能调优

1、需要优化内容
  • 一般常规内容:

    • cpu:程序对cpu的使用情况 - 使用时长,占比等
    • 内存:程序对cpu的使用情况 - 使用时长,占比,内存泄露等。如果在往里分,程序堆、栈使用情况
    • I/O: IO的使用情况 - 哪个程序IO占用时间比较长
  • golang 程序中:

    • goroutine: go的协程使用情况,调用链的情况
    • goroutine leak :goroutine泄露检查
    • go dead lock:死锁的检测分析
    • data race detector:数据竞争分析,其实也与死锁分析有关
2、调优方法工具
  • 比如 linux 中 cpu 性能调试,工具有 top,dstat,perf 等。
  • golang 性能调试优化方法:

    • Benchmark基准测试,对特定代码的运行时间和内存信息等进行测试
    • Profiling程序分析,程序的运行画像,在程序执行期间,通过采样收集的数据对程序进行分析
    • Trace跟踪,在程序执行期间,通过采集发生的事件数据对程序进行分析

二、pprof

  • pprof 是 golang 官方提供的性能调优分析工具,可以对程序进行性能分析,并可视化数据,看起来相当的直观。

  • 对下面 golang 中 2 个监控性能的包 pprof 进行运用:

    • runtime/pprof:采集程序运行数据进行性能分析,一般用于后台工具型应用,这种应用运行一段时间就结束。
    • net/http/pprof:对 runtime/pprof 的二次封装,一般是服务型应用。比如 web server ,它一直运行。这个包对提供的 http 服务进行数据采集分析。
  • 上面的 pprof 开启后,每隔一段时间就会采集当前程序的堆栈信息,获取函数的 cpu、内存等使用情况。通过对采样的数据进行分析,形成一个数据分析报告。
  • pprof 以 profile.proto 的格式保存数据,然后根据这个数据可以生成可视化的分析报告,支持文本形式和图形形式报告。(以下只讨论net/http/pprof)
1、net/http/pprof
import _ "net/http/pprof"

650581-20210330181541666-1171115210.png

名称url说明
allocs$host/debug/pprof/allocs?debug=1过去所有内存抽样情况
block$host/debug/pprof/block?debug=1同步阻塞时程序栈跟踪的一些情况
heap$host/debug/pprof/heap?debug=1活动对象的内存分配情况
mutex$host/debug/pprof/mutex?debug=1互斥锁持有者的栈帧情况
profile$host/debug/pprof/profilecpu profile,点击时会得到一个文件,然后可以用 go tool pprof 命令进行分析
threadcreate$host/debug/pprof/threadcreate?debug=1创建新 OS 线程的堆栈跟踪情况
trace$host/debug/pprof/trace当前程序执行的追踪情况,点击时会得到一个文件,可以用 go tool trace 命令来分析这个文件
1.1、命令行交互分析

go tool pprof http://localhost:8090/debug/pprof/profile?seconds=70

  • 参数 seconds = 70:进行 70s 的数据样本采集,这个参数可以根据实际情况调整

650581-20210330181600702-127232672.png

650581-20210329122145607-2007829916.png

字段说明
flat当前函数占用 cpu 耗时
flat %当前函数占用 cpu 耗时百分比
sum%函数占用 cpu 时间累积占比,从小到大一直累积到 100%
cum当前函数加上调用当前函数的函数占用 cpu 的总耗时
%cum当前函数加上调用当前函数的函数占用 cpu 的总耗时占比
  • 从字段数据我们可以看出哪一个函数比较耗费时间,就可以对这个函数进一步分析。 分析用到的命令是 list

list 命令:可以列出函数最耗时的代码部分,格式:list 函数名

650581-20210329122206105-453790225.png

1.2、图形可视化分析
  • 用 http 测试工具模拟用户访问

go get -u github.com/rakyll/hey

  • 安装完成后,进行 http 测试:

hey -n 1000 http://localhost:8090/pprof-test

  • 同时开启另一终端执行命令:

go tool pprof http://localhost:8090/debug/pprof/profile?seconds=120

  • 如果要查看函数最耗时部分代码,可以用 list cyclenum 命令查看。
  • 若要生成一张图片,就输入 web 命令生成SVG图片
1.3、web 可视化
  • 执行命令:

go tool pprof -http=":8080" http://localhost:8090/debug/pprof/profile

  • 同时开启另一终端执行测试命令:

hey -n 200 -q 5 http://localhost:8090/pprof-test

650581-20210330181955303-144225281.png

1.4、pprof 的采样过程
  • pprof会根据每秒的数据量去设置时间间隔收集数据,默认参数是100,意味着pprof会在每秒收集100次数据,也就是10毫秒一次。可以通过调用StartCPUProfile函数来开启pprof。
  • 这个流程会自动在每个运行的线程上创建一个定时器,允许GO以特定的时间间隔去收集数据(只分析了当前运行的线程)
  • 当Go调度器调度了一个goroutine到当前线程运行时,其它的线程也将会被实时跟踪。
  • 接下来,分析数据会在每个特定时间间隔存储到缓冲区中
  • 实际上,数据是由gsignal触发存储的,gsignal是一个处理输入信号的goroutine。由定时器在每个时间间隔内发出信号。
1.5、数据收集
  • 定时器设置完成后,一旦产生缓存,pprof需要去收集数据并且生成报告,这个流程需要一个单独的goroutine每100毫秒执行一次去收集格式化数据。
  • 关于收集的数据,Go生成了追溯来获取函数调用,并用它来格式化内联调用。
  • 流程处理完成以后,会有个专门的goroutine将报告转储到文件中,使其可用于可视化
参考链接