Go 性能优化指南 | 青训营笔记

80 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天

Go 语言 - 性能优化指南


课程目标: 学习Go项目性能优化工具的使用方法

字符串处理

使用Strings.Builder

在处理字符串拼接的过程中,有三种处理方式 :使用 +strings.Builderbytes.Buffer

其中,使用 + 拼接性能最差,strings.Builder , strings.Buffer 相近,strings.Builder 更快

分析:

  • 字符串在 Go 语言中是不可变类型,占用内存大小是固定的
  • 使用 + 每次都会重新分配内存
  • strings.Builderbytes.Buffer 底层都是 []byte 数组
  • 内存扩容策略,不需要每次拼接重新分配内存

空结构体

使用空结构体节省内存

空结构体 struct{} 不占用任何的内存空间,可以在任何场景下当做占位符来使用

使用空结构体实现 Set

我们在实现 Set 的时候,可以考虑使用 map 来替代。替代的时候,我们只需要只用 map 的键,而不需要值。即使值复制为 布尔类型,都会占用一个字节的内存。

atomic 包

实现多线程计数器的时候,除了给公共变量加锁以外,还可以使用 atomic 来实现计数器

分析:

  1. 锁的实现是通过操作系统来实现的,属于系统调用
  2. atomic 操作是通过硬件来实现的,效率比直接加锁高
  3. sync.Mutex 应该用来保护一段代码逻辑,不仅仅用来保护一个变量
  4. 对于非数值操作,可以使用 atomic.Value, 可以承载一个 interface {}

Go 性能优化分析工具

pprof

项目实战

cpu

go tool pprof "http://localhost:6060/debug/pprof/profile?second=10" # 这里只采集10s

top命令

进入 pprof 界面后,输入 top 就可以得到函数占用资源的多少

指令含义
flat当前函数本身的执行耗时
flat%flat 占 CPU 总时间的比例
sum%上面每一行的 flat% 的总和
cum指当前函数本身加上其调用函数的总耗时
cum%cum 占CPU 总时间的比例

观察上图可以看到,有的函数 flat == cum 有的函数 flat == 0

  • flat == cum : 说明函数中没有调用其他函数,
  • flat == 0 : 说明该函数中只有其他函数的调用

list 命令

由上图可知,消耗 CPU 资源最大的函数是排在第一位的 Eat 函数,此时我们可以使用list 函数对它进行查看

可以看到代码中第 2424 行这个 for 循环使用了 9.15s9.15s 的 CPU,耗时最长。

web 命令

使用 web 命令可以看到程序执行过程中的可视化

内存

 go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"

可以看到 Mouse 占用的内存最高


image-20230117223714005.png

通过 VIEW 中够可以看到代码来源,火焰图等图标分析

image-20230117223834830.png

SAMPLE 中我们还可以看到四个指标:

  • alloc_objects : 程序累计申请的对象数
  • alloc_space : 程序累计申请的内存大小
  • inuse_objects : 程序当前持有的对象数
  • inuse_space : 程序当前占用的内存大小
goroutine - 协程
 go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/goroutine"

进入后点击火焰图查看协程的 CPU 占用情况

image-20230117224338299.png

  1. 从上到下表示每个协程的调用顺序
  2. 每一块代表一个函数,块越长表示占用 CPU 的时间越长
  3. 火焰图是动态的,可以点击块进行分析

切换到 Source 视图,搜索排在第一的 wolf 函数

可以看到,该函数每次开启 5050 个协程,且等待 30s30s 后才退出,占用非常多的 CPU

Mutex - 锁
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/mutex"

仍然和前面一样,查看哪一个函数调用最多的锁,然后切换到 source 视图,查看源代码

block - 阻塞
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/block"

仍然和前面一样,查看哪一个函数阻塞时间最长,然后切换到 source 视图,查看源代码

小细节

image-20230117225224954.png

在使用 top 命令的时候,它会自动过滤节点,将小于某个时间段的节点自动丢弃,图中就将运行时长小于 0.04s 的节点扔掉了,共扔掉了 72 个,如果想要详细分析这些节点,可以进入网页可视化图中查看。