性能分析方法总结之pprof
1、代码实例
以下例子除了特别说明,都以这段代码为实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package main import ( "log" "time" "net/http" _ "net/http/pprof" ) var datas []string func main() { go func () { for { log.Printf( "len: %d" , Add( "go-programming-tour-book" )) time.Sleep(time.Millisecond * 10) } }() _ = http.ListenAndServe( "0.0.0.0:6060" , nil) } func Add(str string) int { data := []byte(str) datas = append(datas, string(data)) return len(datas) } |
2、性能分析方法
2.1通过浏览器分析
运行程序后,在浏览器访问http://127.0.0.1:6060/debug/pprof/
2.2通过交互式终端分析
1)分析CPU资源占用情况
运行命令go tool pprof http://localhost:6060/debug/pprof/profile\?seconds\=60
执行该命令后,需等待 60 秒(可调整 seconds 的值),pprof 会进行 CPU Profiling,结束后将默认进入 pprof 的命令行交互式模式,可以对分析的结果进行查看或导出。另外如果你所启动的 HTTP Server 是 TLS 的方式,那么在调用 go tool pprof
时,需要将调用路径改为:go tool pprof https+insecure://localhost:6060/debug/pprof/profile\?seconds\=60
。可以通过top 10命令查看资源占用前10的函数。
相关参数说明:
-
- flat:函数自身的运行耗时。
- flat%:函数自身在 CPU 运行耗时总比例。
- sum%:函数自身累积使用 CPU 总比例。
- cum:函数自身及其调用函数的运行总耗时。
- cum%:函数自身及其调用函数的运行耗时总比例。
- Name:函数名。
2)分析内存占用情况
执行命令go tool pprof http://localhost:6060/debug/pprof/heap
命令执行完毕显示内容如下图:
执行该命令后,能够很快的拉取到其结果,因为它不需要像 CPU Profiling 做采样等待,这里需要注意的一点是 Type
这一个选项,你可以看到它默认显示的是 inuse_space
,实际上可以针对多种内存概况进行分析,常用的类别如下:
-
- inuse_space:分析应用程序的常驻内存占用情况。
- alloc_objects:分析应用程序的内存临时分配情况。
- inuse_objects:查看每个函数所分别的对象数量
- alloc_space:查看分配的内存空间大小
3)分析协程情况
执行命令go tool pprof http://localhost:6060/debug/pprof/goroutine
在查看 goroutine 时,我们可以使用 traces
命令,这个命令会打印出对应的所有调用栈。
4)分析mutex情况
执行命令go tool pprof http://localhost:6061/debug/pprof/mutex
可以调用 top
命令,查看互斥量的排名,也可以调用 list
命令,看到指定函数的代码情况(包含特定的指标信息,例如:耗时)。
注意:需要特别注意的是 runtime.SetMutexProfileFraction
语句,如果未来希望进行互斥锁的采集,那么需要通过调用该方法来设置采集频率,若不设置或没有设置大于 0 的数值,默认是不进行采集的。例如如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | func init() { runtime.SetMutexProfileFraction(1) } func main() { var m sync.Mutex var datas = make( map [int] struct {}) for i := 0; i < 999; i++ { go func (i int) { m.Lock() defer m.Unlock() datas[i] = struct {}{} }(i) } _ = http.ListenAndServe( ":6061" , nil) } |
5)分析block情况
执行命令go tool pprof http://localhost:6061/debug/pprof/block
可以调用 top
命令,查看阻塞的排名,也可以调用 list
命令,查看阻塞的具体情况。
注意:与 Mutex 的 runtime.SetMutexProfileFraction
相似,Block 也需要调用 runtime.SetBlockProfileRate()
进行采集量的设置,否则默认关闭,若设置的值小于等于 0 也会认为是关闭。
2.3查看可视化界面
首先,运行程序,然后从路由http://127.0.0.1:6060/debug/pprof/profile获取profile文件。
然后,执行go tool pprof -http=:6001 profile命令,如果出现错误提示
Could not execute dot; may need to install graphviz.
,那么意味着你需要安装 graphviz
组件。
3、通过测试用例做分析
3.1代码
add.go
1 2 3 4 5 6 7 | package main func Add(str string) int { data := []byte(str) datas = append(datas, string(data)) return len(datas) } |
add_test.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package main import ( "testing" ) func TestAdd(t *testing.T) { _ = Add( "go-programming-tour-book" ) } func BenchmarkAdd(b *testing.B) { for i := 0; i < b.N; i++ { Add( "go-programming-tour-book" ) } } |
3.2对CPU进行分析
首先,执行命令go test -bench=. -cpuprofile=cpu.profile,
执行完毕后会在当前命令生成 cpu.profile 文件。
然后,执行命令go tool pprof -http=:6001 cpu.profile,即可在浏览器查看CPU情况。
3.3对内存进行分析
首先,执行命令go test -bench=. -memprofile=mem.profile
,
执行完毕后会在当前命令生成mem.profile 文件。
然后,执行命令go tool pprof -http=:6001 mem.profile,即可在浏览器查看内存使用情况。
4、通过lookup写入文件做分析
4.1代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | package main import ( "io" "net/http" "runtime" "os" "runtime/pprof" ) type LookupType int8 const ( LookupGoroutine LookupType = iota LookupThreadcreate LookupHeap LookupAllocs LookupBlock LookupMutex ) func pprofLookup(lookupType LookupType, w io.Writer) error { var err error switch lookupType { case LookupGoroutine: p := pprof.Lookup( "goroutine" ) err = p.WriteTo(w, 2) case LookupThreadcreate: p := pprof.Lookup( "threadcreate" ) err = p.WriteTo(w, 2) case LookupHeap: p := pprof.Lookup( "heap" ) err = p.WriteTo(w, 2) case LookupAllocs: p := pprof.Lookup( "allocs" ) err = p.WriteTo(w, 2) case LookupBlock: p := pprof.Lookup( "block" ) err = p.WriteTo(w, 2) case LookupMutex: p := pprof.Lookup( "mutex" ) err = p.WriteTo(w, 2) } return err } func init() { runtime.SetMutexProfileFraction(1) runtime.SetBlockProfileRate(1) } func main() { http.HandleFunc( "/lookup/heap" , func (w http.ResponseWriter, r *http.Request) { _ = pprofLookup(LookupHeap, os.Stdout) }) http.HandleFunc( "/lookup/threadcreate" , func (w http.ResponseWriter, r *http.Request) { _ = pprofLookup(LookupThreadcreate, os.Stdout) }) http.HandleFunc( "/lookup/block" , func (w http.ResponseWriter, r *http.Request) { _ = pprofLookup(LookupBlock, os.Stdout) }) http.HandleFunc( "/lookup/goroutine" , func (w http.ResponseWriter, r *http.Request) { _ = pprofLookup(LookupGoroutine, os.Stdout) }) _ = http.ListenAndServe( "0.0.0.0:6060" , nil) } |
通过 runtime/pprof 所提供的 Lookup 方法来进行相关内容的采集和调用,其一共支持六种类型,分别是:goroutine、threadcreate、heap、block、mutex,其提供了 io.Writer
接口,也就是只要实现了对应的 Write 方法,我们可以将其写到任何支持地方去。
参考:Go 大杀器之性能剖析 PProf(上) | Go 语言编程之旅 (eddycjy.com)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· 单线程的Redis速度为什么快?
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码