go tool pprof 工具是用于分析由 runtime/pprof
包 或 net/http/pprof
包产生的profile数据,完整的帮助文档在 https://github.com/google/pprof/blob/main/doc/README.md ,pprof
工具支持的参数很多,可以用命令 go tool pprof --help
来查看全部参数列表,今天主要来说下 -base
和 -diff_base
这两个参数, 因为文档对这两个参数的描述比较晦涩,难以理解这两个参数之间的区别。
-diff_base source Source of base profile for comparison
-base source Source of base profile for profile subtraction
pprof can subtract one profile from another, provided the profiles are of compatible types (i.e. two heap profiles). pprof has two options which can be used to specify the filename or URL for a profile to be subtracted from the source profile:
-diff_base= profile: useful for comparing two profiles. Percentages in the output are relative to the total of samples in the diff base profile.
-base= profile: useful for subtracting a cumulative profile, like a golang block profile, from another cumulative profile collected from the same program at a later time. When comparing cumulative profiles collected on the same program, percentages in the output are relative to the difference between the total for the source profile and the total for the base profile.
The -normalize flag can be used when a base profile is specified with either the -diff_base or the -base option. This flag scales the source profile so that the total of samples in the source profile is equal to the total of samples in the base profile prior to subtracting the base profile from the source profile. Useful for determining the relative differences between profiles, for example, which profile has a larger percentage of CPU time used in a particular function.
When using the -diff_base option, some report entries may have negative values. If the merged profile is output as a protocol buffer, all samples in the diff base profile will have a label with the key “pprof::base” and a value of “true”. If pprof is then used to look at the merged profile, it will behave as if separate source and base profiles were passed in.
When using the -base option to subtract one cumulative profile from another collected on the same program at a later time, percentages will be relative to the difference between the total for the source profile and the total for the base profile, and all values will be positive. In the general case, some report entries may have negative values and percentages will be relative to the total of the absolute value of all samples when aggregated at the address level.
这两个参数都是用于多个profile文件之间的对比,文档中关于这两个参数的描述翻译过来:
比较profile文件
pprof
工具可以从一个profile文件中减去另一个profile文件,前提是这两个profile文件类型是兼容的(比如两个都是heap profile文件)。pprof有两个选项,用于指定需要减去的这个profile文件的文件名或URL:
-diff_base=profile
:用于比较两个profile文件。计算出的百分比是基于diff_base 参数指定的这个profile文件中的采样指标之和。
-base=profile
:用于累计指标类型的profile文件(比如golang的block profile),适用于将同一个程序后采集的profile文件减去先采集的profile文件的场景。当对比在同一个程序上收集的累计指标类型的profile文件时,展示的百分比是基于当前采集的profile文件中的采样指标之和与之前采集的profile文件中采样指标之和之间的差值。
当用-diff_base
或-base
参数指定基准profile文件时,可以加上-normalize
选项,加上该选项后,在计算当前profile文件与基准profile文件的采样和差值之前,会把当前profile文件的采样数据全部乘以一个固定的百分比(用基准profile采样和除以当前profile采样和得到),使得当前profile的采样数据之和与基准profile采样和相等。通常用于确定两个profile文件之间的相对差异,例如,用于比较哪个profile在特定函数中消耗的CPU时间占比更高。
当使用-diff_base
选项时,某些指标可能会存在负值。如果合并后的profile文件以protocol buffer
格式输出,则基准profile文件中的所有采样都会加上pprof::base
的Label,Label的值为“true”。如果随后使用pprof
工具来查看合并之后的profile文件,那么它的表现和同时传入合并前的原始profile文件和基准profile文件保持一致。
当使用-base
选项从同一个程序上后收集的一个累计指标类型的profile文件中减去一个之前收集的同类型的profile文件时,百分比将相对于后收集的profile文件的采样指标总和与先收集的profile文件的采样指标总和之间的差值,并且所有值都将为正数(因为累计指标是随着时间在不断增长的,后采集的值不会比先采集的值要小)。一般来说,有些指标可能会存在负值,这时当在地址级别进行聚合时,百分比将相对于所有采样指标的绝对值之和。
总结下来就是,两个参数都是用于计算当前 profile文件减去基准profile文件所获得的差值,用这个差值生成一个新的profile文件,区别在于计算这个新生成的profile文件每个采样指标的占比时,-base
是基于基准profile文件的指标和(所以百分比可能大于100%),而 -diff_base
则是基于差值profile文件的指标和(也就是这个新生成的profile文件本身的指标和,所以每个采样的指标占比不会超过100%)。
比如我这里由同一个程序前后生成了两个profile文件: profile001.pb.gz
和 profile002.pb.gz
,现在分别用 -base
和 -diff_base
参数生成一个差集profile:
$ pprof -proto -output gen_by_base_opt.pb.gz -base profile001.pb.gz profile002.pb.gz
Generating report in gen_by_base_opt.pb.gz
$ pprof -proto -output gen_by_diff_base_opt.pb.gz -diff_base profile001.pb.gz profile002.pb.gz
Generating report in gen_by_diff_base_opt.pb.gz
$ ll
total 48
-rw-r--r-- 1 zy staff 2839 5 18 19:42 gen_by_base_opt.pb.gz
-rw-r--r-- 1 zy staff 4772 5 18 19:42 gen_by_diff_base_opt.pb.gz
-rw-r--r-- 1 zy staff 3092 5 17 16:43 profile001.pb.gz
-rw-r--r-- 1 zy staff 4504 5 17 16:59 profile002.pb.gz
分别计算出这两个差集的指标和:
package main
import (
"fmt"
"github.com/google/pprof/profile"
"log"
"os"
)
func sumProfile(file string) map[string]int64 {
f, err := os.Open(file)
if err != nil {
log.Fatal(err)
}
defer f.Close()
pprof, err := profile.Parse(f)
if err != nil {
log.Fatal(err)
}
sums := make(map[string]int64, len(pprof.SampleType))
for _, sample := range pprof.Sample {
for i, v := range sample.Value {
sums[pprof.SampleType[i].Type] += v
}
}
return sums
}
func main() {
var sum map[string]int64
sum = sumProfile("./profile001.pb.gz")
fmt.Println("sum of profile001.pb.gz: ")
keys := make([]string, 0, len(sum))
for k := range sum {
keys = append(keys, k)
}
for _, k := range keys {
fmt.Println(k, " --> ", sum[k])
}
fmt.Println()
sum = sumProfile("./profile002.pb.gz")
fmt.Println("sum of profile002.pb.gz: ")
for _, k := range keys {
fmt.Println(k, " --> ", sum[k])
}
fmt.Println()
sum = sumProfile("./gen_by_base_opt.pb.gz")
fmt.Println("sum of gen_by_base_opt.pb.gz: ")
for _, k := range keys {
fmt.Println(k, " --> ", sum[k])
}
fmt.Println()
sum = sumProfile("./gen_by_diff_base_opt.pb.gz")
fmt.Println("sum of gen_by_diff_base_opt.pb.gz: ")
for _, k := range keys {
fmt.Println(k, " --> ", sum[k])
}
fmt.Println()
}
执行结果:
sum of profile001.pb.gz:
alloc_objects --> 26137
alloc_space --> 31409035
inuse_objects --> 3914
inuse_space --> 3148768
sum of profile002.pb.gz:
alloc_space --> 98663649
inuse_objects --> 9397
inuse_space --> 4771620
alloc_objects --> 51635
sum of gen_by_base_opt.pb.gz:
alloc_objects --> 25498
alloc_space --> 67254614
inuse_objects --> 5483
inuse_space --> 1622852
sum of gen_by_diff_base_opt.pb.gz:
alloc_objects --> 25498
alloc_space --> 67254614
inuse_objects --> 5483
inuse_space --> 1622852
可以看出,gen_by_base_opt.pb.gz
和 gen_by_diff_base_opt.pb.gz
的各项指标和等于 profile002.pb.gz
值减去 profile001.pb.gz
。
通过 pprof
工具来查看生成的差集profile:
$ pprof -http=: -base=profile001.pb.gz profile002.pb.gz
Serving web UI on http://localhost:57739
$ pprof -http=: -diff_base=profile001.pb.gz profile002.pb.gz
Serving web UI on http://localhost:57845
对比两幅火焰图可以看出两幅图除了百分比之外基本没有差别,并且从右上角可以看出这里展示的是 alloc_space
指标数据,而 64.14MB
正好就是我们上面生成的profile文件gen_by_base_opt.pb.gz
和 gen_by_diff_base_opt.pb.gz
的 alloc_space
指标和(67254614 / 1024 / 1024 = 64.13899803161621)。
而百分比之所以不同,是因为计算百分比的基数不同,-base
是以计算出的差集本身作为基准值计算百分比,而-diff_base
则是以指定的base profile,也就是这里的 profile001.pb.gz
指标值作为基准进行计算 ( 67254614 / 31409035 = 2.141250566914902)。