这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
高质量编程与性能调优
高质量编程
一个优秀的程序员需要具备良好的编程素养。学习完本课后我总结了以下几点编程规范,这也是我们小组开发时要遵守的基本编程规则。
代码规范
- 格式
- 推荐使用 GoLand 开发,代码保存时自动格式化
- 其他 IDE 使用 gofmt+goimports 对代码格式化
- 注释
- 包中声明的每个公共符号(变量、常量、结构体、函数)必须注释
- 对于实现接口的方法不需要注释,但其接口方法的定义部分必须注释
- 对于方法的注释,应诠释代码作用、代码参数意义、代码返回错误的场景
- 命名规范
- 使用驼峰命名法,缩略词全大写(ServerHTTP),但当其不需要导出时全小写(xmlHTTPRequest)
- 不允许使用拼音与意义不明的单一字符
- 控制流程
- 多使用,合理使用设计模式
- 参数检查与功能实现需分离,如使用
而非if param == nil { return nil } doSome(param)if param != nil { doSome(param) } - 在 if-else 逻辑中,应该先判断简单、代码量少的操作逻辑,将复杂的,大段的逻辑留在分支的最后
if !enable { doEnable() } else { loadConfiguration() doSetup() doBroadcast() // ...... }
- 错误与异常处理
- 简单异常直接使用
errors.New或fmt.Errorf处理即可 - 对于存在异常上下文的嵌套错误,必须使用
fmt.Errorf,用%w占位符将错误加入错误链中 - 异常分类:
- error 表示上下文异常,便于处理预料到的错误逻辑
- panic 代表程序产生了影响运行的真正错误
- recover 仅defer中有效,可重新获取对程序的控制
- 简单异常直接使用
单元测试
- 所有关键函数必须增加单元测试,测试覆盖率必须在 50% +,接口覆盖率必须达到 80% +
- 测试分支需要满足相互独立,全面覆盖
性能优化
性能优化建议
- slice
- 预分配内存 - #make 初始化切片时提供容量信息,减少内存拷贝
- 在已有切片上创建切片,原切片底层数组仍被引用,资源未被释放 -> copy 代替 re-slice
- map
- 预分配内存 - #make 初始化map时提供容量信息,减少内存拷贝和 rehash 消耗
- string
- 拼接字符串使用 strings.Builder、bytes.Buffer
- 使用 builder#Grow 预分配大小进一步提升效率
- struct{} 空结构体
- 空结构体实例不占据任何内存,可作为任何场景下的占位符使用 -> set 实现,对于 map 结构只需要 key,不关心 value
- 原子操作
- atomic 硬件实现,效率更高,推荐用于保护变量
- sync.Mutex 通过操作系统实现,属于系统调用,效率更低,语义上来说更应该保护逻辑而非变量
Benchmark 相关参数解读
BenchmarkFlib-16 5800983 206.1 ns/op
-16 为 GOMAXPROCS,1.5 后默认为 cpu 核数
5800983 实际执行次数
206.1 ns/op 每次执行的时间
0 B/op 每次申请多大的内存
0 allocs/op 每次执行申请几次内存
调优工具 pprof
在本节课中,使用到里 github 上的测试项目实地的学习 pprof 的调优过程。
- 测试项目下载 github.com/wolfogre/go…
- 进入 pprof debug 根目录 http://localhost:6060/debug/pprof/goroutine
cpu问题排查过程
- cpu 占用检查:go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"
- 根据指定的正则表达式查找代码行
(pprof) list Eat - 调用关系可视化
(pprof) web - 排查掉 trigger.go
pprof 命令行介绍
(pprof) top
flat flat% sum% cum cum%
4.52s 100% 100% 4.52s 100% github.com/wolfogre/go-pprof-practice/animal/felidae/tiger.(*Tiger).Eat
- flat - 函数本身耗时
- flat% - flat 占 cpu 总时间的比例
- sum% - 上面每一行 flat% 的总和
- cum - 当前函数本身加上其调用函数的总耗时
- cum% - cum 占 cpu 总时间的比例
说明:flat == cum 函数中没有调用其他函数;flat == 0 函数中只有其他函数的调用
内存问题排查过程
- 内存占用检查 go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"
- 查看 top 视图与 source 视图(搜索框搜索对应 list 命令
- 排查掉 mouse.go
- 转到 SAMPLE/alloc_space,排查掉 dog.go
SAMPLE 采样分类如下
- alloc_objects 程序累积申请的对象数
- alloc_space 程序累积申请的内存大小
- inuse_objects 程序当前持有的对象数
- inuse_space 程序当前占用的内存大小
协程问题排查过程
- go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/goroutine"
- 转到 VIEW/Flame Graph,查看火焰图
- 由上到下表示调用顺序
- 每一块代表一个函数,越长表示占用 cpu 时间越长
- 火焰图是动态的,支持点击色块进行分析
- 排查掉 wolf.go
锁与阻塞问题排查过程
- go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/mutex"
- 解锁时间过长
- 排查掉 wolf.Howl
- go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/block"
- 阻塞时间过长
- 排查掉 cat.go