Go pprof 认知到实践
快速开始
测试环境:go version go1.22.2 windows/amd64,源代码开源在 https://github.com/oldme-git/teach-study/tree/master/golang/base/pprof
在正式开始之前,请确保安装 graphviz,这一步不可省略,它可以协助 pprof 生成更直观的数据分析图。可以参考官方网站的安装方法。
go 使用 runtime/pprof 包来对程序进行采样,当然,还有另外一个包 net/http/pprof,这里先按下不表。先来看一个 CPU 分析的例子:
package main
import (
"math"
"math/rand"
"os"
"runtime/pprof"
)
func main() {
// 保存 CPU 采样数据
file := "cpu.pprof"
os.Remove(file)
f, err := os.OpenFile(file, os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
panic(err)
}
defer f.Close()
// 开始采样
err = pprof.StartCPUProfile(f)
if err != nil {
panic(err)
}
defer pprof.StopCPUProfile()
// 测试程序
for i := 0; i < 1000; i++ {
nums := genRandomNumbers(10000)
for _, v := range nums {
_ = math.Pow(float64(v), rand.Float64())
}
}
}
// 测试程序,生成一个随机数切片
func genRandomNumbers(n int) []int {
nums := make([]int, n)
for i := 1; i < n; i++ {
nums[i] = rand.Int() * n
}
return nums
}
这是一个很简单的例子,运行 go run main.go 在当前目录下生成一个 cpu.pprof 文件。然后输入命令 go tool pprof cpu.pprof 进入 pprof 的命令行中。
PS D:\project\teach-study\golang\base\pprof\cpu> go run main.go
PS D:\project\teach-study\golang\base\pprof\cpu> go tool pprof cpu.pprof
File: main.exe
Build ID: C:\Users\half\AppData\Local\Temp\go-build787417447\b001\exe\main.exe2024-05-08 11:13:12.7105156 +0800 CST
Type: cpu
Time: May 8, 2024 at 11:13am (CST)
Duration: 1.26s, Total samples = 1.07s (85.20%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
pprof 命令有很多,可以输入 help 查看,不过一般常用的就两个: top 和 web。输入 top5 可以查看前 5 的耗时调用。
// 输入
top5
// 输出
Showing nodes accounting for 700ms, 65.42% of 1070ms total
Showing top 5 nodes out of 69
flat flat% sum% cum cum%
210ms 19.63% 19.63% 210ms 19.63% math.archLog
180ms 16.82% 36.45% 180ms 16.82% math.archExp
160ms 14.95% 51.40% 670ms 62.62% math.pow
80ms 7.48% 58.88% 80ms 7.48% internal/chacha8rand.block
70ms 6.54% 65.42% 70ms 6.54% math/rand.globalRand
来认识一下这五个指标:
| flat | 是我们最关注的指标,它代表自身耗时,不包含内部调用。 |
| falt% | 自身耗时相对于总耗时的百分比 |
| cum | 自身耗时加上内部函数调用的总耗时 |
| cum% | 自身耗时加上内部函数调用的总耗时相对于总耗时的百分比 |
| sum% | 前 N 行的 flat% 之和。对于上述例子的第四行 58.88=19.63+16.82+14.95+7.48 |
只是依赖文字无法很好的理解这些指标,我们可以使用 web 命令来生成更直观的 svg 分析图。输入 web 命令后,会自动在浏览器打开 svg:

svg 中的每个单元格包含了包名,函数名,flat, flat%, cum, cum% :

单元格颜色越红,代表 cum 越大,反之越小;单元格越大,代表 flat 越大,反之越小。单元格之间的箭头线代表调用链,线越粗代表消耗的更多的资源,反之亦然。带有 inline 字段表示该函数被内联进了调用方(当作普通线处理就行)。
函数调用是存在一些固定开销的,例如维护帧指针寄存器BP、栈溢出检测等。因此,对于一些代码行比较少的函数,编译器倾向于将它们在编译期展开从而消除函数调用,这种行为就是内联。
更多的 Web UI
通过 web 命令已经可以获取很直观的性能分析图,我们还可以使用 -http 参数来启用一个 web 服务,获取更多的性能分析。输入 exit 退出 pprof 命令界面,输入命令:
go tool pprof -http=:7000 cpu.pprof
之后会自动在浏览器打开 http://localhost:7000/ui/。

在 view 中可以使用火焰图(Flame Graph),火焰图有新旧两种,可以根据线的长短和颜色判断 CPU 耗时。其他的选项可以点点看看,不复杂,很容易就学会了。

web 服务采样
对于 web 服务的 pprof 采样,我们可以使用基于 runtime/pprof 封装的更便捷的 net/http/pprof 包。
package main
import (
"fmt"
"math"
"math/rand"
"net/http"
_ "net/http/pprof"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// 测试程序
for i := 0; i < 1000; i++ {
nums := genRandomNumbers(10000)
for _, v := range nums {
_ = math.Pow(float64(v), rand.Float64())
}
}
fmt.Fprint(w, "Hello, world!")
})
http.ListenAndServe(":8080", nil)
}
// 测试程序,生成一个随机数切片
func genRandomNumbers(n int) []int {
nums := make([]int, n)
for i := 1; i < n; i++ {
nums[i] = rand.Int() * n
}
return nums
}
打开 http://127.0.0.1:8080/debug/pprof/ ,可以看到 pprof 的实时采样数据:

