高质量编程与性能调优实战 | 青训营

165 阅读6分钟

01高质量编程

1.1简介

什么是高质量

  1. 各种边界条件是否考虑完备
  2. 异常情况处理,稳定性保证
  3. 易读易维护

编程原则

  1. 简单性
  2. 可读性
  3. 生产力

1.2编码规范

如何编写高质量的Go 代码

  1. 代码格式: 推荐使用gofmt自动格式化代码

  2. 注释: 注释应该解释代码作用; 注释应该解释代码如何做的; 注释应该解释代码实现的原因; 注释应该解释代码什么情况会出错.

  3. 命名规范: 简洁胜于冗长; 缩略词全大写,但当其位于变量开头且不需要导出时,使用全小写; 变量距离其被使用的地方越远,则需要携带越多的上下文信息;

  4. 控制流程: 避免嵌套,保持正常流程清晰; 尽量保持正常代码路径为最小缩进;

  5. 错误和异常处理: error 尽可能提供简明的上下文信息链,方便定位问题; panic 用于真正异常的情况; recover 生效范围,在当前goroutine 的被defer 的函数中生效;

1.3性能优化建议

简介:

  • 性能优化的前提是满足正确可靠、简洁清晰等质量因素
  • 性能优化是综合评估,有时候时间效率和空间效率可能对立
  • 针对Go语言特性,介绍Go相关的性能优化建议

1.3.1性能优化建议-Benchmark

  • 性能表现需要实际数据衡量
  • Go语言提供了支持基准性能测试的benchmark工具 结果说明
PS E:\Users\Dell\GolandProjects\pprof> go test -bench .
goos: windows
goarch: amd64
pkg: pprof
cpu: Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz
BenchmarkFib10-4                 3981487               297.2 ns/op
  • go test -bench .:这行输入的命令

  • goos: windows:这行显示运行基准测试的操作系统,此处为 Windows。

  • goarch: amd64:这行显示运行基准测试的机器架构,此处为 64 位 AMD 架构。

  • pkg: GoProject1:这行显示包含基准测试代码的包名,此处为 "pprof"。

  • cpu: Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz:这行显示运行基准测试的机器 CPU 信息,包括 CPU 型号和时钟频率。

  • BenchmarkFib10-4:这行表示测试函数名,-4代表CPU核数,3981487表示一个执行了3981487次,每次执行花费 297.2 ns

1.3.2性能优化建议-slice

预分配内存

运行结果如下:

BenchmarkNoPreAlloc-4                145           7885452 ns/op
BenchmarkPreAlloc-4                  661           1831792 ns/op

可以看到预分配内存后,性能更好。因此应尽可能在使用make()初始化切片时提供容量信息。

大内存未释放

当我们在已有切片基础上创建新的切片时,新切片并不会创建一个新的底层数组这种情况下,如果我们从一个大切片中截取出一个小切片,并且在代码中保留对大切片的引用,那么原底层数组将会一直存在于内存中,得不到释放.

优化建议:使用copy替代re-slice 为了避免上述陷阱.

1.3.3性能优化建议-map

  1. 不断向map中添加元素的操作会触发map的扩容
  2. 提前分配好空间可以减少内存拷贝和Rehash 的消耗
  3. 建议根据实际需求提前预估好需要的空间

1.3.4性能优化建议-字符串处理

  • 使用拼接性能最差,strings.Builderbytes.Buffer相近,strings.Builder更快

  • 字符串在Go语言中是不可变类型,占用内存大小是固定的

  • 使用每次都会重新分配内存

  • strings.Builderbytes.Buffer底层都是[]byte数组。内存扩容策略,不需要每次拼接重新分配内存

  • bytes.Buffer转化为字符串时重新申请了一块空间

  • strings.Builder直接将底层的byte 转换成了字符串类型返回

  • 预分配内存后,strings.Builderbytes.Buffer性能都有所提升

