高质量编程与性能调优 | 青训营笔记

144 阅读4分钟

高质量编程与性能调优 | 青训营笔记

这是我参与「第三届青训营 -后端场」笔记创作活动的第1篇笔记

01.高质量编程

1 编码规范

1.1 代码格式

使用gofmt。 Go语言的gofmt可以自动对代码进行格式修正,Goland集成开发环境自带这一功能,在保存代码的同时会自动对排版进行对齐。

1.2 注释

注意在一些必需的地方添加注释,方便别人阅读程序和错误排查

1.3 命名规范

变量、函数、类方法名的命名都需要遵守规则,让其简单易懂,无二义性。

1.4 控制流程

1.5 错误和异常处理

panic

  • 不建议在业务中用panic
  • error尽可能提供简明的上下文信息链,方便定位问题
  • panic用于真正异常的情况

recover生效范围,在当前goroutine的被defer的函数中生效 (defer语句会在函数返回前调用;多个defer语句是后进先出的)

2 性能优化建议

2.1 Benchmark

基准性能测试工具Benchmark可以方便的对一些简单的场景进行性能测试,从而判断相应的优化能起多大的作用。

2.2 Slice

在已有切片的基础上再创建切片,不会创建新的底层数组,如果在大型切片的基础上多次切片,会导致原底层数组在内存中有引用,即使它已经不必存在主存中,也因为有引用而得不到释放。可以用copy方法替代切片,copy是深拷贝一个数组,不会和原数组产生引用关系。

2.3 Map

map预分配内存

  • 不断向map中添加元素会触发map的扩容
  • 提前分配好空间可以减少内存拷贝和rehash开销
  • 建议根据实际需求提前预估好需要的空间

2.4 字符串处理

使用+拼接字符串性能最差,在性能优化场景建议使用strings.Builderbytes.Buffer,其中 string.Builder更快。

bytes.Buffer转化为字符串时重新申请了一块空间。 string.Builder直接将底层[]byte转换成了字符串类型返回。

使用Grow提前给定字符串大小

builder.Grow(n * len(str)

2.5 空结构体

使用空结构体节省内存。例如使用map时如果只需要键不需要值,可以用空结构体来对值赋值,这样编译后值不会占存储空间,因为空结构体为0字节。(即使是bool类型也会占用1字节,所以空结构体是可以节省内存的)

2.6 atomic包

在并发编程时可以考虑使用atomic包,atomic操作是通过硬件实现的,效率比锁高;而锁是通过操作系统来实现,属于系统调用。

小结:

  • 避免常见的性能陷阱可以保证大部分程序的性能
  • 普通应用代码,不要一味地追求程序的性能
  • 越高级的性能优化手段越容易出现问题
  • 在满足正确可靠、简洁清晰的质量要求的前提下提高程序性能

性能调优原则

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

3 性能分析工具 ppof

使用ppof对一段程序的性能分析过程如下:

主要命令:toplist Eatweb 注意web命令需要安装Graphviz。

go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"
​
(pprof) top
Showing nodes accounting for 3.22s, 98.77% of 3.26s total
Dropped 29 nodes (cum <= 0.02s)
      flat  flat%   sum%        cum   cum%
     3.20s 98.16% 98.16%      3.22s 98.77%  github.com/wolfogre/go-pprof-practice/animal/felidae/tiger.(*Tiger).Eat
     0.02s  0.61% 98.77%      0.02s  0.61%  runtime.asyncPreempt
         0     0% 98.77%      3.22s 98.77%  github.com/wolfogre/go-pprof-practice/animal/felidae/tiger.(*Tiger).Live
         0     0% 98.77%      3.24s 99.39%  main.main
         0     0% 98.77%      3.24s 99.39%  runtime.main
         0     0% 98.77%      0.02s  0.61%  runtime.systemstack
​
(pprof) list Eat
Total: 3.26s
ROUTINE ======================== github.com/wolfogre/go-pprof-practice/animal/felidae/tiger.(*Tiger).Eat in C:\Users\administration\go\pkg\mod\github.com\wolfogre\go-pprof-practice@v0.0.0-20190402114113-8ce266a210ee\animal\felidae\tiger\tiger.go
     3.20s      3.22s (flat, cum) 98.77% of Total
         .          .     19:}
         .          .     20:
         .          .     21:func (t *Tiger) Eat() {
         .          .     22:   log.Println(t.Name(), "eat")
         .          .     23:   loop := 10000000000
     3.20s      3.22s     24:   for i := 0; i < loop; i++ {
         .          .     25:           // do nothing
         .          .     26:   }
         .          .     27:}
         .          .     28:
         .          .     29:func (t *Tiger) Drink() {

flat是函数自身的消耗;cum是本身和调用函数的消耗。 如果flat==cum,说明函数中没有调用其他函数;flat==0,函数中只有其他函数的调用.

3.1 pprof-排查实战

image-20220513122903997.png

  • alloc_objects: 程序累计申请的对象数
  • alloc_space: 程序累计申请的内存大小
  • inuse_objects: 程序当前持有的对象数
  • inuse_space: 程序当前占用的内存大小

goroutine-协程 http://localhost:6060/debug/pprof/goroutine

image-20220513190526361.png 火焰图从上到下表示调用顺序,每一个块表示一个函数,越长代表占用cpu时间越长。火焰图是动态的,支持点击块进行分析。

mutex-锁

命令: go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/mutex"

block-阻塞

命令: go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/block"

3.2 性能分析工具pprof-采样过程和原理

1.CPU

image-20220513194758933.png

2.Heap-堆内存

image-20220513194839089.png

性能调优案例

业务服务优化

基础库优化

AB实验SDK的优化

Go语言优化

总结

性能调优原则:要依靠数据不是猜测。 擅用性能分析工具pprof。