PProf工具的使用|青训营笔记
这是我参与「第五届青训营」伴学笔记创作活动的第19天
最近需要对项目进行性能优化,找到项目的性能瓶颈,为此记录此文~
性能检测
benchmark(基准测试) 可以度量某个函数或方法的性能,也就是说,如果我们知道性能的瓶颈点在哪里,benchmark 是一个非常好的方式。但是面对一个未知的程序,如何去分析这个程序的性能,并找到瓶颈点呢?
pprof 就是用来解决这个问题的。pprof 包含两部分:
- 编译到程序中的
runtime/pprof包 - 性能剖析工具
go tool pprof
1 性能分析类型
1.1 CPU 性能分析
CPU 性能分析(CPU profiling) 是最常见的性能分析类型。
启动 CPU 分析时,运行时(runtime) 将每隔 10ms 中断一次,记录此时正在运行的协程(goroutines) 的堆栈信息。
程序运行结束后,可以分析记录的数据找到最热代码路径(hottest code paths)。
一个函数在性能分析数据中出现的次数越多,说明执行该函数的代码路径(code path)花费的时间占总运行时间的比重越大。
1.2 内存性能分析
内存性能分析(Memory profiling) 记录堆内存分配时的堆栈信息,忽略栈内存分配信息。
内存性能分析启用时,默认每1000次采样1次,这个比例是可以调整的。因为内存性能分析是基于采样的,因此基于内存分析数据来判断程序所有的内存使用情况是很困难的。
本文重点介绍的是CPU的性能分析
CPU 性能分析
-
生成profile文件
- 调用
runtime/pprof库即可得到我们想要的数据。假设我们实现了这么一个程序,随机生成了 5 组数据,并且使用冒泡排序法排序。
- 调用
// main.go
package main
import (
"math/rand"
"os"
"runtime/pprof"
"time"
)
func generate(n int) []int {
rand.Seed(time.Now().UnixNano())
nums := make([]int, 0)
for i := 0; i < n; i++ {
nums = append(nums, rand.Int())
}
return nums
}
func bubbleSort(nums []int) {
for i := 0; i < len(nums); i++ {
for j := 1; j < len(nums)-i; j++ {
if nums[j] < nums[j-1] {
nums[j], nums[j-1] = nums[j-1], nums[j]
}
}
}
}
func main() {
f, _ := os.OpenFile("cpu.pprof", os.O_CREATE|os.O_RDWR, 0644)
defer f.Close()
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
n := 10
for i := 0; i < 5; i++ {
nums := generate(n)
bubbleSort(nums)
n *= 10
}
}
分析数据
go tool pprof -http=:9999 cpu.pprof
除了在网页中查看分析数据外,我们也可以在命令行中使用交互模式查看。
$ go tool pprof cpu.pprof
File: main
Type: cpu
Time: Feb 16, 2023 at 9:37pm (CST)
Duration: 11.54s, Total samples = 11.41s (98.87%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
(pprof) top
Showing nodes accounting for 11.36s, 99.56% of 11.41s total
Dropped 8 nodes (cum <= 0.06s)
flat flat% sum% cum cum%
11.36s 99.56% 99.56% 11.38s 99.74% main.bubbleSort (inline)
0 0% 99.56% 11.40s 99.91% main.main
0 0% 99.56% 11.40s 99.91% runtime.main```