Go语言常用性能分析入门
pprof包来做代码的性能监控,pprof 是一种用于分析数据可视化和分析的工具。
Package pprof writes runtime profiling data in the format expected by the pprof visualization tool.
主要有2个package
net/http/pprof
采集工具型应用运行数据进行分析
runtime/pprof
采集服务型应用运行时数据进行分析
在使用pprof之前先安装一些工具
Graphviz
Graphviz 是一款由 AT&T Research 和 Lucent Bell 实验室开源的可视化图形工具,可以很方便的用来绘制结构化的图形网络,支持多种格式输出。
官网:https://graphviz.org
安装
mac
brew install graphviz
liunx
yum install graphviz
windows
可以直接去官方下载安装包,window需要添加系统变量、
验证
dot -version
go-torch
go-torch是uber开源的分析工具
官网 https://github.com/uber-archive/go-torch
安装FlameGraph脚本
git clone https://github.com/brendangregg/FlameGraph.git
cp FlameGraph/flamegraph.pl /usr/local/bin
安装go-torch
go get -v github.com/uber/go-torch
pprof 使用
本次使用在liunx环境上。
首先我们这边写一份代码
main.go
package main
import (
"log"
"os"
"runtime/pprof"
"strconv"
)
type User struct {
Name string
Age int
}
func main() {
//创建输出文件句柄
f, err := os.Create("cpu.prof")
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
// 获取系统信息
// 监控cpu
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
users := GetUsers()
UpdateUsers(users)
// 监控堆内存
f1, err := os.Create("mem.prof")
if err != nil {
log.Fatal("could not create memory profile: ", err)
}
// runtime.GC() // GC,获取最新的数据信息
if err := pprof.WriteHeapProfile(f1); err != nil { // 写入内存信息
log.Fatal("could not write memory profile: ", err)
}
f1.Close()
// 监控goroutine
f2, err := os.Create("goroutine.prof")
if err != nil {
log.Fatal("could not create groutine profile: ", err)
}
if gProf := pprof.Lookup("goroutine"); gProf == nil {
log.Fatal("could not write groutine profile: ")
} else {
gProf.WriteTo(f2, 0)
}
f2.Close()
}
func GetUsers() []*User {
users := []*User{}
for i := 0; i < 1000000; i++ {
users = append(users, &User{
Name: "user" + strconv.Itoa(i),
Age: i,
})
}
return users
}
func UpdateUsers(users []*User) {
for _, user := range users {
user.Age *= 2
}
}
编译main.go
go build main.go
运行main二进制文件
./main
我们可以看到生成了3个.prof 文件
.
├── cpu.prof
├── goroutine.prof
├── main
├── main.go
└── mem.prof
0 directories, 5 files
我们使用go tool 工具查看prof 文件
我们先看下cpu.prof
go tool pprof cpu.prof
File: main
Type: cpu
Time: Oct 14, 2021 at 12:52pm (CST)
Duration: 200.38ms, Total samples = 0
No samples were found with the default sample value type.
Try "sample_index" command to analyze different sample values.
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
使用top 命令查看方法耗时
(pprof) top -cum
Showing nodes accounting for 60ms, 25.00% of 240ms total
Showing top 10 nodes out of 38
flat flat% sum% cum cum%
0 0% 0% 150ms 62.50% main.main
0 0% 0% 150ms 62.50% runtime.main
0 0% 0% 140ms 58.33% main.GetUsers
30ms 12.50% 12.50% 90ms 37.50% runtime.mallocgc
0 0% 12.50% 80ms 33.33% runtime.gcBgMarkWorker
0 0% 12.50% 70ms 29.17% runtime.gcBgMarkWorker.func2
10ms 4.17% 16.67% 70ms 29.17% runtime.gcDrain
0 0% 16.67% 70ms 29.17% runtime.systemstack
0 0% 16.67% 60ms 25.00% runtime.newobject
20ms 8.33% 25.00% 60ms 25.00% runtime.scanobject
使用list 查看 GetUsers方法
(pprof) list GetUsers
Total: 240ms
ROUTINE ======================== main.GetUsers in /data/home/lalalala/Desktop/study/main.go
0 140ms (flat, cum) 58.33% of Total
. . 55:}
. . 56:
. . 57:func GetUsers() []*User {
. . 58: users := []*User{}
. . 59: for i := 0; i < 1000000; i++ {
. 70ms 60: users = append(users, &User{
. 70ms 61: Name: "user" + strconv.Itoa(i),
. . 62: Age: i,
. . 63: })
. . 64: }
. . 65: return users
. . 66:}
可以清晰的看到那一行耗时最多
生成svg
(pprof) svg
Generating report in profile001.svg
用浏览器打开
图中可以清晰看到每个方法的耗时和调用关系
生成火焰图
$ go-torch cpu.prof
INFO[16:56:02] Run pprof command: go tool pprof -raw -seconds 30 cpu.prof
INFO[16:56:02] Writing svg to torch.svg
用浏览器打开
通过Http方式输出Profile
在服务请启动时导入
import _ "net/http/pprof"
先准备一个例子
package main
import (
"fmt"
"net/http"
_ "net/http/pprof"
"strconv"
)
// 故意写比较慢的递归形式
func GetFibonacciService(n int) int {
if n == 1 || n == 2 {
return 1
}
return GetFibonacciService(n-1) + GetFibonacciService(n-2)
}
func main() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
})
http.HandleFunc("/count", func(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, strconv.Itoa(GetFibonacciService(25)))
})
http.ListenAndServe(":9999", nil)
}
可以直接在浏览器查看http://localhost:9999/debug/pprof/
Profile Descriptions:
- allocs: A sampling of all past memory allocations
- block: Stack traces that led to blocking on synchronization primitives
- cmdline: The command line invocation of the current program
- goroutine: Stack traces of all current goroutines
- heap:A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample.
- mutex:Stack traces of holders of contended mutexes
- profile: CPU profile. You can specify the duration in the seconds GET parameter. After you get the profile file, use the go tool pprof command to investigate the profile.
- threadcreate:Stack traces that led to the creation of new OS threads
- trace:A trace of execution of the current program. You can specify the duration in the seconds GET parameter. After you get the trace file, use the go tool trace command to investigate the trace.
也可以用命令行查看
go tool pprof http://localhost:9999/debug/pprof/profile?seconds=10
Fetching profile over HTTP from http://localhost:9999/debug/pprof/profile?seconds=10
Saved profile in C:\Users\penggewu\pprof\pprof.samples.cpu.003.pb.gz
Type: cpu
Time: Oct 14, 2021 at 4:59pm (CST)
Duration: 10s, Total samples = 0
No samples were found with the default sample value type.
Try "sample_index" command to analyze different sample values.
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
其他操作和pprof操作基本一样
生成svg和火焰图操作相同。
优秀文章推荐:
https://www.liwenzhou.com/posts/Go/performance_optimisation/