pprof 介绍
pprof
是 Go
语言自带的一个性能分析工具,它提供了对程序不同方面性能数据的采集和分析功能,主要涉及 CPU 性能、内存分配、阻塞情况、锁竞争等方面。通过分析这些数据,开发者可以了解程序在运行过程中各个函数的资源消耗情况,进而有针对性地进行优化。
工作流程
- 采样:
pprof
通过在程序运行期间按一定时间间隔对程序的状态进行采样,记录函数调用栈、内存分配等信息。 - 数据生产: 将采样得到的数据保存到文件中,这些文件通常以
.prof
为扩展名,包含了程序运行时的详细性能信息。 - 数据展示: 使用
go tool pprof
命令读取这些数据文件,以文本或可视化的方式展示分析结果,帮助开发者理解程序的性能状况。
如何使用
CPU性能
利用 pprof
我们可以方便的得到一段代码中各个阶段占用的cpu时间,通常我们选取其中占用时间最多的进行有针对性的分析,达到优化软件性能的目的。下面以一个斐波那契数列的计算进行演示。原始代码如下:
go
代码解读
复制代码
package main import ( "fmt" "time" ) func fibonacci(n int) int { if n <= 1 { return n } return fibonacci(n-1) + fibonacci(n-2) } func main() { for i := 0; i < 40; i++ { go fibonacci(i) // 启动多个协程进行计算 } fmt.Println("Calculating Fibonacci numbers...") time.Sleep(5 * time.Second) // 等待各个协程完成计算 }
-
引入
runtime/pprof
包go
代码解读
复制代码
package main import ( ... "runtime/pprof" ... ) ... func main() { // 创建一个文件用于保存 CPU 分析数据 f, err := os.Create("cpu.prof") if err != nil { panic(err) } defer f.Close() // 开始 CPU 分析 err = pprof.StartCPUProfile(f) if err != nil { panic(err) } defer pprof.StopCPUProfile() ... // 斐波那契数列计算执行 }
bash
代码解读
复制代码
go run main.go
执行完成以上代码后,我们会得到一个
cpu.prof
文件,该文件包含了程序运行期间的 CPU 使用情况。 -
使用
pprof
工具进行分析 ```bash go tool pprof cpu.profrust
代码解读
复制代码
File: main Type: cpu Time: Feb 25, 2025 at 11:53am (CST) Duration: 5s, Total samples = 920ms (18.39%) Entering interactive mode (type "help" for commands, "o" for options) (pprof) ... ```
进入
pprof
交互界面后,我们可以使用help
命令查看详细的帮助文档,具体的文档内容就不过多介绍了,这里简单讲讲几个常用的命令。top
: 显示cpu占用最多的函数,可以跟上数字显示占用最多的n个(如top3
显示占用最高的3个函数)bash
代码解读
复制代码
(pprof) top Showing nodes accounting for 920ms, 100% of 920ms total flat flat% sum% cum cum% 920ms 100% 100% 920ms 100% main.fibonacci
list <function_name>
: 显示指定函数的源代码,并标注每行代码的 CPU 使用情况。bash
代码解读
复制代码
(pprof) list main.fibonacci Total: 920ms ROUTINE ======================== main.fibonacci in /home/beer/workspace/test/pprof/main.go 920ms 1.34s (flat, cum) 145.65% of Total 240ms 240ms 10:func fibonacci(n int) int { 130ms 130ms 11: if n <= 1 { 50ms 50ms 12: return n . . 13: } 500ms 920ms 14: return fibonacci(n-1) + fibonacci(n-2) . . 15:} . . 16: . . 17:func main() { . . 18: // 创建一个文件用于保存 CPU 分析数据 . . 19: f, err := os.Create("cpu.prof")
web
:打开一个可视化的调用图,直观地展示函数之间的调用关系和 CPU 使用情况。需要安装graphviz
工具。dot
: 也可以使用在线可视化工具如 。可以使用dot > cpu.dot
将结果导出成dot
格式内容,将内容拷贝到在线工具中就可看到具体效果。
内存性能
利用 pprof
我们可以方便的得到一段代码在采样时的内存占用情况,可以定位到内存分配占用的具体代码,我们可以找出内存占用不合理的地方,进行更精确的内存优化。下面依旧使用斐波那契数列进行演示,为了保证采样数据的有效性,对函数进行了一些代码修改。
-
引入
runtime/pprof
包,运行分析程序go
代码解读
复制代码
package main import ( "fmt" "os" "runtime/pprof" "time" ) func fibonacci(n int) int { if n <= 1 { return n } fb := []int{0, 1} for i := 2; i <= n; i++ { fb = append(fb, fb[len(fb)-2]+fb[len(fb)-1]) } time.Sleep(time.Second * 6) // 为了保证采样时 内存没有被释放掉 return fb[n] } func main() { for i := 0; i < 80; i++ { go fibonacci(i) } fmt.Println("Calculating Fibonacci numbers...") time.Sleep(time.Second * 5) // 创建一个文件用于保存内存分析数据 f, err := os.Create("mem.prof") if err != nil { panic(err) } defer f.Close() // 进行内存分析 err = pprof.WriteHeapProfile(f) if err != nil { panic(err) } }
执行完成以上代码后,我们会得到一个
mem.prof
文件,该文件包含了采样时程序的内存使用情况。 -
使用
pprof
工具进行分析bash
代码解读
复制代码
go tool pprof mem.prof File: main Type: inuse_space Time: Feb 25, 2025 at 1:58pm (CST) Entering interactive mode (type "help" for commands, "o" for options) (pprof) top Showing nodes accounting for 512.03kB, 100% of 512.03kB total flat flat% sum% cum cum% 512.03kB 100% 100% 512.03kB 100% main.fibonacci
使用
list
命令可以扛到具体的内存分配情况。bash
代码解读
复制代码
(pprof) list main.fibonacci Total: 512.03kB ROUTINE ======================== main.fibonacci in /home/beer/workspace/test/pprof/main.go 512.03kB 512.03kB (flat, cum) 100% of Total . . 10:func fibonacci(n int) int { . . 11: if n <= 1 { . . 12: return n . . 13: } . . 14: fb := []int{0, 1} . . 15: for i := 2; i <= n; i++ { 512.03kB 512.03kB 16: fb = append(fb, fb[len(fb)-2]+fb[len(fb)-1]) . . 17: } . . 18: time.Sleep(time.Second * 6) . . 19: return fb[n] . . 20:} . . 21:
其他命令如top、list、web、dot等同cpu分析中用法是相同的用法,这里就不进行用于展示了。
Web服务中的性能分析
上面介绍的CPU和内存分析,仅仅针对相对独立的代码片段进行的,而对于环境更加复杂的情况,pprof
也提供了也写工具,如面对web服务时。 对于基于net/http
构建的服务,我们可以直接引入net/http/pprof
包,这个包会自动为我们添加好一系列测试接口。
go
代码解读
复制代码
package main import ( "net/http" _ "net/http/pprof" "time" ) func fibonacci(n int) int { if n <= 1 { return n } time.Sleep(time.Second * 2) return fibonacci(n-1) + fibonacci(n-2) } func main() { go func() { for i := 0; i < 40; i++ { go fibonacci(i) } time.Sleep(5 * time.Second) }() // 启动 pprof 服务 go func() { http.ListenAndServe("localhost:8888", nil) }() select {} }
运行程序后,可以通过以下 URL 访问不同的性能分析数据:
注意:这里的接口都是在访问时触发采样,等待采样完成后下载结果数据。
对于常见的Web框架,对应的社区中也有相应工具库可以集成pprof,如:
- Gin框架
go
代码解读
复制代码
package main import ( "net/http" "github.com/gin-contrib/pprof" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() pprof.Register(r) r.GET("/ping", func(ctx *gin.Context) { ctx.JSON(http.StatusOK, gin.H{ "message": "pong", }) }) r.Run(":8888") }