这是我参与「第五届青训营 」伴学笔记创作活动的第四天。今天学习了性能分析工具pprof的使用,并进行了性能分析实战,了解到了一些go语言的基本优化方法。
pprof工具的基本使用
我们在平时运行程序的时候并不不知道我们的程序占用了多少cpu,也不知道使用了多少内存,这不利于我们寻找需要优化的地方,pprof工具就可以帮我们统计方法的消耗,并将数据展示给我们,帮助我们定位需要优化的方法。pprof的具体内容如下:
功能
我们可以直接使用go get拉取pprof的依赖进行使用,golang的hhtp标准库对ppro进行了一些封装,让我们能够在http服务中使用,它可以记录程序在运行时各种数据,例如:cpu、堆内存、goroutine、锁竞争、阻塞调用,线程数据等,用户可以通过列表、调用图、火焰图、源码、反汇编等视图去展示搜收集到的数据信息。
页面介绍
Wolfogre提供了练习pprof的项目,并提前埋下了一些炸弹代码供我们使用工具找出,项目入口:
func main() {
log.SetFlags(log.Lshortfile | log.LstdFlags)
log.SetOutput(os.Stdout)
runtime.GOMAXPROCS(1)
runtime.SetMutexProfileFraction(1)
runtime.SetBlockProfileRate(1)
go func() {
if err := http.ListenAndServe(":6060", nil); err != nil {
log.Fatal(err)
}
os.Exit(0)
}()
for {
for _, v := range animal.AllAnimals {
v.Live()
}
time.Sleep(time.Second)
}
}
项目启动后我们就可以访问http://localhost:6060/debug/pprof/ 进入监控页面:
- allocs:内存分配情况
- blocks:阻塞操作情况
- cmdline:程序启动命令及
- goroutine:当前所有goroutine的堆栈信息
- heap:堆上内存使用情
- mutex:锁竞争操作情况
- profile: CPU占用情况
- threadcreate:当前所有创建的系统线程的堆栈信息
- trace:程序运行跟踪数据
pprof实战
cpu的排查
我采用go tool pprof +采样链接来启动采样,等待一会后会显示这段时间内程序的运行数据,输入top查看cpu使用最高的函数,默认会显示占用最高的前10个函数的信息,一般我们需要优化前几名的函数,使用list 函数名的方式可以定位到具体的一个函数,并展示它每行的占用,在给出的实例项目中,可以定位到eat方法的第24行,它执行了100亿次的空循环,导致cpu占用过高,这样我们就找到了问题所在。
内存的排查
pprof还提供了 另一种更简便明了的方法,通过-http=:8080参数,可以开启pprof自带的Web UI,性能指标会以网页的形式呈现。输入 go tool pprof -http=:8080 http://localhost:6060/debug/pprof/heap 进行采样,完成后会自动打开浏览器,:
我们可以看到是Steal()方法占用了1g的内存,切换到Source视图下查找Steal方法,我们发现steal函数会向buffer中不断追加内存至到1g,这样我们就找到了内存的问题。
协程排查
打开pprof的主页,我们可以看到有一百多个协程在运行,这对于一个小程序来说并不正常,将刚才指令的结尾换成gorouting运行后,打开view切换到Flame Graph视图,图中,自顶向下展示了各个调用,表示各个函数调用之间的层级关系,每一行中,条形越长代表消耗的资源占比越多,我们看到wolf.drink()方法创建了绝大部分的协程,通过Source视图找到drink方法,我们看到34行申请了没有意义的10条协程,这样我们就找到了协程的问题。
锁排查
和之前一样将后缀改成mutex,在graph视图中找到出现问题的函数为wolf.Howl()方法,之后在Source视图搜索该方法,定位到58行发生可锁竞争导致了阻塞,这样我们就找了锁的问题。
阻塞排查
将结尾改成block之后根据graph视图,找到了问题方法为cat.pee(),在Source中搜索该方法,定位到第39行pee()方法试图读取time.After()方法生成的channel导致阻塞了一秒钟,这样我们就找到了阻塞的问题。