Golang pprof 性能分析工具

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) // 等待各个协程完成计算 }

  1. 引入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 使用情况。

  2. 使用pprof工具进行分析 ```bash go tool pprof cpu.prof

    
    

    rust

    代码解读

    复制代码

    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 我们可以方便的得到一段代码在采样时的内存占用情况,可以定位到内存分配占用的具体代码,我们可以找出内存占用不合理的地方,进行更精确的内存优化。下面依旧使用斐波那契数列进行演示,为了保证采样数据的有效性,对函数进行了一些代码修改。

  1. 引入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文件,该文件包含了采样时程序的内存使用情况。

  2. 使用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") }

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值