高质量编程与性能优化 | 青训营笔记

96 阅读5分钟

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

写在开头

本人是一个第一次参加字节青训营的学生,主要是简单记录一下自己学习的过程以及复习(详细的需要自己去看每天对应的课程),每天会发前一天课程的笔记以及自己的思考(对项目)

高质量编程

什么是高质量?

编写的代码能够达到正确可靠,简洁清晰的目标可称为高质量代码。

边界问题是否考虑完备,异常情况处理,稳定性,易读,易维护。

高质量编码原则是相通的,有三点:

  1. 简单性

  2. 可读性

  3. 生产力

编码规范—注释

公共符号始终要注释,比如对外提供的函数,注释描述他的功能和用途,只有在功能简单的时候才能不写。

注释可以解释代码的外部因素,提供额外的信息,有的代码脱离了这些信息就很难理解了。

可以注释代码的输入限制,可能会出错的情况。

编码规范—命名

变量:

  1. 简洁
  2. 如果是缩略词全大写,但是缩略词如果在变量开头且不需要导出时,使用全小写(Go会把首字母大写的变量权限设置为public)
  3. 变量距离其被使用的地方越远,则需要携带越多的信息。
  4. 全局变量在其名字中需要更多的上下文信息,使得在不同地方可以认出来是干啥的

函数:

  1. 函数名不携带包名的上下文信息,因为包名和函数名是一起出现的。
  2. 尽量简短
  3. 当名为 hello 的包某个函数返回类型和函数名不同时,可以在函数名中加入类型信息
  4. 不要给实现了接口的方法注释,可以直接去接口看注释

包:

  1. 由小写字母组成。不包含大写字母和下划线等字符
  2. 简短并包含一定的上下文信息。
  3. 不要与标准库冲突!

人们在阅读理解代码的时候也可以看成是计算机运行程序,好的命名能让人把关注点留在主流程上,清晰地理解程序的功能,避免频繁切换到分支细节,增加理解成本

编码规范—控制流程

举个例子:

if foo {
    return x
} else {
    return nil
}
// 可以写成
if foo {
    return x
}
return nil
  1. 避免嵌套,保持正常流程清晰
  2. 如果两个分支中都包含 return 语句,则可以去除冗余的 else
  3. 尽量保持正常代码路径为最小缩进,优先处理错误情况和特殊情况,并尽早返回或继续循环来减少嵌套,增加可读性
  4. 线性原理,处理逻辑尽量走直线,避免复杂的嵌套分支

编码规范—错误和异常

简单错误优先使用 errors.New 来创建匿名变量来直接表示该错误。

Wrap 和 Unwrap 可以在 fmt.Errorf 中使用 %w 关键字来将一个错误 wrap 至其错误链中。

尽量不要在业务代码中用panic,容易整个程序崩溃,如果有不可逆转的错误,可以使用panic。

recover 只能在被 defer 的函数中使用,嵌套无法生效,只在当前 goroutine 生效

性能调优

性能优化的前提是满足正确可靠,简洁清晰等质量因素,并且是综合评估,时间和空间效率可能对立。

我们可以用 Benchmark 评估性能

go test -bench=. -benchmem  // 通过这个指令评估性能

Slice优化建议

尽量使用 make() 初始化切片时写入有多大容量,尤其是在追加切片的时候。

因为切片本质是一个数组片段的描述,包括了数组的指针,切片操作并不复制切片指向的元素,创建一个新的切片会复用原来切片的底层数组,因此切片操作是非常高效的。

我们再用 append 往切片里面增加数值的时候,添加之后的长度小于等于之前的总值,会直接利用底层数组的空间,而大于的话,会分配一个更大的区域容纳底层数组。

为了避免发生内存copy,最好预先设置

还有就是在已有切片的基础上进行切片,不会创建新的底层数组。因为原来的底层数组没有发生变化,内存会一直占用,直到没有变量引用该数组,可能会出现小量数据占大量空间,这个时候建议用copy操作优化。

Map优化建议

一直向 map 中添加元素的操作会触发 map 的扩容,根据实际需求提前预估好需要的空间,提前分配好空间可以减少内存拷贝和 Rehash 的消耗。

字符串优化

字符串在 Go 语言中是不可变类型,占用内存大小是固定的,当使用 + 拼接字符串的时候会生成一个新的字符串,也会在存储空间里开辟一段新的空间,新空间的大小是原来两个字符串的大小之和。而 string 的 api 则是转换成 byte 数组再返回,尽量使用 api 而不是 + 拼接。

总结

后面的优化工具涉及到内部课图片,有兴趣可以回去复习看一下!