1.3.5性能优化建议-空结构体

  • 空结构体 struct{}实例不占据任何的内存空间
  • 可作为各种场景下的占位符使用
  • 节省资源
  • 空结构体本身具备很强的语义,即这里不需要任何值,仅作为占位符

1.3.6性能优化建议-atomic包

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

02性能调优实战

2.1简介

性能调优原则

  • 要依靠数据不是猜测
  • 要定位最大瓶颈而不是细枝末节
  • 不要过早优化
  • 不要过度优化

2.2性能分析工具pprof

2.2.1性能分析工具pprof-功能简介

image.png

2.2.2性能分析工具pprof-排查实战

首先从GitHub链接github.com/wolfogre/go… 下载代码。代码运行后打开终端,执行命令: go tool pprof http://127.0.0.1:8080/debug/pprof/profile?seconds=30

执行命令后结果如下

PS E:\Users\Dell\GolandProjects\go-pprof-practice> go tool pprof http://127.0.0.1:8080/debug/pprof/profile?seconds=30
Fetching profile over HTTP from http://127.0.0.1:8080/debug/pprof/profile?seconds=30
Saved profile in C:\Users\13286\pprof\pprof.samples.cpu.006.pb.gz
Type: cpu
Time: Aug 3, 2023 at 10:47am (CST)
Duration: 30.01s, Total samples = 50ms ( 0.17%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) 

2.2.2性能分析工具pprof-排查实战

我们可以在交互界面输入top3来查看程序中占用CPU前5位的函数:

(pprof) top3
Showing nodes accounting for 50ms, 100% of 50ms total
Showing top 3 nodes out of 24
      flat  flat%   sum%        cum   cum%
      30ms 60.00% 60.00%       30ms 60.00%  runtime.stdcall3
      10ms 20.00% 80.00%       10ms 20.00%  runtime.cgocall
      10ms 20.00%   100%       10ms 20.00%  runtime.gopark
  • flat:当前函数占用CPU的耗时
  • flat::当前函数占用CPU的耗时百分比
  • sun%:函数占用CPU的耗时累计百分比
  • cum:当前函数加上调用当前函数的函数占用CPU的总耗时
  • cum%:当前函数加上调用当前函数的函数占用CPU的总耗时百分比

Flat == Cum,函数中没有调用其他函数; Flat == 0,函数中只有其他函数的调用. 我们还可以使用list 函数名命令查看具体的函数分析

(pprof) list stdcall3
Total: 50ms
ROUTINE ======================== runtime.stdcall3 in C:\Users\13286\sdk\go1.19.11\src\runtime\os_windows.go
      30ms       30ms (flat, cum) 60.00% of Total
         .          .   1108://go:cgo_unsafe_args
         .          .   1109:func stdcall3(fn stdFunction, a0, a1, a2 uintptr) uintptr {
         .          .   1110:   mp := getg().m
         .          .   1111:   mp.libcall.n = 3
         .          .   1112:   mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
      30ms       30ms   1113:   return stdcall(fn)
         .          .   1114:}
(pprof)

我们还可以输入web,通过svg图的方式查看程序中详细的CPU占用情况.想要查看图形化的界面首先需要安装graphviz图形化工具。由于我是Windows系统,进入官网下载graphviz:graphviz.gitlab.io/download/

我安装后还是不能运行web命令,终端显示在path路径下没有找到graphviz.我想到path路径修改后,重启电脑才能生效.重启后果然可以运行

image.png pprof也支持分析内存性能数据。 输入下面的命令会在网页中显示内存占用情况

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

结果如图所示

image.png

在网页中还可以查看火焰图. 火焰图(Flame Graph)是 Bredan Gregg 创建的一种性能分析图表,因为它的样子近似火焰而得名。 点击图中的view,再点击Flame Graph,即可看到火焰图

image.png

结果如下,内存性能火焰图示例: image.png