PProf工具的使用|青训营笔记

112 阅读2分钟

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 性能分析

  1. 生成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

image.png 除了在网页中查看分析数据外,我们也可以在命令行中使用交互模式查看。

$  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```