目录

Golang 性能分析神器 pprof 最全图文教程

一、简介

pprof 是 Go 标准库提供的性能剖析工具,能够帮助开发者分析程序在运行时的各项性能指标。在我日常的 Go 开发中,pprof 是排查性能问题的首选工具。

核心优势:

  • 零侵入:只需导入包即可使用,无需修改业务代码
  • 低开销:基于采样机制,对生产环境影响极小
  • 可视化:提供火焰图等直观的分析界面
  • 全面性:涵盖 CPU、内存、协程、锁等多个维度

我在实际项目中使用 pprof 成功定位并解决了多次内存泄漏和 CPU 高占用问题,接下来分享一下具体的使用方法。

二、用法

(一)开启 pprof

集成 pprof 非常简单,我通常采用以下方式:

第一步:导入包

import (
    _ "net/http/pprof"
)

第二步:启动 HTTP 服务

go func() {
        logs.Info(http.ListenAndServe(":30552", nil))
    }()

完整示例代码:

import _ "net/http/pprof"

func StartPprof() {
    go func() {
        // 启动 pprof 服务,端口可以根据实际情况调整
        logs.Info(http.ListenAndServe(":30552", nil))
    }()
}

func main(){
  // 其他初始化代码...

  // 启动 pprof 服务(建议在开发和测试环境使用)
  StartPprof() 

  // 启动主程序
  err = router.Run(address)
    if err != nil {
        panic(err)
    }
}

⚠️ 注意事项:

  • 端口号避免与业务端口冲突
  • 生产环境建议通过配置开关控制是否启动
  • 可以配合防火墙规则,限制访问来源

### (二)用法一:浏览器直接访问

最简单的方式是通过浏览器直接访问 pprof 提供的接口:

