这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天
高质量编程与性能调优
高质量编程
简介
-
什么是高质量的代码?
--编写的代码能够达到正确可靠,简洁清晰的目标即可称为高质量代码
- 边界条件考虑完备
- 异常情况处理,保证稳定性
- 简洁清晰易读易维护
-
编程原则
-
简单性
消除多余的复杂性,用简单清晰的逻辑编写代码
-
可读性
代码是写给人看的,而不只是机器
编写可维护代码的第一步是确保可读
-
生产力
团队整体工作效率
-
如何编写高质量的Go代码?
-
代码格式
- 使用gofmt/goimports统一代码格式
-
注释
-
注释需要做什么?
- 解释代码的作用
- 解释代码如何做的
- 解释代码为什么这么做
- 解释代码什么情况下会出错,即代码的适用条件
-
对于所有的公共符号(变量、函数)都需要给出 注释
-
注释要提供代码未表达的上下文信息
-
-
命名规范
-
变量:
- 命名简洁清晰、
- 缩略词全大写(如果在开头且非导出,使用全小写)
- 变量距离其使用的地方越远,则命名中带的上下文信息需要越多
-
函数名
- 函数名不用带包名的上下文信息
- 函数名应尽量简短
-
package
- 只包含小写字母,不用大写字母和下划线
- 简短并且包含一定的上下文信息
- 不要与标准库同名
-
-
控制流程
- if/else尽量避免嵌套,保持流程清晰
- 保持正常代码路径为最小缩进,优先处理错误/特殊情况并返回来保持正常代码为最小缩进
-
错误&异常处理
-
简单错误【只出现一次的错误,在其他地方不需要捕获该错误】
- 使用errors.New创建匿名便来来表示
- 使用fmt.Errorf来格式化
-
错误的Wrap和Unwrap
- 可以生成一个错误的跟踪链,包含其上下文的信息
-
错误判断
-
使用error.Is来判断是否是特定的错误
- 不要使用==运算符,因为前文描述过的Wrap的情况
-
使用error.As来转换为特定的错误
-
panic:尽量不要在业务代码中使用panic,因为不含recover的调用函数会导致程序崩溃
- 一般在启动阶段启动失败时,在main或者init函数中使用panic
-
recover
- 只能在defer的函数中使用
- 只适用于当前的goroutine
- 可以在recover后,在log中使用debug.Stack()记录程序调用栈
-
-
性能优化建议
性能优化的前提是满足正确可靠、简洁清晰等代码质量要素。并且性能优化有时候是时间与空间的tradeoff。
使用Benchmark工具
func BenchmarkFib(b *testing.B){
for n :=0; n<b.N; n++{
Fib(10)
}
}
go test -bench
会打印出执行的次数(即b.N),每次执行时间,每次执行申请内存数量与次数等。
Go语言性能优化建议
Slice
- 在make时提供切片容量以预分配内存
-
在已有切片的基础上创建新切片时不会释放原来的底层数组
- 在一个大切片上重新切片一个只使用了其中几个元素的切片,原有的底层数组不会被回收
- 在这种情况下可以使用copy代替re-slice
Map
-
map也可以预分配内存来提高性能
- 提前分配空间可以减少扩容时的拷贝和Rehash的消耗
- 如果无法预知Map的大小则不要做额外的操作
字符串处理
-
使用strings.Builder来拼接字符串
- Go中的字符串是不可变类型,使用+拼接时会创建新字符串、重新分配内存
- strings.Builder和bytes.Buffer的底层都是带扩容策略的[]byte数组
- 使用bytes.Buffer需要重新申请空间转字符串,性能略差于strings.Builder
-
预分配strings.Builder或者bytes.Buffer
- 使用
builder.Grow(n * len(str))来预先扩容
- 使用
空结构体
空结构体不占据任何内存
- 用
map[int]struct{}来实现map,相比于map[int]bool要节省空间
使用原子变量代替锁
atomic.AddInt32(&i, 1)
//相比于
m.Lock()
i++
m.Unlock()
//原子变量的性能要高一些
- 一般来说
sync.Mutex应该用在保护一段代码逻辑的地方,而不是单纯地保护一个变量
性能优化分析
性能优化原则
- 依靠数据而不是猜测
- 定位最大瓶颈而不是细枝末节
- 不要过早优化
- 不要过度优化
性能分析工具pprof
pprof简介
功能上可以分为四大部分
-
工具-Tool
- runtime/pprof
- net/http/pprof
-
采样- Sample
- CPU
- 堆内存
- Goroutine
- 锁-Mutex
- 阻塞
- 线程创建
-
分析-Profile
- 网页
- 可视化终端
-
展示-View
- Top
- 调用图
- 火焰图
- Peek
- 源码&反汇编