扫码订阅《 》或入驻星球,即可阅读文章!

GOLANG ROADMAP

阅读模式

  • 沉浸
  • 自动
  • 日常
首页
Go学习
  • Go学院

    • Go小课
    • Go视界
    • Go小考
    • Go实战
  • Go资源

    • 优质课程
    • 在线宝典
    • 资源下载
    • 帮找资源
训练营 🔥
  • Go体系课&实战训练营
  • 升值加薪陪跑训练营
Go求职
  • 求职刷题

    • 企业题库
    • 面试宝典
    • 求职面经
  • 求职服务

    • 内推互助
    • 求职助力
    • 内推公司
Go友会
  • 城市
  • 校园
推广返佣
  • 返佣排行
  • 返佣规则
  • 推广学院
实验区
  • Go周边
  • Go宝典

    • 推荐图书
    • 精品博文
  • Go开源

    • Go仓库
    • Go月刊
更多
  • 用户中心

    • 我的信息
    • 我的返佣
    • 我的消息
  • 玩转星球

    • 星球介绍
    • 星主权益
    • 吐槽专区
    • 成长记录
  • 合作交流

    • 商务合作
    • 讲师招募
    • 生态伙伴
author-avatar

GOLANG ROADMAP


首页
Go学习
  • Go学院

    • Go小课
    • Go视界
    • Go小考
    • Go实战
  • Go资源

    • 优质课程
    • 在线宝典
    • 资源下载
    • 帮找资源
训练营 🔥
  • Go体系课&实战训练营
  • 升值加薪陪跑训练营
Go求职
  • 求职刷题

    • 企业题库
    • 面试宝典
    • 求职面经
  • 求职服务

    • 内推互助
    • 求职助力
    • 内推公司
Go友会
  • 城市
  • 校园
推广返佣
  • 返佣排行
  • 返佣规则
  • 推广学院
实验区
  • Go周边
  • Go宝典

    • 推荐图书
    • 精品博文
  • Go开源

    • Go仓库
    • Go月刊
更多
  • 用户中心

    • 我的信息
    • 我的返佣
    • 我的消息
  • 玩转星球

    • 星球介绍
    • 星主权益
    • 吐槽专区
    • 成长记录
  • 合作交流

    • 商务合作
    • 讲师招募
    • 生态伙伴
  • Go工程化规范设计

    • 前言
    • 开源规范
    • 文档规范
    • 版本规范
    • Git规范
    • 目录结构
    • 编码规范
    • 代码测试
    • 性能分析
    • API 设计
    • 项目管理
    • 研发流程
    • 参考资料
  • Go工程化标准实践

    • 前言
    • 项目结构
    • API 设计
    • 配置管理
    • 模块管理
    • 测试
    • 参考资料

扫码订阅《 》或入驻星球,即可阅读文章!

性能分析


Kyle

# 采集数据

使用 Go 命令行工具生成性能数据文件:

  • v1.test,测试生成的二进制文件,进行性能分析时用于解析各种符号。
  • cpu.profile,CPU 性能数据文件。
  • mem.profile,内存性能数据文件。
go test -benchtime=30s -benchmem -bench=".*" -cpuprofile cpu.profile -memprofile mem.profile
# goos: linux
# goarch: amd64
# pkg: github.com/marmotedu/iam/internal/apiserver/service/v1
# cpu: AMD EPYC Processor
# BenchmarkListUser-8          280     4283077 ns/op
# PASS
# ok    github.com/marmotedu/iam/internal/apiserver/service/v1  1.798s
1
2
3
4
5
6
7
8

或使用代码生成:

func main() {
    cpuOut, _ := os.Create("cpu.out")
    defer cpuOut.Close()
    pprof.StartCPUProfile(cpuOut)
    defer pprof.StopCPUProfile()

    memOut, _ := os.Create("mem.out")
    defer memOut.Close()
    defer pprof.WriteHeapProfile(memOut)

    Sum(3, 5)

}