```bash
# pprof 首页入口
http://0.0.0.0:30552/debug/pprof/ 

/img/go-pprof/0201.png
pprof

实用的分析接口:

# 查看内存堆栈(哪些函数持续占用内存)
http://0.0.0.0:30552/debug/pprof/heap?debug=1

# 查看内存分配情况(哪些函数频繁分配内存)
http://0.0.0.0:30552/debug/pprof/allocs?debug=1

# 查看协程总览
http://0.0.0.0:30552/debug/pprof/goroutine?debug=1
# 查看协程详细堆栈(定位阻塞问题)
http://0.0.0.0:30552/debug/pprof/goroutine?debug=2

# 阻塞分析
http://0.0.0.0:30552/debug/pprof/block?debug=1
http://0.0.0.0:30552/debug/pprof/block?debug=2

# 锁竞争分析
http://0.0.0.0:30552/debug/pprof/mutex?debug=1
http://0.0.0.0:30552/debug/pprof/mutex?debug=2

⚠️ 重要提示:

  • 必须加上 debug 参数,否则会直接下载二进制文件
  • debug=1 显示概要信息,debug=2 显示完整堆栈信息
  • 这种方式适合快速查看,但不够直观

(三)用法二:go tool pprof(强烈推荐)

浏览器直接访问只能看到文本信息,不够直观。我在实际工作中主要使用 go tool pprof 生成火焰图,能够更快速地定位性能瓶颈。

前置条件:需要安装 graphviz 才能正常展示火焰图

graphviz 安装教程请参考:开源的图形可视化工具graphviz安装教程

基本用法:

go tool pprof <参数> <pprof 数据源>

# 示例:启动 web 服务展示最近 60 秒的 CPU 性能数据
go tool pprof -http=0.0.0.0:8081 -seconds=60 http://0.0.0.0:30552/debug/pprof/profile

参数说明:

  • -http= 指定 IP:端口,启动 web 服务展示可视化界面。如果不指定,会下载数据文件并进入命令行交互模式

  • -seconds= 指定采样时长,例如 -seconds=60 表示采样 60 秒的数据。不指定则显示进程启动以来的累计数据

数据源说明:

可以是 pprof 服务接口:http://0.0.0.0:30552/debug/pprof/profile

也可以是本地数据文件:xxx.pb.gz

如何获取 pprof 数据文件:

# 直接访问接口会下载数据文件(同时进入命令行交互模式)
go tool pprof http://0.0.0.0:30552/debug/pprof/profile    # CPU 性能数据
go tool pprof http://0.0.0.0:30552/debug/pprof/heap       # 内存堆栈数据
go tool pprof http://0.0.0.0:30552/debug/pprof/allocs     # 内存分配数据
go tool pprof http://0.0.0.0:30552/debug/pprof/goroutine  # 协程数据
go tool pprof http://0.0.0.0:30552/debug/pprof/block      # 阻塞分析数据
go tool pprof http://0.0.0.0:30552/debug/pprof/mutex      # 锁竞争数据

常用的分析命令:

# CPU 耗时分析(最常用)
go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/profile

# 内存堆栈分析(排查内存泄漏)
go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/heap

# 内存分配分析(查找频繁分配的函数)
go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/allocs

# 协程分析(排查协程泄漏)
go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/goroutine

# 阻塞分析
go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/block

# 锁竞争分析
go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/mutex

接下来我会详细讲解每种分析方式的具体操作步骤和解读方法。

三、CPU 耗时分析实战

这是我使用最频繁的功能,用于定位哪些函数消耗了大量 CPU 时间。

(一)执行命令

go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/profile

执行后浏览器会自动打开 0.0.0.0:8081/ui(如果没有自动打开,手动访问即可)

这里没有加 -seconds= 参数,会显示进程启动以来的累计 CPU 数据

(二)火焰图解读

火焰图示例:

/img/go-pprof/0301.png
CPU耗时分析

关键元素说明:

  • 方框:每个方框代表一个函数,方框越大、颜色越深,表示该函数占用的 CPU 资源越多

  • 颜色含义:

    • 红色:CPU 占用较高的热点函数
    • 绿色:CPU 占用相对较低
    • 颜色深度:直接反映 CPU 占用程度
  • 方框粗细:和颜色类似,越粗表示 CPU 占用越多。如果一个函数又红又粗,说明它是主要的性能瓶颈

  • 箭头类型:

    类型 示例 含义
    普通箭头 FuncA ──→ FuncB 函数直接调用关系
    带时间标签的箭头 FuncA ── 50ms → FuncB FuncA 调用 FuncB 耗时 50ms
    虚线箭头 FuncA ···→ runtime.mallocgc 编译器隐式插入的操作(如内存分配)
    双向箭头 FuncA <──> FuncB 相互调用或递归调用,需要特别关注是否有性能问题
  • (inline) 标记:表示该函数被编译器内联优化处理,这通常是好事

  • 数字含义:

    /img/go-pprof/0302.png
    函数耗时分析

(三)Top 视图分析

访问 0.0.0.0:8081/ui/top 可以看到指标排序视图:

/img/go-pprof/0303.png
CPU指标分析

这个视图和火焰图展示的是同一份数据,只是呈现方式不同,更便于查看具体的代码位置。

指标详解:

  1. flat:函数本身(不包括它调用的子函数)直接消耗的 CPU 时间,单位 ms
  2. flat%:flat / 总时间 × 100%
  3. sum%:当前行及之前所有行的 flat% 累加值
  4. cum:函数总耗时(包含所有子函数调用),单位 ms
  5. cum%:cum / 总时间 × 100%

(四)实战经验总结

根据我的实际使用经验:

  • 看火焰图找热点:哪个框又红又粗,哪个就是 CPU 消耗大户。顺着箭头找到调用链,就能定位到具体代码位置

  • 用 Top 视图确认位置:如果火焰图中函数名不够明确,切换到 Top 视图,可以看到完整的包路径和文件名

  • 优化建议:

    • 优先优化 flat 值大的函数(直接耗时多)
    • 关注 cum 值大但 flat 值小的函数(子函数调用多)
    • 查看具体优化方法可参考文末的【常见优化措施】

四、内存堆栈分析实战

用于排查内存泄漏问题,查看哪些函数长期占用内存不释放。

(一)执行命令

go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/heap

浏览器会自动打开 0.0.0.0:8081/ui

显示进程启动以来的内存堆栈累计数据

(二)火焰图分析

/img/go-pprof/0401.png
pprof内存堆栈分析

分析方法和 CPU 火焰图类似,重点关注又红又粗的方框,这些函数占用了大量常驻内存。

(三)指标分析

/img/go-pprof/0402.png
pprof内存堆栈指标分析

指标详解:

  1. flat:函数本身直接占用的堆内存(不包括子函数),单位 KB
  2. flat%:flat / 总内存 × 100%
  3. sum%:当前行及之前所有行的 flat% 累加
  4. cum:函数总占用内存(包含所有子函数),单位 KB
  5. cum%:cum / 总内存 × 100%

(四)实战技巧

在我实际排查内存泄漏时的经验:

  • 定位常驻内存:火焰图中又红又粗的函数就是内存泄漏的嫌疑对象

  • 结合 Top 视图:通过 Top 视图找到具体的代码文件和行号

  • 常见原因:

    • 全局变量或缓存未设置淘汰机制
    • goroutine 泄漏导致相关数据无法回收
    • 闭包引用导致对象无法释放
    • 定时器未正确关闭
  • 优化建议:详见文末【常见优化措施】

五、内存分配分析实战

和内存堆栈分析不同,这个功能主要用于查找频繁分配内存的函数,即使这些内存很快被释放了,频繁分配也会给 GC 带来压力。

(一)执行命令

go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/allocs

浏览器会自动打开 0.0.0.0:8081/ui

显示进程启动以来的内存分配累计数据

(二)火焰图分析

火焰图示例:

/img/go-pprof/0501.png
pprof内存分配分析

从这个火焰图可以看出:

  1. OperatuonRecord 存在循环调用,需要确认这是否符合业务预期
  2. CreateImgCompress 函数内存分配量很大,需要深入分析其调用的子函数,找出真正的问题点

(三)指标分析

/img/go-pprof/0502.png
pprof内存分配指标分析

指标详解:

  1. flat:函数本身分配的内存(不包括子函数),单位 KB
  2. flat%:flat / 总分配内存 × 100%
  3. sum%:当前行及之前所有行的 flat% 累加
  4. cum:函数总分配内存(包含所有子函数),单位 KB
  5. cum%:cum / 总分配内存 × 100%

(四)实战经验

我在项目中遇到过几次内存频繁分配的问题:

案例 1:字符串拼接 在循环中使用 += 拼接字符串,导致大量内存分配。改用 strings.Builder 后性能提升 10 倍以上。

案例 2:切片扩容 未预分配切片容量,导致频繁触发 growslice。预估容量后使用 make([]T, 0, cap) 显著降低了分配次数。

案例 3:JSON 序列化 高并发场景下使用标准库 encoding/json,CPU 和内存压力都很大。替换为 json-iterator 后问题解决。

优化建议:

  • 关注 flat 值大的函数
  • 查找代码中是否有循环内的内存分配
  • 考虑使用对象池(sync.Pool)复用对象
  • 详细优化方法见文末【常见优化措施】

六、协程分析实战

协程泄漏是 Go 程序中常见但容易被忽视的问题,通过 pprof 可以快速发现。

(一)执行命令

协程分析直接通过浏览器访问即可,无需 go tool pprof:

# 查看协程总数和简要信息
http://0.0.0.0:30552/debug/pprof/goroutine?debug=1 

# 查看每个协程的详细堆栈信息
http://0.0.0.0:30552/debug/pprof/goroutine?debug=2

debug 参数说明:

  • debug=1:显示协程总数和状态概览
  • debug=2:显示每个协程的完整调用栈

(二)数据分析

协程总览(debug=1):

/img/go-pprof/0601.png
pprof协程分析

从图中可以看到进程当前有 27 个协程。协程数量本身没有绝对的好坏,关键是要确认这些协程是否符合业务预期。

协程详细信息(debug=2):

/img/go-pprof/0602.png
pprof协程堆栈信息

这里会列出所有协程的运行堆栈。需要注意的是,协程 ID 越大不代表有问题,重点是看协程所处的状态和堆栈信息。

(三)实战案例:发现协程泄漏

我在一个项目中发现了一个意外的 trace 协程:

/img/go-pprof/0603.png
pprof协程泄漏

通过堆栈信息,我发现是某个第三方包启动的协程。排查代码后确认这个功能并没有实际使用,于是:

  1. 注释掉相关的导入包
  2. 删除未使用的初始化代码
  3. 重启服务后该协程消失

(四)排查协程泄漏的经验

根据我的实际经验,协程泄漏通常有以下几种情况:

1. Channel 阻塞

// 错误示例:goroutine 会永久阻塞
ch := make(chan int)
go func() {
    ch <- 1  // 没有接收方,永久阻塞
}()

2. 死循环未退出

// 错误示例:没有退出条件
go func() {
    for {
        // 业务逻辑
    }
}()

3. 等待锁或资源

// 错误示例:等待永远不会释放的锁
go func() {
    mutex.Lock()
    // 某些条件下 unlock 未执行
}()

排查建议:

  • 定期检查协程数量是否持续增长
  • 关注堆栈中 chan send/receive、select、Lock 等关键字
  • 为协程添加 context 超时机制
  • 确保每个 goroutine 都有明确的退出条件

七、锁竞争分析

在高并发场景下,锁竞争可能成为性能瓶颈。pprof 可以帮助我们找出锁竞争最激烈的地方。

(一)执行命令

http://0.0.0.0:30552/debug/pprof/mutex?debug=1

锁分析也是直接通过浏览器访问 pprof 接口

(二)数据分析

/img/go-pprof/0701.png
pprof锁分析

(三)实战经验

从界面可以看到锁竞争次数最多的堆栈信息,通过这些信息可以:

  1. 确认是否是业务代码:如果是第三方库的锁竞争,可能需要考虑更换实现
  2. 定位代码位置:找到具体的文件和行号
  3. 评估优化必要性:不是所有锁竞争都需要优化,要结合实际性能影响判断

常见优化方案:

  • 缩小锁的粒度(减少临界区代码)
  • 使用读写锁 sync.RWMutex 替代互斥锁
  • 考虑使用 sync.Map 或分段锁
  • 使用 channel 代替共享内存通信

八、阻塞分析

阻塞分析可以帮助我们找出程序中阻塞操作最严重的地方,比如 channel 操作、网络 I/O 等。

(一)执行命令

http://0.0.0.0:30552/debug/pprof/block?debug=1

阻塞分析同样通过浏览器直接访问 pprof 接口

(二)分析思路

阻塞分析展示的是程序中各种阻塞操作的统计信息,主要包括:

  • Channel 发送/接收阻塞:找出 channel 操作的瓶颈
  • 网络 I/O 阻塞:定位网络请求慢的地方
  • 系统调用阻塞:发现系统级别的阻塞

优化建议:

  • 增加 channel 缓冲区大小
  • 为阻塞操作添加超时机制
  • 使用异步处理减少阻塞
  • 优化数据库查询和网络请求

九、单元测试中使用 pprof

除了在运行中的服务上使用 pprof,我们还可以在单元测试中生成性能数据,这对于优化特定函数非常有用。

(一)示例代码

package main

import (
    "fmt"
    "os"
    "runtime/pprof"
)

func main() {
    // ========== CPU 性能分析 ==========
    fcpu, err := os.Create("./cpu.pprof")
    if err != nil {
        fmt.Println("创建 cpu.pprof 文件失败:", err.Error())
        return
    }
    defer fcpu.Close()

    // 开始 CPU 性能采样
    err = pprof.StartCPUProfile(fcpu)
    if err == nil {
        defer pprof.StopCPUProfile()
    }

    // 执行需要测试的代码
    var count int
    for i := 0; i < 10000000; i++ {
        count++
    }

    // ========== 内存性能分析 ==========
    fmem, err := os.Create("./memory.pprof")
    if err != nil {
        fmt.Println("创建 memory.pprof 文件失败:", err.Error())
        return
    }
    defer fmem.Close()

    // 写入内存性能数据
    err = pprof.WriteHeapProfile(fmem)
    if err != nil {
        fmt.Println("写入内存性能数据失败:", err.Error())
        return
    }

    fmt.Println("测试完成,count:", count)
}

(二)分析生成的文件

运行上面的代码后,会生成两个文件:

  • cpu.pprof:CPU 性能数据
  • memory.pprof:内存性能数据

使用 go tool pprof 分析这些文件:

# 分析 CPU 性能
go tool pprof -http=:8081 ./cpu.pprof

# 分析内存性能
go tool pprof -http=:8082 ./memory.pprof

(三)实际应用场景

我在项目中经常用这种方式来:

  1. 对比优化前后的性能:生成优化前后的 pprof 文件,对比差异
  2. 压测特定函数:在单元测试中模拟大量调用,找出性能瓶颈
  3. 基准测试(Benchmark):结合 Go 的 benchmark 功能,更精确地测量性能

十、常见性能优化措施

基于我使用 pprof 排查问题的经验,总结了以下几种最常见且最有效的优化方法。

1. 字符串拼接优化

这是我遇到最多的性能问题之一。在循环中使用 += 拼接字符串,性能极差。

错误示例:

var result string
for i := 0; i < 10000; i++ {
    result += "test"  // 每次都会重新分配内存
}

正确做法:

var builder strings.Builder
builder.Grow(40000)  // 预分配容量
for i := 0; i < 10000; i++ {
    builder.WriteString("test")
}
result := builder.String()

性能对比:使用 strings.Builder 比 += 快 100 倍以上

详细分析可参考:Go语言字符串拼接性能对比与最佳实践 - 深度优化指南

2. 数据库查询优化

在 pprof 中看到数据库相关函数耗时长,通常是慢查询导致。

排查方法:

  • 开启数据库慢查询日志
  • 使用 EXPLAIN 分析查询计划
  • 检查是否缺少索引
  • 避免 SELECT *,只查询需要的字段
  • 批量操作替代单条操作

实际案例: 我曾遇到一个接口响应慢的问题,通过 pprof 发现是一个查询耗时 500ms。添加联合索引后,降到了 5ms,性能提升 100 倍。

3. 切片/Map 预分配容量

如果在 pprof 中看到 growslice 或 mapassign 频繁出现,说明切片或 map 在不断扩容。

优化方法:

// 不好的做法
var slice []string
var m map[string]int

// 好的做法:预分配容量
slice := make([]string, 0, expectedSize)
m := make(map[string]int, expectedSize)

效果:减少内存分配次数,降低 GC 压力。

4. 避免协程泄漏

通过 goroutine 分析发现协程数量持续增长,需要及时处理。

最佳实践:

// 使用 context 控制协程生命周期
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

go func(ctx context.Context) {
    select {
    case <-ctx.Done():
        return  // 超时或取消时退出
    case result := <-ch:
        // 处理结果
    }
}(ctx)

5. JSON 序列化性能优化

标准库 encoding/json 在高并发场景下性能不足。

替代方案:

// 使用 json-iterator 替代标准库
import jsoniter "github.com/json-iterator/go"

var json = jsoniter.ConfigCompatibleWithStandardLibrary

// 使用方式和标准库一样
data, err := json.Marshal(obj)

性能提升:比标准库快 2-3 倍,内存分配也更少。

6. 使用对象池(sync.Pool)

对于频繁创建和销毁的对象,使用对象池可以显著降低 GC 压力。

示例:

var bufferPool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}

// 获取对象
buf := bufferPool.Get().(*bytes.Buffer)
buf.Reset()  // 重置状态
defer bufferPool.Put(buf)  // 使用完放回池中

// 使用 buf...

7. 避免反射和类型断言

pprof 中看到大量 reflect 相关调用,说明使用了过多反射。

优化建议:

  • 用代码生成替代反射
  • 使用泛型(Go 1.18+)
  • 缓存反射结果

8. 并发控制

无限制地创建 goroutine 会导致资源耗尽。

使用协程池:

// 使用 semaphore 限制并发数
sem := make(chan struct{}, 100)  // 最多 100 个并发

for _, item := range items {
    sem <- struct{}{}  // 获取信号量
    go func(item Item) {
        defer func() { <-sem }()  // 释放信号量
        // 处理任务
    }(item)
}

使用 worker pool: 推荐使用 ants、tunny 等成熟的协程池库。


以上是我在实际项目中总结的优化经验,具体使用哪种方法,需要根据 pprof 分析结果来决定。记住:先测量,再优化,不要过早优化。

十一、常见问题解答

1. 开启 pprof 会影响服务性能吗?

会有一定影响,但通常可以忽略。

pprof 采用采样机制,对性能的影响通常在 1%-5% 之间。不过在以下情况下影响可能更明显:

  • 高并发场景下长时间采样
  • 频繁采样 CPU 或内存数据
  • 在极度追求性能的场景(如游戏服务器)

我的建议:

  • 开发和测试环境可以常驻开启
  • 生产环境按需开启,排查完问题及时关闭
  • 使用配置开关控制 pprof 的启用

2. heap 和 allocs 有什么区别?

这是我经常被问到的问题,两者的区别很关键:

维度 heap(内存堆栈分析) allocs(内存分配分析)
关注点 当前存活的对象 所有分配行为(包含已释放的对象)
数据来源 实时内存快照 内存分配器事件采样
时间维度 空间维度(当前占用) 时间维度(历史累计)
关键指标 inuse_space / inuse_objects alloc_space / alloc_objects
适用场景 内存泄漏 / 常驻内存过大 GC 压力 / 分配热点 / 频繁短命对象
分析重点 “谁一直占着内存不放” “谁在不断申请内存”

快速记忆:

  • 查找内存泄漏 → 用 heap
  • 查找频繁分配 → 用 allocs

实战案例: 我曾遇到一个服务,内存使用稳定但 GC 频繁。通过 heap 看内存占用不高,但通过 allocs 发现某个函数在循环中频繁创建临时对象。优化后 GC 次数降低了 80%。

3. 如何分析 CPU 耗时?

参见【三、CPU 耗时分析实战】章节。

快速步骤:

  1. 执行 go tool pprof -http=:8081 http://0.0.0.0:30552/debug/pprof/profile
  2. 查看火焰图,找"又红又粗"的方框
  3. 在 Top 视图中确认具体代码位置
  4. 针对性优化

4. 如何分析内存占用?

参见【四、内存堆栈分析实战】章节。

要点:

  • 查看 heap 火焰图
  • 关注 cum 和 flat 指标
  • 排查是否有全局变量、缓存未清理、goroutine 泄漏

5. 如何分析内存分配?

参见【五、内存分配分析实战】章节。

要点:

  • 使用 allocs 火焰图
  • 找出频繁分配的函数
  • 重点关注循环中的内存分配

6. 如何排查协程泄漏?

参见【六、协程分析实战】章节。

排查步骤:

  1. 访问 http://0.0.0.0:30552/debug/pprof/goroutine?debug=2
  2. 查看协程总数是否持续增长
  3. 检查堆栈中是否有 channel 阻塞、锁等待、死循环
  4. 为 goroutine 添加退出机制和超时控制

7. pprof 控制台模式常用命令

不使用 -http 参数时,go tool pprof 会进入命令行交互模式。

示例:

$ go tool pprof http://0.0.0.0:30552/debug/pprof/profile
Fetching profile over HTTP from http://0.0.0.0:30552/debug/pprof/profile
Type: cpu
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 150ms, 83.33% of 180ms total
      flat  flat%   sum%        cum   cum%
      30ms 16.67% 16.67%       40ms 22.22%  runtime.lock2
      30ms 16.67% 33.33%       30ms 16.67%  syscall.Syscall
      ...

常用命令:

命令 说明
help 查看帮助信息,列出所有支持的命令
top 按指标大小列出前 10 个函数
top 20 列出前 20 个函数
list 函数名 查看指定函数的详细分析(包括源代码)
traces 打印所有调用栈及其指标信息
web 生成 SVG 图形并在浏览器中打开(需要 graphviz)
pdf 生成 PDF 格式的报告
exit / quit 退出交互模式

个人建议: 命令行模式功能强大但不够直观,我更推荐使用 -http 参数启动 web 服务,通过浏览器查看,体验更好。

8. 如何对比优化前后的性能?

方法一:生成两份 pprof 文件对比

# 优化前
go tool pprof -output=before.pprof http://0.0.0.0:30552/debug/pprof/profile

# 优化后
go tool pprof -output=after.pprof http://0.0.0.0:30552/debug/pprof/profile

# 对比两个文件
go tool pprof -http=:8081 -base=before.pprof after.pprof

方法二:使用 benchmark

func BenchmarkOld(b *testing.B) {
    for i := 0; i < b.N; i++ {
        // 优化前的代码
    }
}

func BenchmarkNew(b *testing.B) {
    for i := 0; i < b.N; i++ {
        // 优化后的代码
    }
}

运行:

go test -bench=. -benchmem

这样可以直观地看到性能提升和内存分配的变化。


全文总结:

pprof 是 Go 性能优化的利器,掌握它能大幅提升问题排查效率。我的建议是:

  1. 先运行再优化:不要过早优化,用 pprof 找到真正的瓶颈
  2. 多维度分析:CPU、内存、协程要综合看,往往问题相互关联
  3. 持续监控:定期采样分析,及时发现潜在问题
  4. 实践出真知:多在实际项目中使用,积累经验

希望这篇教程能帮助你快速上手 pprof,提升 Go 程序的性能!

版权声明

未经授权,禁止转载本文章。
如需转载请保留原文链接并注明出处。即视为默认获得授权。
未保留原文链接未注明出处或删除链接将视为侵权,必追究法律责任!

本文原文链接: https://fiveyoboy.com/articles/go-pprof/

备用原文链接: https://blog.fiveyoboy.com/articles/go-pprof/