GoLang 提供了非常强大的代码性能分析工具 pprof,可以非常方便的分析代码运行性能,同时非常直观。
简介
监控代码性能的有两个包 net/http/pprof
以及 runtime/pprof
,其中前者是以 HTTP 的方式将数据暴露出来,实际上其内部封装的仍然是 runtime/pprof
。
另外,在 go test
工具中,同时也封装了 runtime/pprof
这个包,可以通过类似 -cpuprofile=cpu.prof
-memprofile=mem.prof
的参数指定。
使用过程分成了两步,首先需要运行代码收集相关的性能数据,然后通过相关的工具对数据进行分析,查找程序运行的瓶颈点。
runtime/pprof
对于 runtime/pprof
这个库,在 runtime/pprof 中展示了相关的使用方式,这里简单借助斐波纳契数列介绍其使用方法。
package main
import (
"flag"
"fmt"
"log"
"os"
"runtime/pprof"
)
var (
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file.")
)
func fibonacci(num int) int {
if num < 2 {
return 1
}
return fibonacci(num-1) + fibonacci(num-2)
}
func main() {
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
defer f.Close()
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
}
for i := 0; i < 30; i++ {
nums := fibonacci(i)
fmt.Println(nums)
}
}
在编译完上述的代码之后,可以通过 --cpuprofile
参数指定输出的 CPU 性能采集文件。
$ go build fibonacci.go
$ ./fibonacci --cpuprofile=fibonacci.prof
$ go tool pprof fibonacci fibonacci.prof
File: fibonacci
Type: cpu
Time: Apr 1, 2020 at 11:32am (CST)
Duration: 200.49ms, Total samples = 10ms ( 4.99%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top # 也可以使用topN命令,N表示显示多少个top信息
Showing nodes accounting for 10ms, 100% of 10ms total
flat flat% sum% cum cum%
10ms 100% 100% 10ms 100% main.fibonacci
0 0% 100% 10ms 100% main.main
0 0% 100% 10ms 100% runtime.main
(pprof) list fibonacci # 查看函数采样
内存泄漏排查
在 GoLang 引入了协程之后,在有效提高性能的同时,因为各个协程之间是软限制的协作关系,导致了协程泄漏的风险,而且大部分 Go 中的内存泄漏都是因为协程的泄漏引起的,因为对于常规的对象都可以通过 gc 进行回收。
通过 go tool pprof http://localhost:6060/debug/pprof/heap
直接访问栈内存信息。
top
默认按指标大小列出前 10 个函数,比如内存是按内存占用多少,CPU 是按执行时间多少。
(pprof) top
Showing nodes accounting for 520.61MB, 99.90% of 521.11MB total
Dropped 9 nodes (cum <= 2.61MB)
flat flat% sum% cum cum%
520.61MB 99.90% 99.90% 520.61MB 99.90% main.main
0 0% 99.90% 520.61MB 99.90% runtime.main
默认 top
命令会列出前 5 个统计数据,各个列的含义如下:
flat 本函数占用的内存量。
flat% 本函数内存占使用中内存总量的百分比。
sum% 前面每一行flat百分比的和,比如第2行的99.90%其实是99.90%+0%。
cum 累计量,假设main函数调用了foobar函数,那么函数foobar的内存占用量也会被统计进来。
cum% 累计量占总量的百分比。
list
确认某个函数中资源的开销,可以具体到某一行的数据,如果函数名不明确,会进行模糊匹配,比如 list main
会列出 main.main
和 runtime.main
。
(pprof) list main.main
Total: 521.11MB
ROUTINE ======================== main.main in /tmp/foobar/main.go
520.61MB 520.61MB (flat, cum) 99.90% of Total
. . 18: }()
. . 19:
. . 20: tick := time.Tick(time.Second / 100)
. . 21: var buf []byte
. . 22: for range tick {
520.61MB 520.61MB 23: buf = append(buf, make([]byte, 1024*1024)...)
. . 24: }
. . 25:}