什么是高质量代码
一句话来说就是:编写的代码能够达到正确可靠、简洁清晰的目标可称之为高质量代码
- 各种边界条件是否考虑完备
- 异常情况处理,稳定性保证
- 易读易维护
编程原则
- 简单性
- 消除多余的复杂性
- 不理解代码无法跟进修复
- 可读性
- 代码要可以让别人读懂
- 生产力
- 团队整体工作效率要高
编码规范
- 代码格式
- gofmt
- goimport
- 注释
- 注释应该解释代码功能
- 注释应该解释代码实现的原因
- 注释应该解释代码在什么时候会出错
- 公共符号始终要注释
- 命名规范
- 变量名
- 简介胜于冗长
- 缩略词全部大写,但当其位于变量开头且不需要导出时,应该使用小写
- 变量距离离变量使用的地方越远,越需要上下文信息
- 函数名
- 函数名不携带包名的上下文信息
- 函数名尽量简短
- 包名
- 只由小写组成
- 不要与标准库同名
- 简短且包含一定的上下文信息
- 变量名
- 控制流程
- 避免嵌套,保证流程清晰
- 尽量保证代码为最小缩进
- 错误和异常处理
- 简单错误
- 简单的错误指的是仅出现一次的错误,且在其他地方不需要捕获该错误
- 优先使用 errors.New 来创建匿名变量来直接表示简单错误
- 如果有格式化的需求,使用 fmt.Errorf
- 错误的Wrap和Unwrap
- 错误判定
- Panic
- 不建议在业务中使用panic
- 如果问题可以解决的话,尽量使用error代替panic
- Recover
- recover只能在defer中使用
- 嵌套无法生效
- 只在当前协程中生效
- defer时后进先出
- 简单错误
性能优化建议
Benchmark测试效率
Slice预分配内存
尽可能在使用make初始化切片时提供内容信息可以减少切片的创建(节省时间)
另外一个问题:大内存没有释放
- 在已有切片基础上创建切片,不会创建新的底层数组
- 场景
- 原切片较大,代码在原切片基础上新建小切片
- 原底层数组在内存中有引用,得不到释放
- 可使用 copy 替代re-slice
Map预分配内存
- 不断向map 中添加元素的操作会触发 map 的扩容
- 提前分配好空间可以減少内存拷贝和 Re hash 的消耗
- 建议根据实际需求提前预估好需要的空间
字符串处理
- 常见的字符串拼接方式
- 直接使用String 加法效率最差
- 使用String Builder时 效率更好
- 使用Strung Buffer 效率最好
- 分析
- 字符串在 Go 语言中是不可变类型,占用内存大小是固定的
- 使用+每次都会重新分配内存
- strings.Builder, bytes.Buffer 底层都是 []byte 数组
- 内存扩容策路,不需要每次拼接重新分配内存
使用空结构体来节省空间
- 可以用Map加空结构体来实现Set
Atomic包
- 使用 atomic 包
- 锁的实现是通过操作系统来实现,属于系统调用
- atomic操作是通过硬件实现,效率比锁高
- sync.Mutex 应该用来保护一段逻辑,不仅仅用于保护一个变量
- 对于非数值操作,可以使用atomic.Value,能承载一个interface{}
性能优化分析
性能调优原则
- 要依靠数据不是猜测
- 要定位最大瓶颈而不是细枝末节
- 不要过早优化
- 不要过度优化