pprof性能分析

本文最后更新于:6 months ago

pprof 性能分析

benchmark(基准测试) 可以度量某个函数或方法的性能,也就是说,如果我们知道性能的瓶颈点在哪里,benchmark 是一个非常好的方式。但是面对一个未知的程序,如何去分析这个程序的性能,并找到瓶颈点呢?

pprof 就是用来解决这个问题的。pprof 包含两部分:

  • 编译到程序中的 runtime/pprof
  • 性能剖析工具 go tool pprof

windows本地查看腾讯云云服务器上的pprof测试结果,只需远程安装:

sudo yum install graphviz

一、CPU性能分析

启动 CPU 分析时,运行时(runtime) 将每隔 10ms 中断一次(为什么是10ms呢?后面会提到),记录此时正在运行的协程(goroutines) 的堆栈信息。启动 CPU 分析时,运行时(runtime) 将每隔 10ms 中断一次,记录此时正在运行的协程(goroutines) 的堆栈信息。程序运行结束后,可以分析记录的数据找到最热代码路径(hottest code paths)。

编译器热路径是编译器中的代码执行路径,大部分执行时间都花在了这些路径上,并且可能会非常频繁地执行这些路径。

一个函数在性能分析过程中出现的次数越多,说明执行该函数的代码路径(code path)花费的时间占总运行时间的比重越大。

具体实现方式

在main函数之前添加这两句,可以将性能数据输出到cpu.pprof

f, _ := os.OpenFile("cpu.pprof", os.O_CREATE|os.O_RDWR, 0644)
defer f.Close()
pprof.StartCPUProfile(f)	//源码中提到:当频率大于500Hz,可能会影响本身的效率,而经过测试,最适合的频率是100Hz,不会影响且会提供有价值的数据。
defer pprof.StopCPUProfile()

对数据及进行分析:

$ go tool pprof -http=127.0.0.1:6060 cpu.pprof

之后会自动打开浏览器,如下,可以通过不同的方式查看结果:

image-20220731201857858

也可以直接通过命令行来分析:

[root@VM-16-2-centos pprof-test]# go tool pprof cpu.pprof
File: main
Type: cpu
Time: Jul 31, 2022 at 3:58pm (CST)
Duration: 17.34s, Total samples = 17.25s (99.46%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 17.18s, 99.59% of 17.25s total
Dropped 17 nodes (cum <= 0.09s)
      flat  flat%   sum%        cum   cum%
    17.18s 99.59% 99.59%     17.21s 99.77%  main.bubbleSort (inline)
         0     0% 99.59%     17.22s 99.83%  main.main
         0     0% 99.59%     17.22s 99.83%  runtime.main
(pprof) top -cum
Showing nodes accounting for 17.18s, 99.59% of 17.25s total
Dropped 17 nodes (cum <= 0.09s)
      flat  flat%   sum%        cum   cum%
         0     0%     0%     17.22s 99.83%  main.main
         0     0%     0%     17.22s 99.83%  runtime.main
    17.18s 99.59% 99.59%     17.21s 99.77%  main.bubbleSort (inline)
(pprof) help
  Commands:
    callgrind        Outputs a graph in callgrind format
    comments         Output all profile comments
    disasm           Output assembly listings annotated with samples
    dot              Outputs a graph in DOT format
    eog              Visualize graph through eog
    evince           Visualize graph through evince
    gif              Outputs a graph image in GIF format
    gv               Visualize graph through gv
    kcachegrind      Visualize report in KCachegrind
    list             Output annotated source for functions matching regexp
    pdf              Outputs a graph in PDF format
    peek             Output callers/callees of functions matching regexp
    png              Outputs a graph image in PNG format
    proto            Outputs the profile in compressed protobuf format
    ps               Outputs a graph in PS format
    raw              Outputs a text representation of the raw profile
    svg              Outputs a graph in SVG format
    tags             Outputs all tags in the profile
    text             Outputs top entries in text form
    top              Outputs top entries in text form
    topproto         Outputs top entries in compressed protobuf format
    traces           Outputs all profile samples in text form
    tree             Outputs a text rendering of call graph
    web              Visualize graph through web browser
    weblist          Display annotated source in a web browser
    o/options        List options and their current values
    q/quit/exit/^D   Exit pprof

二、内存分析

内存性能分析(Memory profiling) 记录堆内存分配时的堆栈信息,忽略栈内存分配信息。

内存性能分析启用时,默认每1000次采样1次,这个比例是可以调整的。因为内存性能分析是基于采样的,因此基于内存分析数据来判断程序所有的内存使用情况是很困难的。

具体实现方式

需要导入"github.com/pkg/profile"然后在main函数之前添加:

defer profile.Start(profile.MemProfile, profile.MemProfileRate(1)).Stop()

对数据及进行分析:

go tool pprof -http=127.0.0.1:6060 /tmp/profile2738765321/mem.pprof
image-20220731204427226

三、针对web程序

针对一直运行的后台服务,比如 web 应用或者分布式应用,我们可以使用 net/http/pprof 库,它能够在应用提供 HTTP 服务时进行分析。

pprof 采集后台服务,如果使用了默认的 http.DefaultServeMux,通常是代码直接使用 http.ListenAndServe(“0.0.0.0:8000”, nil),这种情况则比较简单,只需要导入包即可。

import (
    _ "net/http/pprof"
)

注意该包利用下划线"_"导入,意味着我们只需要该包运行其init()函数即可,如此该包将自动完成信息采集并保存在内存中。

如果你使用自定义的 ServerMux复用器,则需要手动注册一些路由规则:

r.HandleFunc("/debug/pprof/", pprof.Index)
r.HandleFunc("/debug/pprof/heap", pprof.Index)
r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
r.HandleFunc("/debug/pprof/profile", pprof.Profile)
r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
r.HandleFunc("/debug/pprof/trace", pprof.Trace)

这些路径分别表示:

  • /debug/pprof/profile:访问这个链接会自动进行 CPU profiling,持续 30s,并生成一个文件供下载,可以通过带参数?=seconds=60进行60秒的数据采集。
  • /debug/pprof/block:Goroutine阻塞事件的记录。默认每发生一次阻塞事件时取样一次。
  • /debug/pprof/goroutines:活跃Goroutine的信息的记录。仅在获取时取样一次。
  • /debug/pprof/heap: 堆内存分配情况的记录。默认每分配512K字节时取样一次。
  • /debug/pprof/mutex: 查看争用互斥锁的持有者。
  • /debug/pprof/threadcreate: 系统线程创建情况的记录。 仅在获取时取样一次。

在Gin框架中使用pprof

可以导入包go get github.com/gin-contrib/pprof,之后就可以看到一些新的接口生成:

image-20220731222053015

profile文件保存在/root/pprof/pprof.api.samples.cpu.001.pb.gz,之后直接使用上述的分析方式即可在windows界面打开:

[root@VM-16-2-centos api]# go tool pprof -http=127.0.0.1:6060 /root/pprof/pprof.api.samples.cpu.001.pb.gz
Serving web UI on http://127.0.0.1:6060

关于火焰图的基本理解方法:

image-20220731222622729

参考资料

[1] https://geektutu.com/post/hpg-pprof.html

[2] https://bbs.huaweicloud.com/blogs/289818

[3] https://bytedancecampus1.feishu.cn/docs/doccn4F3nKRdd9Uq7RGl6JPPFWg#

[4] http://liumurong.org/2019/12/gin_pprof/


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!