这是我参与「第三届青训营 -后端场」笔记创作活动的第3篇笔记,学校事情好多,记的效率太慢了。
高质量编程的简介
编写的代码能够达到正确可靠,简介清晰的目标可称为高质量代码
- 各种边界条件是否考虑完备
- 异常情况处理,稳定性保证
- 易读易维护
编程原则
应用场景千变万化,各种语言的特性和语法不同
但高质量编程遵循的原则是相同的
- 简单性
- 可读性
- 生产力
编码规范
代码格式
go语言官方自己有一个gofmt的工具,实现统一的编码规范
同时也可以使用goimports这个工具
注释
注释的用法:
- 解释代码的作用
- 解释代码如何做的
- 解释代码实现的原因
- 解释代码什么情况下会出错
总之,代码是最好的注释,注释应该提供代码来表达出上下文的信息。
命名规范
变量
- 简洁胜于冗长
- 缩略词要全大写
函数
- 函数名无需携带包的上下文信息
- 函数名尽量简短
包
- 只由小写字母组成,不包含大写字母和下划线等其他
- 简短并包含上下文信息
- 不与标准库同名
总结,命名规范是为了降低代码的阅读难度。
控制流程
- 线性原则,处理逻辑尽量走直线,避免复杂的嵌套
- 正常流程代码沿着屏幕向下移动
- 提高可读性和可维护性
- 故障大多出现在复杂的条件语句和循环语句
错误处理
error是简单的错误,比error严重的错误是panic
在业务中尽量不要使用panic,因为panic会导致程序崩溃
性能优化
Benchamrk
- 性能表现需要实际数据衡量
- Go语言提供了支持基准性能测试的benchamrk工具
slice优化
使用slice的时候最好先预分配内存
尽可能在使用make()时提供初始化容量
slice的底层是一个数组片段的描述
包括:
- 一个指向底层数组的指针
- 数组片段的长度
- 数组片段的容量(不改变内存分配的情况下的最大长度)
特殊情况
如果原切片是由大量元素组成的,但是我们在原切片的基础上切片,虽然只使用了一小段,但底层数组在内存中仍然存在,占据大量的空间。
解决办法:使用copy代替re-slice
map优化
map跟slice一样,最好也要预分配内存
因为不断向map里添加元素会不断触发map的扩容
提前分配好空间可以减少内存的拷贝和ReHash的消耗
字符串优化
如果需要频繁拼接字符串,不要使用string
最好使用strings.Builder
因为字符串在go中的字符串是常量,占用的内存是固定的
使用string的+拼接会重新分配内存,造成内存浪费,性能最差
bytes.Builder和strings.Builder性能相近,strings.Builder会更快一些
空结构体优化内存
空结构体不占用内存,可作为各种场景下的占位符使用
atomic包
锁的实现是通过操作系统来实现的,属于系统调用
atomic操作时通过硬件来实现的,效率比锁高
sync。Mutex应该用来保护一段逻辑,不仅仅用来保护变量
对于非数值操作可以使用atomic.Value(),能承载一个interface{}
性能优化的层面
- 业务层优化
- SDK
- 基础库
- 语言运行时优化
- OS
业务层主要针对特定的场景进行优化,一般可以获得较大的性能收益。
语言运行时的优化主要是解决更加通用的问题,考虑更多的场景,Tradeoffs
数据驱动
- 自动化性能分析工具——pprof
- 依靠数据而非猜测
- 首先优化最大的瓶颈