这里面一共有九个采样数据:
| allocs | 查看历史累计的所有内存分配的采样数据 |
| block | 查看历史累计的导致同步原语阻塞的堆栈跟踪 |
| cmdline | 包含进程的完整命令行信息,通常用于记录程序启动时的命令行参数 |
| goroutine | 实时查看当前所有运行的 goroutines 堆栈跟踪 |
| heap | 实时查看活动对象的内存分配情况 |
| mutex | 查看历史累计的导致互斥锁的竞争持有者的堆栈跟踪 |
| profile | 进行 30s 的 CPU Profiling,浏览器会转圈,30s 后下载一个分析用的 profile 文件 |
| threadcreate | 查看创建新 OS 线程的堆栈跟踪 |
| trace | 程序执行 trace, 和其他样本数据不同的是,这个需要使用 go tool trace 来分析。trace 是一种更详细的性能分析工具,用于跟踪程序的执行过程,包括函数调用、协程切换等。 |
默认是不追踪 block 和 mutex 的,如果需要,在代码中加入这两个:
runtime.SetBlockProfileRate(1) // 开启 block
runtime.SetMutexProfileFraction(1) // 开启 mutex
这些信息都是实时变化的,刷新一下浏览器即可看见,但是这些信息不易阅读,我们可以把它们下载下来,使用 pprof 分析,以 allocs 为例:
// 下载 allocs 数据
curl -o allocs.pprof http://localhost:8080/debug/pprof/allocs
// pprof
go tool pprof .\allocs.pprof
非 Web 程序的其他采样
在快速开始部分已经介绍了 CPU 采样,对于其他采样,可以参考这段代码:
package main
import (
"math"
"math/rand"
"os"
"runtime/pprof"
)
func main() {
// 保存 CPU 采样数据
file := "allocs.pprof"
os.Remove(file)
f, err := os.OpenFile(file, os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
panic(err)
}
defer f.Close()
// 测试程序
for i := 0; i < 1000; i++ {
nums := genRandomNumbers(10000)
for _, v := range nums {
_ = math.Pow(float64(v), rand.Float64())
}
}
pprof.Lookup("allocs").WriteTo(f, 0)
}
// 测试程序,生成一个随机数切片
func genRandomNumbers(n int) []int {
nums := make([]int, n)
for i := 1; i < n; i++ {
nums[i] = rand.Int() * n
}
return nums
}
Go pprof 认知到实践的更多相关文章
- 关于Prometheus运维实践项目
关于Promethues运维实践项目 1. 什么是Prometheus运维实践项目 是什么 Prometheus,普罗米修斯,是古希腊神话中为人间带来火种的神. Prometheus运维实 ...
- 实战Go内存泄露【转】
最近解决了我们项目中的一个内存泄露问题,事实再次证明pprof是一个好工具,但掌握好工具的正确用法,才能发挥好工具的威力,不然就算你手里有屠龙刀,也成不了天下第一,本文就是带你用pprof定位内存泄露 ...
- 这是我用Microsoft Word 2010 直接发布的测试用博客
目的:如题所示. 那么先试试拷贝一段网页内容,发布后观察各种格式的显示效果如何. 下面的文字来自中国网新闻,地址是http://news.china.com.cn/2015-10/23/conte ...
- 20165326 学习基础和c语言基础调查
学习基础和c语言基础调查 一.关于个人技能 阅读了娄老师关于做中学的文章,我想起了自己之前学习技能的经历. 从小到大我学过的东西不少,除学校的教育课程外,我还参加过各种兴趣班,书法.绘画.舞蹈.吉他. ...
- Javascript中的原型链、prototype、__proto__的关系
javascript 2016-10-06 1120 9 上图是本宝宝用Illustrator制作的可视化信息图,希望能帮你理清Javascript对象与__proto__.prototype和 ...
- 为管理复杂组件状态困扰?试试 vue 简单状态管理 Store 模式【转】
https://juejin.im/post/5cd50849f265da03a54c3877 在 vue 中,通信有几种形式: 父子组件 emit/on vuex 中共享 state 跨组件 Eve ...
- Linux学习笔记-第9天 与windows相同的分区知识
可能是因为之前自己已经对分区.引导.操作系统已经有了足够的认知和实践经验,也可能是因为这个知识已经看了三遍的原因.最近笔记有点少. 不是特别同意老师 新硬盘也可以不分区的关点.默认硬盘不分区应该是不能 ...
- 前端如何低门槛开发iOS、Android、小程序多端应用
现如今跨平台开发技术已不是什么新鲜话题了,在市面上也有一些开源的框架可供选择,然而技术成熟.产品服务健全的平台并不多,其中也不乏推陈出新的框架值得关注. 比如最近使用的AVM,由APICloud迭代推 ...
- 2022年Web前端开发流程和学习路线(详尽版)
前言 前端侧重于人机交互和用户体验,后端侧重于业务逻辑和大规模数据处理.理论上,面向用户的产品里,所有问题(包括产品.设计.后端.甚至看不见的问题)的表现形式,都会暴露在前端,而只有部分问题(数据问题 ...
- 研发效能DevOps推荐书单
专注 300 页之内的经典书籍推荐 研发效能涉及的知识很多,从大的方向去划分包括制度.组织.平台.运营等:单从软件研发的角度去看也包括很多,包括最底层的软工认知.实践,到团队管理和组织.敏捷研发,项目 ...
随机推荐
- KingbaseES V8R6 集群运维案例-- sys_internal.init.*文件引起sys_basebackup失败
案例说明: KingbaseES V8R6集群在执行'repmgr standby clone'或sys_basebackup克隆备库时出现如下图相关sys_internal.init文件错误: 适用 ...
- KingbaseES V8R6数据库运维案例之---索引坏块故障处理
案例说明: 在执行表数据查询时,出现下图所示错误,索引故障导致表无法访问,后重建索引问题解决.本案例复现了此类故障解决过程. 适用版本: KingbaseES V8R3/R6 一.创建测试环境 # 表 ...
- KingbaseES V8R6 空闲事务会话超时自动终止机制
背景 如果会话在事务中停留的时间过长,则允许自动终止空闲会话.可以由配置参数idle_in_transaction_session_timeout 事务处于空闲状态的时长,它有助于防止被遗忘的交易事务 ...
- CentOS 7 上搭建nginx来部署静态网页
目录 0. Nginx简介 1. 安装以及使用 1.1 安装和启动 1.2 配置服务器的访问地址 1.3 重启nginx,打开浏览器访问 0. Nginx简介 Nginx (engine x) 是一个 ...
- Linux中JMeter的使用
Linux中JMeter的使用 Linux版本JMeter安装 # 1.下载.安装JMeter 如果有安装包直接上传即可 wget -c https://archive.apache.org/dist ...
- #莫比乌斯反演,整除分块#洛谷 6222 「P6156 简单题」加强版
题目 多组询问,给出\(n,k\) 求 \[\sum_{i=1}^n\sum_{j=1}^n(i+j)^kgcd(i,j)\mu^2(gcd(i,j)) \] 对\(\text{unsigned}\) ...
- OpenHarmony社区运营报告(2023年12月)
• 截至2023年12月22日,OpenAtom OpenHarmony(简称"OpenHarmony")社区累计超过6700名贡献者,产生26.9万多个PR,2.4万多个St ...
- vue3探索——使用ref与$parent实现父子组件间通信
在vue3中,可以使用vue3的API defineExpose()函数结合ref或者$parent,实现父子组件数据的传递. 子组件向父组件传递数据defineExpose()和ref 子组件:通过 ...
- Ubuntu SVN 需要证书及密码验证问题
问题概览 问题一 Ubuntu 20.04 下使用 SVN ,会报错 SVN 的证书错误,无论是选择接受 t 还是永久接受 p,下次都会要求再次接受:在 kali 或者 Windows 上没有出现该问 ...
- Mysql系列:Mysql5.7编译安装--系统环境:Centos7 / CentOS9 Stream
Mysql系列:Mysql5.7编译安装 系统环境:Centos7 / CentOS9 Stream 1:下载mysql源码包 https://dev.mysql.com/downloads/mysq ...