使用 go pprof 工具进行性能分析

pprof 介绍

pprof是Go的性能分析工具,在程序运行过程中,可以记录程序的运行信息,可以是CPU使用情况、内存使用情况、goroutine运行情况等,当需要性能调优或者定位Bug时候,这些记录的信息是相当重要。
go 对于 profiling 支持的比较好,标准库就提供了profile 库 runtime/pprof 和 net/http/pprof,而且也提供了很多好用的可视化工具来辅助开发者做 profiling。
对于在线服务,对于一个 HTTP Server,访问 pprof 提供的 HTTP 接口,获得性能数据。

性能数据获取

pprof 的应用场景主要分为两种:

1. 服务型应用,例如 web 服务器等各种服务类型端的性能分析;
2. 工具型应用,例如一些命令行工具,执行完毕后直接退出的应用;

1. 工具型应用性能分析示例

工具型应用性能分析基于 "runtime/pprof" 包,将性能数据存放在文件中 :

package main

import (
	"fmt"
	"os"
	"runtime/pprof"
)

func main() {
	// 创建 cup 数据记录文件
	cpuProfile, err := os.Create("./cpu_profile")
	if err != nil {
		fmt.Printf("创建文件失败:%s", err.Error())
		return
	}
	defer cpuProfile.Close()
	// 创建内存数据记录文件
	memProfile, err := os.Create("./mem_profile")
	if err != nil {
		fmt.Printf("创建文件失败:%s", err.Error())
		return
	}
	defer memProfile.Close()

	//采集CPU信息
	pprof.StartCPUProfile(cpuProfile)
	defer pprof.StopCPUProfile()

	//采集内存信息
	pprof.WriteHeapProfile(memProfile)

	// 执行一个逻辑
	for i := 0; i < 9999; i++ {
		fmt.Println("pprof 工具型测试")
	}

	println("-- mian done --")
}

使用 go tool pprof 命令查看数据

执行上面的代码后,会产生2个性能数据文件 : cpu_profile 和 mem_profile,可以使用 go tool pprof 命令查看数据 :

# 01. 执行命令
# cup 使用数据
go tool pprof .\cpu_profile

# 内存使用数据
# go tool pprof .\mem_profile

# 02. 回显 :
PS ...\goDemo> go tool pprof .\cpu_profile
File: main.exe
Build ID: ...\main.exe2023-10-11 13:54:05.4308623 +0800 CST
Type: cpu
Time: Oct 11, 2023 at 1:54pm (CST)
Duration: 979.59ms, Total samples = 510ms (52.06%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)

# 03. 输入命令继续
top5
# 数据内容 :
flat    flat%   sum%        cum   cum%
510ms   100%   100%      510ms   100%  runtime.cgocall
  0     0%   100%      510ms   100%  fmt.Fprintln
  0     0%   100%      510ms   100%  fmt.Println
  0     0%   100%      510ms   100%  internal/poll.(*FD).Write
  0     0%   100%      510ms   100%  internal/poll.(*FD).writeConsole

列信息字段作用 :

flat:函数在 CPU 上运行的时间
flat%:函数在CPU上运行时间的百分比
sum%:是从第一行到当前行所有函数累加使用 CPU 的比例,如第二行sum=53.85=30.77+23.08
cum:这个函数以及子函数运行所占用的时间,应该大于等于flat
cum%:这个函数以及子函数运行所占用的比例,应该大于等于flat%
最后一列:函数的名字

以 web 界面形式查看数据

您还可以开启一个 web 服务,以 web 形式查看性能数据 :

go tool pprof -http=:8808 .\mem_profile

注意 : 需要安装 graphviz

graphviz

Graphviz 是一款由 AT&T Research 和 Lucent Bell 实验室开源的可视化图形工具,可以很方便的用来绘制结构化的图形网络,支持多种格式输出。Graphviz 输入是一个用 dot 语言编写的绘图脚本,通过对输入脚本的解析,分析出其中的点、边及子图,然后根据属性进行绘制。Graphviz layout 以简单的文本语言描述图形,并以实用的格式制作图表,如用于网页的 images 和 SVG ;用于放入在其它文件中或显示在交互式图形浏览器中的 PDF 和 Postscript 。

安装 graphviz

官网 :  https://www.graphviz.org/

下载及安装 graphviz :  https://www.graphviz.org/download/

再次运行性能数据查看命令

go tool pprof -http=:8808 .\mem_profile

会打开一个浏览器窗口,以图形界面形式展示性能数据 :


1. 服务型应用性能分析示例

对于服务类型的应用,主要在服务内部匿名引入 net/http/pprof 包,然后通过 HTTP 访问 pprof 页面。

package main

import (
	"fmt"
	"net/http"
	_ "net/http/pprof"
)

func main() {
	http.HandleFunc("/", Test)
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		fmt.Println("ListenAndServe Err:", err.Error())
		return
	}
}

func Test(resp http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(resp, "")
}

上面的代码开启了一个 HTTP 服务,端口为 8080,可以通过浏览器访问 :

http://localhost:8080/ 进行测试。

查看服务性能数据 :

执行http://localhost:8080/debug/pprof/可以看到画像信息:

项目功能描述

allocs:过去所有内存分配的采样
block:导致同步基元阻塞的堆栈跟踪
cmdline:当前程序的命令行调用
goroutine:堆栈所有当前goroutine的跟踪。使用debug=2作为查询参数,以与未恢复的死机相同的格式导出。
heap:活动对象的内存分配的采样。您可以指定gc GET参数以在获取堆样本之前运行gc。
mutex:争用互斥体持有者的堆栈跟踪
profile:CPU配置文件。您可以在seconds GET参数中指定持续时间。获取配置文件后,使用go tool pprof命令来调查该配置文件。
threadcreate:导致创建新操作系统线程的堆栈跟踪
trace:当前程序执行的跟踪。您可以在seconds GET参数中指定持续时间。获取跟踪文件后,使用go tool trace命令来调查跟踪。

记录指定时间段内的性能数据 :

浏览器访问 :  http://localhost:8080/debug/pprof/profile?seconds=10

10 秒( 可以通过参数设置监控时长 )后 ( 期间可以访问网站首页或者使用 ab 进行压测 ) 会自动下载得到一个性能文件 : profile。

在 profile 同目录下执行 :

go tool pprof .\profile

# 回显
Type: cpu
...

# 继续输入 
top 
# 显示
Showing top 10 nodes out of 146
      flat  flat%   sum%        cum   cum%
    1700ms 28.05% 28.05%     1710ms 28.22%  runtime.cgocall
     560ms  9.24% 37.29%      560ms  9.24%  runtime.stdcall1
     450ms  7.43% 44.72%      460ms  7.59%  runtime.stdcall6
     350ms  5.78% 50.50%     1910ms 31.52%  github.com/cnlesscode/graceMessageQueue/kernel.SaveMsgToDisk
     310ms  5.12% 55.61%      320ms  5.28%  runtime.mapaccess1_faststr
     120ms  1.98% 57.59%      150ms  2.48%  runtime.chanrecv
     110ms  1.82% 59.41%      570ms  9.41%  runtime.netpoll
      80ms  1.32% 60.73%       80ms  1.32%  runtime.siftupTimer
      70ms  1.16% 61.88%       70ms  1.16%  runtime.casgstatus
      70ms  1.16% 63.04%       80ms  1.32%  runtime.lock2

# 使用 list 命令查看耗时函数
list github.com/cnlesscode/graceMessageQueue/kernel.SaveMsgToDisk

以 web 形式查看数据

go tool pprof -http=:9090 .\profile