这是我参与「第五届青训营 」伴学笔记创作活动的第 3天
一、本堂课重点内容:
- 高质量编程
- 性能调优
二、详细知识点介绍:
1.高质量编程
1.1高质量的要求:
- 各种边界条件是否考虑完备
- 异常情况处理
- 易读易维护
1.2知识点:
1.2.1写高质量GO代码
-
代码格式
gofmt
-
注释
-
公共符号始终要注释
-
注释内容
- 代码作用
- 代码如何做的(例:实现过程)
- 代码实现原因
- 代码什么情况下会出错(例:限制条件)
-
-
命名规范
-
变量名
- 缩略词全大写
- 变量距离被使用的地方越远,则需要携带越多上下文信息(例:全局变量)
-
函数名
-
不携带包名
例如:
http包中func Serve()比func ServeHTTP()更规范 -
返回类型与函数名一致可省略类型信息
-
-
package
- 只用小写字母
- 不与标准库同名
-
-
控制流程
- 避免嵌套
- 优先处理错误/特殊情况,尽早返回或继续循环来减少嵌套
-
错误和异常处理
- 优先使用
errors.New来创建匿名变量直接表示简单错误 - 如果有格式化需求,使用
fmt.Errorf Wrap:提供一个error嵌套另一个error的能力,从而生成error跟踪连fmt.Errorf中使用%w:将一个错误关联至错误链中errors.Is:类似于==,用来判断是否为特定错误errors.As:在错误链上获取特定种类的错误- panic:用于真正异常的情况
- recover:在当前goroutine的被defer的函数中生效
- 优先使用
2.性能调试
2.1 benchmark
go test -bench=. -benchmem
2.2 Slice
- 尽量在make()初始化时提供容量信息
type slice struct{
array unsafe.Pointer
len int
cap int
}
由于创建新切片时会复用原来切片的底层数组->大内存未释放
-
建议用copy代替重新创建切片
- 可用
go test -run=. -v测试
- 可用
2.3 map
建议根据实际需求提前预估所需空间
原因:
- 向map添加元素会触发map扩容
- 提前分配可减少内存拷贝和rehash的消耗
2.4字符串处理
-
拼接字符串:
strings.Buider性能最好bytes.Bufffer- +号拼接
-
原因:
- 字符串是不可变类型,内存固定
- 使用+每次会重新分配内存
bytes.Bufffer,strings.Buider底层都是[]byte数组
-
预分配:
//对于strings.Buider var builder strings.Builder //预分配 builder.Grow(n*len(str)) //对于bytes.Bufffer buf:=new(bytes.Buffer) //预分配 buf.Grow(n*len(str))
2.5 空结构体
适用于实现set,可以考虑用map空结构体替代,对于只用map键,不用值的场景
2.6 atomic包
对比于加锁操作性能更好
原因:锁实现通过操作系统,属于系统调用,atomic操作用过硬件
注意:
sync.Mutex应该原来保护一段逻辑,不仅用于保护变量- 非数值操作可以用
atomic.Value,能承载interface{}