Profiling 101
A beginner's guide to profiling
以前也浅尝辄止地试图 profile 过,但是被一大堆概念和工具搞的头昏脑涨,全是问题:
- 什么是 profiling,这个词什么意思?
- CPU profiling 和 memory profiling 有什么区别?为什么都是火焰图?
- Instrument 又是什么意思?
- Golang 的 pprof package 和 pprof tool 有什么区别?
- Mac 的 XCode Instruments 可以比较傻瓜式地 profile,但是没有 flamegraph,怎么办?
然后平时要么没有需求用不到 profiling,要么遇到问题很急,就病急乱投医,各种工具瞎试一通,最后不了了之,也没搞懂在干什么。
这两天在尝试用 profiling 工具查内存问题,虽然最后问题没让我看出来,但是这回终于基本搞懂他们到底是在干什么了,趁热打铁水一篇博客记录一下。
本文不包括:如何根据 profiling 的结果来进行性能优化(这是最终目标,需要大量经验),profiling 的各种具体实现技术。
本文主要科普基本概念,理解 profiling 在干什么,这算是一切后续工作的前提。因此本文目标读者是像我一样可能听说过一些,但是没怎么上手搞过 profiling,对基本概念也不太理解的朋友。
TL; DR
Profiling 其实包括分开的两个步骤:
- 收集程序运行的信息
- 分析/展示信息
Flamegraph 只是一种数据可视化方法,属于 profiling 的第二步。它不包括第一步收集信息,profiling 也可以不用它来展示信息。
什么是 flamegraph
就算不太懂 profiling 原理或者实践,看到这样的火焰图也会觉得非常直观,基本上能猜个大概是什么意思。
这一定程度上导致对门外汉来说提到 profiling 和 flamegraph 就觉得是绑定在一起的两个概念,但实际上不是。
其实它只是一种可视化工具,只负责展示信息。它的 input 可以是任何长得像 stacktrace 的东西。它的主要优点也正是可以明了地看出 stacktrace 和占比。
brendangregg/FlameGraph 这个 repo 作者 Brendan Gregg 是 flamegraph 的发明者。这个 repo 里包含了一系列(perl 写的)flamegraph 工具。里面包括:
flamegraph.pl
是最重要的用来生成 flamegraph SVG 图片的工具stackcollapse-*.pl
系列可以把各种格式的 stacktrace 转换成flamegraph.pl
的 input 格式
所以实际上我手写一个 flamegraph.pl
的输入格式的东西,也可以生成一个 flamegraph。例如这样:
a 1
a;b 0
a;b;c 1
a;b;c;e 1.5
a;b;d 2
上面这个例子应该很好地解释了 flamegraph 的原理。每一行是一个函数的完整的调用栈,最后一个数字是它自己的(时间/内存/…)占用数量。
把 stacktrace 转换成 flamegraph
现在很多 profiling 工具应该都可以直接生成 flamegraph,如果不行的话,可以自己想办法弄个脚本转换一下。例如 XCode Instruments 其实可以导出(它里面叫 Deep Copy… 并且还需要先选中才好导)这样的 stacktrace 文本(内存,CPU 什么的都可以导) :
# Allocation
Bytes Used Count Symbol Name
9.75 KB 100.0% 56 _pthread_start
9.75 KB 100.0% 56 std::sys::unix::thread::Thread::new::thread_start::h8134a9cc26f143c2
...
# Time Profiler
Weight Self Weight Symbol Name
74.00 ms 100.0% 0 s thread_start
74.00 ms 100.0% 0 s _pthread_start
74.00 ms 100.0% 0 s std::sys::unix::thread::Thread::new::thread_start::h8134a9cc26f143c2
...
# CPU Profiler
Weight Self Weight Symbol Name
34.35 Mc 100.0% - thread_start
34.35 Mc 100.0% - _pthread_start
34.35 Mc 100.0% - std::sys::unix::thread::Thread::new::thread_start::h8134a9cc26f143c2
...
我想要把 Allocation(内存 profile)的结果弄成 flamegraph,发现没有现成的工具。上面的工具包里有一个 stackcollapse-instruments.pl
,实际上处理的是 Time Profiler 的格式:
<>;
foreach (<>) {
chomp;
/\d+\.\d+ (?:min|s|ms)\s+\d+\.\d+%\s+(\d+(?:\.\d+)?) (min|s|ms)\t \t(\s*)(.+)/ or die;
my $func = $4;
my $depth = length ($3);
$stack [$depth] = $4;
foreach my $i (0 .. $depth - 1) {
print $stack [$i];
print ";";
}
my $time = 0 + $1;
if ($2 eq "min") {
$time *= 60*1000;
} elsif ($2 eq "s") {
$time *= 1000;
}
printf("%s %.0f\n", $func, $time);
}
这个 perl 代码和正则表达式虽然都看不懂,但是可以目测感受一下就是先做个匹配,然后根据空格缩进的数量处理 stack,最后再把时间单位转换一下就行了。
祭出 regex101
(其实我也第一次用)鉴定一下正则表达式:
再回头结合代码中 $2, $3, $4
的用法,可以非常确信每个变量的含义。
然后想要转 Allocation 的格式的话,依葫芦画瓢小改一下正则表达式和单位就行了(可以结合 regex101 的 debug 功能,看正则匹配到哪儿挂了,相当好用)。注意 Allocation 的信息里默认不包括 Self Bytes,可以手动打开一下。
什么是 profiling
profiling 这个词的含义
翻了翻词典和 wiki 的解释:
the recording and analysis of a person's psychological and behavioral characteristics, so as to assess or predict their capabilities in a certain sphere or to assist in identifying a particular subgroup of people
Profiling, the extrapolation of information about something, based on known qualities
感觉是个非常 general 的词,但感觉差不多就是通过收集信息(观测)来分析推断的意思。常见其他用法包括心理学中的罪犯心理画像侧写,都差不多是一回事。
从第一个解释也可以看出,profiling 其实有两个步骤:收集信息和分析信息。例如 golang 自带的 pprof 包提供了多种方式启用 profiling:go test、HTTP 服务、手动在程序里启用,其实做的事情都是收集信息。以前我还搞不清楚它和 pprof tool 的关系,实际上后者是个分析、可视化工具,前者收集信息,收集成后者定义的输入格式。
收集信息
主要有两种收集信息的方式:
- Statistical sampling:probes the target program's call stack at regular intervals using operating system interrupts.
- Code instrumentation:either inject code into a binary file that captures timing information or by using callback hooks.
后者比前者更精确,但是 overhead 也更大。前面提到的 Mac 上的 XCode Instruments,想必就是以后面这种方式来收集信息的。
instrumenation 这个词的含义
Instrumentation a collective term for measuring instruments that are used for indicating, measuring and recording physical quantities. The term has its origins in the art and science of scientific instrument-making.
Scientific instruments 实际上指科学仪器,that are used for indicating, measuring and recording physical quantities。Instrumentation 可以指使用科学仪器。
那么在软件的语境下, instrumentation 就指往代码中加入一些“仪器”,从而可以观测到软件的行为。它的中文好像叫“插桩”,也挺形象,就是感觉不太符合原意。
插入软件里的东西可能包括:
- Collect profiling statistics:profiling 实际上就是 measuring dynamic program behaviors,非常像做科学实验
- Run-time checking:像 AddressSanitizer 这种也被算作是 instrumentation,感觉稍微有点词义扩大的感觉
不过这么说来,我感觉 sampling 在更广义的角度看也可以算是一种“instrumentation”……
分析/展示信息
分析除了 flamegraph,其他还有像下面这样的 graphviz 图等等。
也可以不可视化,就看一看一些统计信息,例如 TopN。
不同的收集工具接受不同的 input 格式,相应地做的事情也会不一样,例如有的分析工具可能还有把 debug symbol 转成 source code address 这一步,而像之前的 flamegraph 就是接受现成的处理好的(collapsed)stacktrace。pprof 的输入格式是一个 protobuffer 定义。
CPU profiling & memory profiling
CPU profiling 和 memory profiling 有什么区别?为什么都是火焰图?
现在看这个问题就很简单了,它们要做的事情都是收集信息、分析信息这两步,只是具体收集的方法不一样而已。例如 jemalloc 的 heap profile 就是把 memory dump 一坨下来,然后可以用它的 jeprof 工具来分析,包括转成火焰图的格式。
总结
我终于理解了一切,所以那然后该怎么看懂火焰图,定位解决性能问题呢?😭