使用 pprof 和火焰图进行性能分析
程序慢在哪,靠猜没用。Go 的 pprof 采集 CPU、内存、goroutine 等运行数据,火焰图(Flame Graph)把这些数据画成调用栈的图,哪个函数占的时间多一眼能看到。这里记录用这套工具排查死锁和 CPU 消耗热点的过程。
1. pprof 工具简介
pprof 是 Go 内置的性能分析工具,支持 CPU、内存分配、goroutine、阻塞操作等分析,产出的报告用来定位瓶颈。
1.1 安装 pprof
pprof 已经在标准库里,go tool pprof 直接能跑。要装可视化工具,用下面的命令:
go install github.com/google/pprof@latest2. 生成性能分析数据
为了使用 pprof 进行性能分析,我们需要在代码中导入 net/http/pprof 包,并启动一个 HTTP 服务器来暴露性能分析数据。
2.1 示例代码
一个带 pprof 的 HTTP 服务器示例:
package main
import (
"net/http"
_ "net/http/pprof"
)
func main() {
// 启动 HTTP 服务器
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, Go!"))
})
http.ListenAndServe(":8080", nil)
}在上述代码中,我们通过 _ "net/http/pprof" 导入 pprof 包,从而在默认的 HTTP 服务器中启用了 pprof 分析功能。
3. 采集和分析性能数据
3.1 采集 CPU 分析数据
在程序运行期间,我们可以使用以下命令采集 CPU 分析数据:
go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30上述命令将采集 30 秒的 CPU 性能数据,并生成一个可供分析的 pprof 报告文件。
3.2 采集内存分析数据
类似地,我们可以采集内存分析数据:
go tool pprof http://localhost:8080/debug/pprof/heap3.3 查找死锁
Go 的 pprof 工具还可以帮助我们查找死锁和其他阻塞操作。可以通过以下命令获取当前所有 goroutine 的堆栈信息:
go tool pprof http://localhost:8080/debug/pprof/goroutine分析 goroutine 的堆栈信息,可以帮助我们识别和定位死锁或其他阻塞问题。
3.4 分析性能数据
我们可以使用 pprof 工具来分析生成的性能数据文件:
go tool pprof [profile_file]进入 pprof 交互界面后,可以使用以下命令来分析数据:
top: 显示 CPU 消耗最大的函数list [function_name]: 查看指定函数的详细信息web: 生成并打开性能数据的可视化图
4. 火焰图
火焰图是一种可视化性能分析结果的工具,通过显示函数调用栈和消耗的时间,帮助我们快速定位性能瓶颈。火焰图的每个矩形代表一个函数,宽度表示该函数的 CPU 时间或内存使用量。
4.1 安装火焰图工具
我们可以使用 Brendan Gregg 提供的 FlameGraph 工具生成火焰图:
git clone https://github.com/brendangregg/FlameGraph.git4.2 生成火焰图
使用 pprof 生成火焰图所需的折叠文件(folded file):
go tool pprof -raw -output=cpu.pprof http://localhost:8080/debug/pprof/profile?seconds=30将 pprof 输出转换为火焰图工具可识别的折叠文件格式:
go tool pprof -raw -output=cpu.raw cpu.pprof使用 FlameGraph 工具生成火焰图:
./FlameGraph/stackcollapse-go.pl cpu.raw > out.folded
./FlameGraph/flamegraph.pl out.folded > cpu_flamegraph.svg5. 示例:分析和优化代码
5.1 示例代码
以下是一个简单的示例代码,该代码在循环中执行计算:
package main
import (
"net/http"
_ "net/http/pprof"
"time"
)
func main() {
go func() {
for {
work()
}
}()
http.ListenAndServe(":8080", nil)
}
func work() {
time.Sleep(1 * time.Second)
}5.2 性能分析
运行示例代码并采集 CPU 分析数据:
go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30分析采集到的性能数据,并生成火焰图:
go tool pprof -raw -output=cpu.pprof http://localhost:8080/debug/pprof/profile?seconds=30
go tool pprof -raw -output=cpu.raw cpu.pprof
./FlameGraph/stackcollapse-go.pl cpu.raw > out.folded
./FlameGraph/flamegraph.pl out.folded > cpu_flamegraph.svg查看生成的火焰图 cpu_flamegraph.svg,定位程序中的性能瓶颈。
6. 通过 pprof 查找死锁和 CPU 消耗最大的逻辑
在性能分析中,除了常规的 CPU 和内存使用分析,我们还需要关注程序中的死锁和 CPU 消耗最大的逻辑。
6.1 查找死锁
运行以下命令查看当前所有 goroutine 的堆栈信息,帮助识别和定位死锁:
go tool pprof http://localhost:8080/debug/pprof/goroutine在 pprof 交互界面中,使用 goroutine 命令查看详细的 goroutine 堆栈信息,分析可能的死锁问题。
6.2 查找 CPU 消耗最大的逻辑
在 pprof 交互界面中,使用 top 命令查看 CPU 消耗最大的函数:
go tool pprof [profile_file]
top使用 list [function_name] 命令查看指定函数的详细信息,帮助定位 CPU 消耗最大的逻辑:
list work找死锁看 goroutine 堆栈,找热点看 top 和 list,都从 pprof 的交互界面进。
小结
pprof 的工作流是三步:导入 net/http/pprof 暴露端点,go tool pprof 采数据,交互界面里用 top、list、web 看结果。火焰图把同一份数据画成图,矩形宽度就是耗时占比。死锁从 goroutine 堆栈查,CPU 热点从 top 查。