go 调优之火焰图

火焰图(flame graphs)是一种程序函数调用栈深度和耗时比例直观可交互展示的工具。

一图胜千言

FlameGraph

如上图展示,火焰图展示程序中被频繁调用的函数和耗时占比。这边展示的是一章截图,原图是svg是可交互的,比如我们想具体看某个函数中子函数的情况你可以点击那个函数。结果如下

FlameGraph

在go生态中,早期我们经常会用go-torch来生成这样的图。从go1.11后 go tool pprof 就已经集成了这个功能,非常方便我们使用。

演示代码

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	_ "net/http/pprof"
	"time"
)

func fn1() {
	b := fn2()
	fmt.Println(fn3(b))
}

func fn2() (b []byte) {
	b, _ = json.Marshal(map[string]int{
		"a":  22,
		"bb": 333,
	})
	return
}

func fn3(b []byte) int {
	var m map[string]int
	json.Unmarshal(b, &m)

	if len(m) > 0 {
		return m["a"]
	}
	return 0
}

func main() {
	go func() {
		for {
			fn1()
			time.Sleep(1e8)
		}
	}()

	panic(http.ListenAndServe(":8080", nil))
}

如上面代码所示,需要在代码中引入_ "net/http/pprof" 然后需要起一个http服务 http.ListenAndServe(":8080", nil),就这样比较少的代码侵入。

运行

$ go run main.go

然后你可以在浏览器中访问,http://127.0.01:8080/debug/pprof/

看到如下的信息

/debug/pprof/

Types of profiles available:
Count	Profile
1	allocs
0	block
0	cmdline
5	goroutine
1	heap
0	mutex
0	profile
7	threadcreate
0	trace
...

ok 测试程序没问题了

生成火焰图

step 1

运行如下的命令,下载profile文件

go tool pprof http://127.0.0.1:8080/debug/pprof/profile -seconds 5

step 2

运行如下命令生成火焰图

go tool pprof -http=:8081 ~/pprof/pprof.main.samples.cpu.001.pb.gz

注意:这个时候可能会提升缺少 graphviz ubuntu和debian 使用 apt-get install graphviz 安装

用浏览器访问 http://localhost:8081/ui/flamegraph这个时候就能看到火焰图了。

调优过程

我们生成火焰图的目录是为了直观的发现程序中最耗时的函数,然后想办法去优化它, 优化后继续生成火焰图看是否有效果,然后再去发现下一个最耗时的函数,重复上面的步骤直到整个程序达到你满意的性能。