GO高质量编程和性能分析 | 青训营笔记

86 阅读3分钟

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

一、高质量编程

1.1 编程原则

  • 简单性
  • 可读性
  • 生产力

1.2 编码规范

1.2.1 代码格式

使用gofmt自动格式化代码,goimports依赖包管理

1.2.2 注释

注释要:

  • 解释代码作用
  • 解释代码如何作用
  • 解释实现原因
  • 解释什么情况会出错

1.2.3 命名规范

变量:

  • 简洁
  • 缩写词全大写
  • 变量使用距离远,要携带更多上下文信息

pacakge:

  • 包名只由小写组成,不含大写字母和下划线
  • 简短,包含上下文信息
  • 不与标准库重名

函数:

  • 不用携带包的上下文信息,因为它们往往成对出现
  • 名为f的函数返回F类型,可以省略类型信息不导致歧义

1.2.4 控制流程

优先处理特殊情况,尽早return减少嵌套。

1.2.5 错误和异常处理

错误

简单错误: 简单错误是指仅出现一次的错误,在其它地方不用捕获该错误。优先使用errors.New()创建匿名变量直接表示错误,格式化要求用发fmt.Errorf。

复杂错误: 在fmt.Errorf中使用%w format关键字把错误关联至错误链

错误判定: error.Is()判断是否为特定错误,也可以判断错误链上是否有特定错误。errors.As()获取错误链上的特定错误。

异常

当程序中发生严重错误时,可能导致程序崩溃,使用panic抛出,一定要再在defer中调用recover

二、性能优化

2.1 benchmark

使用要求

  • 基准测试的代码文件以_test.go结尾
  • 基准测试的函数以Benchmark开头
  • 接受一个指向Benchmark类型的指针作为唯一参数
  • 不能有返回值
  • 被测试的代码要放到循环里
  • 执行命令:go test -bench=. -benchmen

参数分析

image.png -8 cpu核数 执行次数 每次执行时间花费 每次执行申请多大内存 每次执行申请几次

2.2 优化建议

  • slice预分配内存
  • map预分配内存
  • 复杂字符串拼接用strings.Builder
  • atomic,如想在多线程中实现个计数器。atomic操作通过硬件实现,效率比锁高。对于非数值操作,可以用atomic.Value

三、性能调优实战

3.1 分析工具

image.png example: wolfogre/go-pprof-practice: go pprof practice. (github.com) web页面:go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"

需要安装graphviz,bin目录添加到path,不起效果重启

3.2 基本使用

以采样十秒为基准来分析性能

命令:go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"

cpu

首先查看cpu情况,可以发现Eat函数消耗cpu最多

image.png 参数解释:

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

思考: 什么情况flat=cum,float=0呢?

  • Flat =Cum,函数中没有调用其他函数

  • Flat=0,函数中只有其他函数的调用

已知Eat函数消耗cpu最多,于是进一步检查该函数,发现有一个巨大的循环

image.png

内存

查看内存情况,发现Mouse.Steal消耗了大量内存

image.png

还是通过list Steal来定位问题,发现buffer append了大量的内存

image.png

参考文章

# golang性能分析,pprof的使用