Go性能优化主要方面
//CPU性能分析
//开启
pprof.StartCPUProfile(w io.Writer)
//关闭
pprof.StopCPUProfile()
//示例
var isCPUPprof bool
flag.BoolVar(&isCPUPprof, "cpu", false, "turn cpu pprof on")
flag.Parse()
if isCPUPprof {
file, err := os.Create("./cpu.pprof")
if err != nil {
fmt.Printf("create cpu pprof failed, err:%v\n", err)
return
}
pprof.StartCPUProfile(file)
defer pprof.StopCPUProfile()
}
//使用go tool pprof 工具进行CPU性能分析
//内存性能优化;记录程序的堆栈信息
pprof.WriteHeapProfile(w io.Writer)
//示例
var isMemPprof bool
flag.BoolVar(&isMemPprof, "mem", false, "turn mem pprof on")
flag.Parse()
if isMemPprof {
file, err := os.Create("./mem.pprof")
if err != nil {
fmt.Printf("create mem pprof failed, err:%v\n", err)
return
}
pprof.WriteHeapProfile(file)
file.Close()
}
//使用go tool pprof工具进行内存性能分析,默认是使用-inuse_space进行统计,还可以使用-inuse-objects查看分配对象的数量
//如果使用了默认的http.DefaultServeMux(通常是代码直接使用http.ListenAndServe(“0.0.0.0:8000”, nil)),只需要在web server端代码中匿名导入net/http/pprof
//如果使用自定义的 Mux,则需要手动注册一些路由规则:
r.HandleFunc("/debug/pprof/", 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)
//如果你使用的是gin框架,那么推荐使用"github.com/DeanThompson/ginpp
命令行
go tool pprof 生成的二进制文件 //进入交互界面
//在交互界面输入top3来查看程序中占用CPU前3位的函数
(pprof) top3
Showing nodes accounting for 100.37s, 87.68% of 114.47s total
Dropped 17 nodes (cum <= 0.57s)
Showing top 3 nodes out of 4
flat flat% sum% cum cum%
42.52s 37.15% 37.15% 91.73s 80.13% runtime.selectnbrecv
35.21s 30.76% 67.90% 39.49s 34.50% runtime.chanrecv
22.64s 19.78% 87.68% 114.37s 99.91% main.logicCode
/*
flat:当前函数占用CPU的耗时
flat%::当前函数占用CPU的耗时百分比
sun%:函数占用CPU的耗时累计百分比
cum:当前函数加上调用当前函数的函数占用CPU的总耗时
cum%:当前函数加上调用当前函数的函数占用CPU的总耗时百分比
最后一列:函数名称
*/
//可以使用list 函数名命令查看具体的函数分析,例如执行list logicCode查看我们编写的函数的详细分析。
(pprof) list logicCode
Total: 1.91mins
ROUTINE ================ main.logicCode in .../runtime_pprof/main.go
22.64s 1.91mins (flat, cum) 99.91% of Total
. . 12:func logicCode() {
. . 13: var c chan int
. . 14: for {
. . 15: select {
. . 16: case v := <-c:
22.64s 1.91mins 17: fmt.Printf("recv from chan, value:%v\n", v)
. . 18: default:
. . 19:
. . 20: }
. . 21: }
. . 22:}
//直接输入web;过svg图的方式查看程序中详细的CPU占用情况;需要安装graphviz图形化工具。
//下载graphviz 将graphviz安装目录下的bin文件夹添加到Path环境变量中。 在终端输入dot -version查看是否安装成功。
图形界面
//安装go-torch;他可以直接读取 golang profiling 数据,并生成一个火焰图的 svg 文件。
go get -v github.com/uber/go-torch
/*
从http://localhost:8080/debug/pprof/profile获取 profiling 数据。它有三个常用的参数可以调整:
-u –url:要访问的 URL,这里只是主机和端口部分
-s –suffix:pprof profile 的路径,默认为 /debug/pprof/profile
–seconds:要执行 profiling 的时间长度,默认为 30s
*/
/*
要生成火焰图,需要事先安装 FlameGraph工具
-下载安装perl:https://www.perl.org/get.html
-下载FlameGraph:git clone https://github.com/brendangregg/FlameGraph.git
- 将FlameGraph目录加入到操作系统的环境变量中。
- Windows平台需要把go-torch/render/flamegraph.go文件中的GenerateFlameGraph按如下方式修改,然后在go-torch目录下执行go install即可。
*/
// GenerateFlameGraph runs the flamegraph script to generate a flame graph SVG. func GenerateFlameGraph(graphInput []byte, args ...string) ([]byte, error) {
flameGraph := findInPath(flameGraphScripts)
if flameGraph == "" {
return nil, errNoPerlScript
}
if runtime.GOOS == "windows" {
return runScript("perl", append([]string{flameGraph}, args...), graphInput)
}
return runScript(flameGraph, args, graphInput)
}
/*
使用wrk([地址](https://github.com/wg/wrk))进行压测:go-wrk -n 50000 http://127.0.0.1:8080/book/list 在上面压测进行的同时,打开另一个终端执行go-torch -u http://127.0.0.1:8080 -t 30,30秒之后终端会初夏如下提示:Writing svg to torch.svg,然后使用浏览器打开torch.svg就能看到火焰图了。
*/
Profiling 一般和性能测试一起使用,只有应用在负载高的情况下 Profiling 才有意义。
go test -bench . -cpuprofile=cpu.prof //执行测试的同时,也会执行 CPU profiling,把结果保存在 cpu.prof 文件中
go test -bench . -memprofile=./mem.prof //执行测试的同时,也会执行 Mem profiling,把结果保存在 mem.prof 文件中