这是我参与「第五届青训营 」笔记创作活动的第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"