func Sum(a, b int) int {
    return a + b
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
go run pprof.go
1

要分析 HTTP Server 性能,或使用 net/http/pprof 包(参考 pprof/pprof.go (opens new window)、iam/genericapiserver.go (opens new window)):

func Register(r *gin.Engine, prefixOptions ...string) {
    prefix := getPrefix(prefixOptions...)

    prefixRouter := r.Group(prefix)
    {
        ...
        prefixRouter.GET("/profile", pprofHandler(pprof.Profile))
        ...
    }
}

func pprofHandler(h http.HandlerFunc) gin.HandlerFunc {
    handler := http.HandlerFunc(h)
    return func(c *gin.Context) {
        handler.ServeHTTP(c.Writer, c.Request)
    }
}

// 要开启 HTTP 性能分析,只需注册 HTTP Handler。
// 在启动服务后,访问:http:// x.x.x.x:8080/debug/pprof 查看 profiles 信息。
if s.enableProfiling {
    pprof.Register(s.Engine)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 获取性能数据文件
curl http://127.0.0.1:8080/debug/pprof/profile -o cpu.profile
curl http://127.0.0.1:8080/debug/pprof/heap -o mem.profile
1
2
3

生成采用数据后,可按以下思路分析性能:

  • 采样图:矩形面积最大。
  • 火焰图:格子最宽。
  • go tool pprof:cum% 最大。

# 分析采样图

以 CPU 分析为例。Go 运行时默认以 100 Hz 的频率对 CPU 使用情况采样,即每秒采样 100 次、每 10 毫秒采样一次。每次采样时记录正在运行的函数,并统计其运行时间,生成 CPU 性能数据(cpu.profile)。

先安装 graphviz:

yum -y install graphviz.x86_64
1

生成调用图

go tool pprof -svg cpu.profile > cpu.svg    # svg 格式
go tool pprof -pdf cpu.profile > cpu.pdf    # pdf 格式
go tool pprof -png cpu.profile > cpu.png    # png 格式
1
2
3

其中有向线段描述了函数的调用关系(A 调用 B,A -> B)。

矩形面积越大,表示累积采样时间越大。其中包含采样数据:

  • 函数 / 方法名:包含包名、结构体名、函数名 / 方法名,方便快速定位,例如 fake(*policies)List 表示 fake 包,policies 结构体的 List 方法。
  • 本地采样时间:以及在采样总数中所占的比例。指采样点落在该函数中的总时间(只包含该函数本身、不包含函数体内调用其它函数占用的时间)。该数值较大说明函数本身耗时较大,应集中分析。
  • 累积采样时间:以及在采样总数中所占的比例。指采样点落在该函数,以及被它直接或者间接调用的函数中的总时间(整个函数执行耗时)。该数值较大未必是该函数本身有问题,可能是其调用的函数有性能瓶颈。

# 分析火焰图

火焰图可把采样到的堆栈轨迹(Stack Trace)转化为直观图片显示。

使用 pprof 工具打开数据文件,可在浏览器中直观查看数据:

go tool pprof -http="0.0.0.0:8081" v1.test cpu.profile
1

其中数据采样视图:

  • Top,类似于 linux top 的形式,从高到低排序。
  • Graph,默认弹出来的就是该模式,也就是上一个图的那种带有调用关系的图。
  • Flame Graph:pprof 火焰图。
  • Peek:类似于 Top 也是从高到底的排序。
  • Source:和交互命令式的那种一样,带有源码标注。
  • Disassemble:显示所有的总量。

选择火焰图(Flame Graph):格子越宽的函数,就越可能存在性能问题。

  • 每列代表一个调用栈,每个格子代表一个函数。
  • 纵轴展示了栈的深度,按照调用关系从上到下排列。最下面的格子代表采样时正在占用 CPU 的函数。
  • 调用栈在横向会按照字母排序,并且同样的调用栈会做合并。格子的宽度越大,表示该函数越可能是瓶颈。
  • 火焰图格子的颜色是随机的暖色调,方便区分各个调用信息。

# 分析数据

交互式查看 CPU 性能数据文件:

  • File,二进制可执行文件名称。
  • Type,采样文件的类型,例如 cpu、mem 等。
  • Time,生成采样文件的时间。
  • Duration,程序执行时间。程序在采样时,会自动分配采样任务给多个核心,总采样时间可能会大于总执行时间。
  • (pprof),命令行提示,表示当前在 go tool 的 pprof 工具命令行中(还包括 cgo、doc、pprof、trace 等)。
go tool pprof v1.test cpu.profile
# File: v1.test
# Type: cpu
# Time: Aug 17, 2021 at 2:17pm (CST)
# Duration: 56.48s, Total samples = 440ms ( 0.78%)
# Entering interactive mode (type "help" for commands, "o" for options)
# (pprof)
1
2
3
4
5
6
7

常用命令:

cum:表示采样点落在该函数中的,以及被它调用的函数中的总时间(和百分比)。

内存的性能分析与 CPU 相似:Go 运行时系统会记录程序运行期间的所有堆内存分配。在采样的任何时刻,不管堆内存已用字节数是否有增长,只要有字节被分配且数量足够,分析器就会对它进行采样。

优化的目标是减少内存分配次数以及每次分配的内存大小。

  • 采集数据
  • 分析采样图
  • 分析火焰图
  • 分析数据