[TOC]
性能调优
在计算机性能调试领域里,profiling 是指对应用程序的画像,画像就是应用程序使用 CPU 和内存的情况。 Go语言是一个对性能特别看重的语言,因此语言中自带了 profiling 的库,这篇文章就要讲解怎么在 golang 中做 profiling。
go性能优化
- cpu profile: 报告程序的cpu使用情况,按照一定频率去采集应用程序在cpu和寄存器上面的数据
- mem profile(heap profile):报告程序的内存使用情况
- block profiling:报告 goroutines 不在运行状态的情况,可以用来分析和查找死锁等性能瓶颈
- goroutine profiling:报告 goroutines 的使用情况,有哪些 goroutine,它们的调用关系是怎样的
采集性能的包
Go语言内置了获取程序的运行数据的工具,包括以下两个标准库:
runtime/pprof:采集工具型应用运行数据进行分析net/http/pprof:采集服务型应用运行数据进行分析
注意,我们只应该在性能测试的时候才在代码中引入pprof。
工具型应用(runtime/pprof)
如果你的应用程序是运行一段时间就结束退出类型。那么最好的办法是在应用退出的时候把 profiling 的报告保存到文件中,进行分析。对于这种情况,可以使用runtime/pprof库。 首先在代码中导入runtime/pprof工具:
import "runtime/pprof"
CPU性能分析
开启CPU性能分析:
pprof.StartCPUProfile(w io.Writer)
停止CPU性能分析:
pprof.StopCPUProfile()
应用执行结束后,就会生成一个文件,保存了我们的 CPU profiling 数据。得到采样数据之后,使用go tool pprof工具进行CPU性能分析。
内存性能优化
记录程序的堆栈信息
pprof.WriteHeapProfile(w io.Writer)
得到采样数据之后,使用go tool pprof工具进行内存性能分析。
go tool pprof默认是使用-inuse_space进行统计,还可以使用-inuse-objects查看分配对象的数量
服务型应用(net/http/pprof)
如果你的应用程序是一直运行的,比如 web 应用,那么可以使用net/http/pprof库,它能够在提供 HTTP 服务进行分析。
如果使用了默认的http.DefaultServeMux(通常是代码直接使用 http.ListenAndServe(“0.0.0.0:8000”, nil)),只需要在你的web server端代码中按如下方式导入net/http/pprof
import _ "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/ginpprof"。
不管哪种方式,你的 HTTP 服务都会多出/debug/pprof endpoint,访问它会得到类似下面的内容:
go tool pprof命令
实例
CPU维度的profile数据
mem维度的profile数据
package mainimport ("flag""fmt""os""runtime/pprof""time"
)//实例代码
func logicCode() {var c chan intfor {select {case v := <-c: //阻塞fmt.Printf("recv from chan, value:%v\n", v)default:time.Sleep(time.Millisecond * 400)}}
}//采集数据
func main() {var isCPUPprof bool //是否开启cpu profile的标志位var isMemPprof bool // 是否开启内存 profile标志位//外部传入cpu、mem的参数flag.BoolVar(&isCPUPprof, "cpu", false, "turn cpu pprof on")flag.BoolVar(&isMemPprof, "mem", false, "turn mem pprof on")flag.Parse()// 如果开启了cpu pprof,那么就把cpu相关的记录写入cpu.pprof中if isCPUPprof {f1, err := os.Create("./cpu.pprof")if err != nil {fmt.Printf("create cpu pprof failed, err:%v\n", err)return}pprof.StartCPUProfile(f1) //开启CPU性能分析,往文件中记录cpu profile信息defer func() {pprof.StopCPUProfile() //停止CPU性能分析f1.Close()}()}for i := 0; i < 6; i++ {go logicCode()}time.Sleep(20 * time.Second)//如果开启了mem pprof 那么就把相关信息写入mem.pprof中if isMemPprof {f2, err := os.Create("./mem.pprof")if err != nil {fmt.Printf("create mem pprof failed, err:%v\n", err)return}pprof.WriteHeapProfile(f2)f2.Close()}
}