Day02 Go编码规范与性能调优 | 青训营笔记

60 阅读3分钟

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

高质量编程

简单性

可读性

生产力

代码格式

gofmt自动格式化代码

goimports 自动增删依赖包的引用 排序

注释

解释代码作用

解释代码如何做的

解释代码实现原因

注释什么情况下会出错

命名规范

缩略词全大写,位于变量开头不导出时,全小写

pkg名称

  • 不使用常用变量名作为包名
  • 使用单数

控制流程

尽量保证正常代码路径为最小缩进

  • 优先让错误异常返回

故障问题大多数出现在

错误异常

error

提供上下文信息链

优先使用errors.New创建匿名变量表示简单错误

格式化要求,可以使用fmt.Errorf %w关键字将错误关联至错误链中

错误链

errors.Is 判断是否为特定错误

Errors.As 错误链上获取特定种类的错误

panic

不建议在业务代码中使用panic,不包含recover会造成程序崩溃

recover

只能在defer中使用,可以打印出问题出现的原因

编码规范

尽量使得命名在简洁的情况下无重复地提供更多的信息量

性能调优实战

Bechmark

Go提供了测试工具

go test -bench=. -benchmen

Slice

Slice扩容比较耗时,初始化的时候最好预分配,提供容量信息

另一个陷阱:大内存未释放

  • 原切片较大,代码在原切片上新建小切片

  • 原底层数组在内存中有引用,得不到释放

    func GetLastBySlice(origin []int) {
      return origin[len(origin)-2:]
    }
    ​
    func GetLastByCopy(origin [] int) {
      result := make([]int, 2)
      copy(result, origin[len(origin)-2:])
      return result
    }
    

Map

预分配,性能会更好一些

扩容和Rehash会有性能损耗

string拼接

s += “str”

字符串在Go语言中是不可变类型,占用内存大小是固定的,每次+都会重新分配内存

StringBuilder的性能会更好一些

StringBuilder和Bytebuffer底层都是[]byte,有内存扩容策略,不需要每次扩容重新分配,性能更好一些

struct{}

不占用任何空间,比false还要小

可以用来实现Set

atomic

atomic包比加锁快的多,锁用操作系统来实现,属于系统调用,atomic使用硬件实现,快。sync.Mutex应该用来保护一段逻辑,不仅仅用于保护一个变量。

性能调优实战

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

pprof

耗费多少CPU 内存 可以可视化分析

资源管理器看CPU性能

go tool proof ""

topN命令

flat 当前函数执行耗时

cum

sum % 前面每一行flat%总和

cum 当前函数本身加上其调用函数的总耗时

cum% 占CPU时间的总比例

什么情况下flat==cum?函数中没有调用其他函数

什么情况下flat==0?函数没有其他函数的调用

list命令查找问题代码

web命令调用关系可视化

内存问题

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