简介

pprof是性能调试工具,可以生成类似火焰图、堆栈图,内存分析图等。

整个分析的过程分为两步:1. 导出数据,2. 分析数据。

导出数据

网页

两步,第一步,在引用中加上

第二步,在代码开始运行的地方加上

接着,我们访问http://127.0.0.1:6060/debug/pprof/,即可看到

  • allocs:查看过去所有内存分配的样本(历史累计)。
  • block:查看导致阻塞同步的堆栈跟踪(历史累计)。
  • cmdline: 当前程序的命令行的完整调用路径(从程序一开始运行时决定)。
  • goroutine:查看当前所有运行的 goroutines 堆栈跟踪(实时变化)。
  • heap:查看活动对象的内存分配情况(实时变化)。
  • mutex:查看导致互斥锁的竞争持有者的堆栈跟踪(历史累计)。
  • profile: 默认进行 30s 的 CPU Profiling,得到一个分析用的 profile 文件(从开始分析,到分析结束)。
  • threadcreate:查看创建新 OS 线程的堆栈跟踪(一般没啥用)。

注意,默认情况下是不追踪block和mutex的信息的,如果想要看这两个信息,需要在代码中加上两行:

上文的所有信息都是实时的,如果你刷新一下,是可以看到数字在变化的。此时如果点击蓝色的连接,可以看到一些协程的栈信息,这些信息并不容易阅读。如果想要更加清晰的数据,需要将信息保存下来,在本地进行分析。

profiletrace
profile
?debug=1

总之,目前这个页面最有用的数据就是直接看看协程数,看看同步数,看看堆和线程创建数。点进去的链接基本都没太强的可读性。操作到这里,能看到这个网页即可。接下来我们看看如果通过本地pprof工具进一步分析数据。

方法一(简单,但不推荐)

go tool pprof http://localhost:6060/debug/pprof/XXX

你可以添加一些参数,来辅助你直接分析数据,需要注意,参数都需要在url之前。比如:

-seconds=30-seconds

在运行完这个命令,下载好数据之后,会直接进入和pprof的交互式命令行。下面【分析数据】章节会仔细介绍该怎么用。

方法二(复杂,但推荐)

curl -o XXX-X.out http://localhost:6060/debug/pprof/XXX
?seconds=X

这样,我们就拥有了原始的数据文件,未来可以反复分析,而不用担心原始的数据文件找不到了。

go tool pprof XXX-X.out

test

编写一个test程序,可以更加精准地进行测试。同时一起把性能表现看了,棒!

和网页导出一样,test导出也拥有多个指标的查看方式,具体查看哪些指标,通过命令行的参数来配置。由于go test相比网页,更加定向,因此可以方便地来查看一些数据。这也一并列在这里。

可以看到,go test会把数据导出在特定的文件之中。之后分析数据就需要读取这些数据。

go tool pprof

代码

我们还可以在代码里主动把原始数据写到固定的文件里。代码很简单,主要分为两类,CPU的profile和其他。CPU的采样总是麻烦一些:

其他的,包括alloc、heap、mutex、block、goroutine、threadcreate,写法上都是一样的,只需要替换那个字符串就行了。

分析数据

http://127.0.0.1:6060/debug/pprof

运行上面任一一个命令之后,pprof会等待我们进一步的指示,我们来运行一下help看一看

topweb

文本

toptop5top20

其中一共有五个指标。这些指标的含义见下文“理解指标”。

图片一

要看图片,要先安装graphviz。

web

图片二(推荐)

另一种更加简便的看图的方式,运行如下命令

这将会启动一个web服务器,并自动打开一个网页。这个网页最上方有一个header,我们可以切换到火焰图、top、连线图。

SAMPLE
alloc_objects
alloc_space
inuse_objects
inuse_sapce

怎么看图,见下文“理解指标”、“理解连线图”、“理解火焰图”

理解指标

这一部分很重要!图中的参数也不会超过下面介绍的这些,只是调用关系更加清晰一点。

flat flat%

一个函数内的directly操作的物理耗时。例如

flat只会记录step2和step3的时间;flat%即是flat/总运行时间。内存等参数同理。

所有的flat相加即是总采样时间,所有的flat%相加应该等于100%。

flat一般是我们最关注的。其代表一个函数可能非常耗时,或者调用了非常多次,或者两者兼而有之,从而导致这个函数消耗了最多的时间。

如果是我们自己编写的代码,则很可能有一些无脑for循环、复杂的计算、字符串操作、频繁申请内存等等。

如果是第三方库的代码,则很可能我们过于频繁地调用了这些第三方库,或者以不正确的方式使用了这些第三方库。

cum cum%

相比flat,cum则是这个函数内所有操作的物理耗时,比如包括了上述的step1、2、3、4。

cum%即是cum的时间/总运行时间。内存等参数同理。

一般cum是我们次关注的,且需要结合flat来看。flat可以让我们知道哪个函数耗时多,而cum可以帮助我们找到是哪些函数调用了这些耗时的(flat值大的)函数。

sum%

其上所有行的flat%的累加。可以视为,这一行及其以上行,其所有的directly操作一共占了多少物理时间。

理解连线图

每个节点的信息包括了包名、函数名、flat、flat%、cum、cum%

节点的颜色越红,其cum和cum%越大。其颜色越灰白,则cum和cum%越小。

节点越大,其flat和flat%越大;其越小,则flat和flat%越小

线条代表了函数的调用链,线条越粗,代表指向的函数消耗了越多的资源。反之亦然。

线条的样式代表了调用关系。实线代表直接调用;虚线代表中间少了几个节点;带有inline字段表示该函数被内联进了调用方(不用在意,可以理解成实线)。

对于一些代码行比较少的函数,编译器倾向于将它们在编译期展开从而消除函数调用,这种行为就是内联。

理解火焰图

火焰图的横向长度表示cum,相比下面超出的一截代表flat。

火焰图可以进行点击

参考资料