1. 高质量编程
高质量编程:简单性、可读性、生产力
- 边界条件考虑完备
- 异常处理,稳定性保证
- 易读易维护
编码规范
-
代码格式:gofmt,goimports (gofmt + 依赖管理)
-
注释
- 解释代码作用
- 解释代码怎么做的
- 解释代码实现原因,提供额外的上下文
- 解释代码什么情况会出错
- 公共符号要有注释
-
命名规范
- 变量:缩略词全大写,但位于变量开头且不需导出时,全小写
- package:只有小写字母
-
错误和异常处理
- 简单错误:
errors.New("xxx") - 错误的 Warp 和 Unwarp: Warp 提供了一个 error 嵌套另一个 error 的能力,从而生成 error 的跟踪链
fmt.Errorf("xxx : %w", err)将 err 关联到错误链中- 错误判定:如
errors.Is(err, fs.ERrrNotExist) - 在错误链上获取特定种类的错误:
errors.As(r, &pathError) recover():只能在被 defer 的函数中使用,嵌套无法生效,只在当前 goroutine 生效- 多个 defer 语句后进先出
- 简单错误:
2. 性能优化
go 语言的基准性能测试 benchmark 工具 : go test -bench=. -benchmem
性能优化建议:
-
Slice 预分配内存:在 make() 初始化时提供容量信息
- 如:
data := make([]int, 0, size) - 因为切片是一个结构体,包括数组指针、片段长度、片段容量(不改变内容分配下的最大长度);切片操作不复制切片指向的元素,创建新的切片会复用原来切片的底层数组
- 在原有切片上创建新切片,有内存未释放的问题,建议先创建小切片,然后用 copy()
- 如:
-
map 预分配内存
- 如:
data := make(map[int]int, size) - 提前分配好空间可以减少内存拷贝和 rehash 的消耗
- 如:
-
字符串处理
-
字符串拼接:使用 strings.Builder
var builder strings.Builderbuilder.WriteString(str)return builder.String()
-
或者使用 buffer
buf := new(bytes.Buffer)buf.Grow(n * len(str))buf.WriteString(str)return buf.String()
-
字符串是不可变类型,占用内存大小固定
-
"+" 每次都会重新分配内存
-
strings.Builder , bytes.Buffer 底层是 []byte 数组,内存扩容策略,不需要每次拼接重新分配内存
-
strings.Builder 可以预分配内存:
builder.Grow(n * len(str))
-
-
使用空结构体节省内存
- 空 struct{} 实例不占用内存空间
-
多线程编程:使用 atomic 包 比 加锁的性能好
- 锁的实现是通过操作系统,属于系统调用
- atomic 通过硬件实现
3. 性能调优实战
性能分析工具 pprof
golang pprof 实战 | Wolfogre's Blog
-
下载代码到本地,
go env -w GO111MODULE=on,go.mod 中的 module 改为module go-pprof-practice-master,然后其他文件中 import 的路径改成go-pprof-practice-master/xxx -
运行 main.go
go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"
topN : 查看占用资源最多的函数
-
Tiger.Eat
-
flat : 当前函数本身的指向耗时
-
flat% : flat 占 CPU 总时间的比例
-
sum% : 上面每一行的 flat% 的总和
-
cum : 当前函数本身 + 其调用函数的总耗时
-
cum% :cum 占 CPU 总时间的比例
-
什么情况下 flat == cum , flat == 0?
- flat == cum:没有调用其他函数
- flat == 0: 函数中只有其他函数的调用
list : 根据指定的正则表达式查找代码行
- for 循环
web:调用关系可视化
- 下载 graphviz,配置环境变量
heap - 堆内存
- 可视化:在命令中加 -http(需要 graphviz)
go tool pprof -http localhost:8080 "http://localhost:6060/debug/pprof/heap"
-
Graph 视图
-
Source 视图
-
SAMPLE 里面有4个统计内存开销的方式
- alloc_objects: 程序累计申请的对象数
- alloc_space: 程序累计申请的内存大小
- inuse_objects: 程序当前持有的对象数
- inuse_space: 程序当前占用的内存大小
goroutine 泄漏 —— 导致内存泄漏
go tool pprof -http localhost:8080 "http://localhost:6060/debug/pprof/goroutine"
-
view 下切换为 flame graph (火焰图)
- 从上到下是调用顺序
- 每一块表示一个函数,越长表示占用 CPU 时间更长 (wolf.Drink)
-
然后切换到 source 视图,搜索 wolf
mutex 锁
go tool pprof -http localhost:8080 "http://localhost:6060/debug/pprof/mutex"
block 阻塞
go tool pprof -http localhost:8080 "http://localhost:6060/debug/pprof/block"