Go性能调优 | 青训营笔记

52 阅读3分钟

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

Day3

高质量编程及编码规范

高质量代码:正确可靠,简洁清晰。正确性、完备性、易维护

gofmt:Go语言官方工具,能自动格式化go语言代码为官方统一风格

goimports:也是Go语言官方工具,等于gofmt加上依赖包管理,能自动增删依赖的包引用,并按字母序排列。

注释Tips:

  • 解释代码作用
  • 解释代码如何做
  • 解释代码实现原因
  • 解释代码出错情况(限制条件)

代码是最好的注释,注释应该提供代码为表达出来的上下文信息

命名规范:

  • 简介胜于冗长
  • 缩略词全大写,但当其位于变量开头且不需要导出时全小写。
  • 变量距离其被使用的地方越远,则需要携带更多上下文信息

package:只有小写字母组成,简短并包含一定上下文信息,不与标准库同名

以下规则尽量满足:

  • 不使用常用变量名作为包名。例如bufio而不是buf
  • 使用单数而不是复数。例如使用encoding而不是encodings
  • 谨慎使用缩写。

控制流程:避免嵌套,保持正常流程清晰

错误与异常处理:优先使用errors.New来创建匿名变量来直接表示简单错误,若有格式化要求,使用fmt.Errorf

error的三个API:errors.Is、errors.As、errors.Unwrap

fmt.Errorf能使用%w关键字来将一个错误关联至错误链中。

errors.Is能够进行错误判定:是否包含指定类型错误

errors.As能讲特定错误内容取出

  • error尽可能提供简明的上下文信息,方便定位问题。
  • panic用于真正异常的情况。
  • recover生效范围,再当前goroutine的被defer函数中生效。

性能优化

go test自带benchmark工具,能实现程序基准性能测试

主要注意的点在于预分配,对于slice,其实包含一个指向底层数组的指针,如果能够预分配,能够减少溢出时内存分配的时间,对于map来说,底层是平衡树结构,直接预分配能够一次构建成功,时间复杂度较小,若后续逐渐添加,会在内存分配和rebalancing上面有损耗。

特殊场景:在已有切片上创建切片,不会创建新的底层数组

  • 原切片较大,在原切片基础上新建小切片
  • 原底层数组在内存中有引用,得不到释放。

可用copy代替re-slice

字符串拼接不建议使用直接加,byte.Buffer更好。字符串是不可变类型,占用内存大小固定,每次使用+会重新分配内存,strings.Builder,bytes.Buffer底层是[]byte数组。如果增加预分配代码性能会更好。

性能优化分析工具

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

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

主要调优工具pprof,可通过上述语句进行采样并将数据存入文件,采样时间为10s。

通过top命令查看性能瓶颈,之后可通过list Eat列出具体执行函数的性能消耗

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

能够在浏览器中得到一个性能分析的可视化视图。

goroutine泄露也会导致内存泄漏

go tool pprof -http=:8000 "http://localhost:6060/debug/pprof/goroutine"