以我不多的开发经验来看(很主观,欢迎讨论),后端开发者在学会和各种数据库(关系型MySQL和非关系型NoSQL等)交互(Repository层)、了解各种常见的后端架构(Service层,尤其是云原生时代的分布式微服务架构)、处理好和前端的对齐(Controller层)后,基本上在后端开发的职业路径就到头了。之后可能就分化成数据工程师、架构工程师、算法工程师、网络工程师等等不同细分职业。
但性能优化似乎仍属于后端工程师必不可少的通用技能之一,而在Golang中最常用到的优化评估工具便是benchmark和pprof。本篇文章将逐一介绍二者的基本用法。
benchmark
以一个常见的性能优化技巧为例,在提前知晓切片slice的最大容量capacity时,在make初始化时预分配capacity能够降低运行时间并减少扩容时的内存拷贝。待测试代码如下:
// target_file.go
package main
func PreAllocatedSlice(size int) {
s := make([]int, 0, size)
for i := 0; i < size; i++ {
s = append(s, i)
}
}
func NotPreAllocatedSlice(size int) {
s := make([]int, 0)
for i := 0; i < size; i++ {
s = append(s, i)
}
}
benchmark(基准测试)以如下形式写在测试文件target_file_test.go中
package main
import "testing"
func BenchmarkPreAllocatedSlice(b *testing.B) {
for i := 0; i < b.N; i++ {
PreAllocatedSlice(100)
}
}
func BenchmarkNotPreAllocatedSlice(b *testing.B) {
for i := 0; i < b.N; i++ {
NotPreAllocatedSlice(100)
}
}
使用如下命令串行运行该文件夹下所有基准测试
go test -benchmem -bench .
运行结果如下图所示:
其中-8表示GOMAXPROCS=8进程使用最大CPU数,第二列表示b.N执行次数,第三列表示每次执行花费的时间,第四列表示每次执行申请了多大的内存,第五列表示每次执行申请了几次内存。
整体执行情况符合预期。
pprof
pprof通过读取profile.proto格式的采样数据,生成项目对CPU和内存的使用情况分析报告,并支持在网页上数据可视化,是用来分析程序性能的重要工具。
benchmark负责微观,pprof负责宏观,二者互补。
Golang自带pprof工具,但可视化需要Graphviz工具。
Windows安装Graphviz下载地址:graphviz.org/download/
安装时需要选择添加到系统路径
然后需要在.../Graphviz/bin目录下执行dot.exe命令生成配置文件
以埋了雷的github.com/wolfogre/go…仓库代码为例,使用go run main.go把程序跑起来。
在另一个命令行页面中,使用如下命令在网页端可视化查看堆内存分配情况:
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"
结果如下,定位到问题函数Mouse.Steal
查看Mouse.Steal代码实现:
func (m *Mouse) Steal() {
log.Println(m.Name(), "steal")
max := constant.Gi
for len(m.buffer)*constant.Mi < max {
m.buffer = append(m.buffer, [constant.Mi]byte{})
}
}
发现第五行在反复申请内存,注释掉即可排除问题。
其余各种错误均可以通过替换URL(即"http://localhost:6060/debug/pprof/heap")后缀(把heap可以换成allocs, block, goroutine, heap, mutex, profile?seconds=10等)在网页端可视化页面定位问题,欢迎读者自由探索。