Go 高质量编程与性能调优 | 青训营笔记
这是我参与「第五届青训营 」笔记创作活动的第3天
高质量编程
编码规范
-
原则:简单性、可读性、生产力
-
格式化编码
- gofmt: 自动化将代码统一成官方风格
- goimports:在gofmt的基础之上,自动完成依赖包的引用管理,并将依赖包排序分类
-
注释
-
应当实现
- 解释作用
- 解释如何实现
- 解释实现的原因
- 解释出错的情况
-
公共符号始终要注释,接口实现方法是一个例外
-
-
变量
- 缩略词全大写
- 越远使用的变量更应当携带更多的上下文信息
-
函数
- 函数名尽量不携带包名的上下文信息
-
包
- 只由小写字母组成
- 不与标准库同名
- 包含一定的上下文信息
- 使用单数而不是复数
- 不使用常用变量名作为包名
-
控制流程
- 优先处理错误和特殊情况,保证正常流程最小缩进
-
错误和异常
- 简单错误使用erros.New来新建匿名变量来直接表示
- 对错误使用Wrap和Unwrap进行嵌套,生成error跟踪链
- 错误判定:errors.Is, 获取特定错误类型:errors.As
- 一般业务代码不建议使用panic,只有在程序启动过程中出现不可逆转的错误时,在init或main中使用panic
- recover只在defer中生效,嵌套无效,且只对当前goroutine生效。 一般使用recover恢复后,在log中记录调用栈
性能优化建议
-
使用基准测试Benchmark
go test -bench . -benchmem -
Slice
- 尽可能在make()初始化切片时提供容量信息
-
大内存未释放,在原有切片的基础之上新建小切片导致原底层数组任有引用,得不到释放
优化方法:使用copy代替re-slice
-
map
- 预分配内存,原理同slice
-
字符串处理
-
使用strings.Builder代替字符串“+”拼接 原因在于字符串拼接每次都会重新分配内存,而strings.Builder和bytes.Buffer底层都是[]byte数组
相对而言strings.Builder比bytes.Buffer性能更优,原因后者在String()中转换成字符串时重新申请了一块空间,而前者是直接进行类型转换后返回的
-
-
空结构体
- 空结构体struct{}实际上不占用任何内存空间
- 空结构体的作用:实现Set 当map的value为空结构体时,value不占用空间,只使用其key,即可实现Set的功能
-
atomic包
- atomic包是实现并发同步的原子性的包,其底层是通过硬件实现的,效率比锁高。
- sync.Mutex应当被用于保护一段逻辑而不是一个变量,变量的维护可以使用automic包
性能调优实战
-
pprof
-
CPU监控
go tool pprof "http://localhost:6060/debug/pprof/profile?second=10":监控10s内的CPU对程序的处理情况,并转储为文件top:展示占用资源最多的函数list:根据指定的正则表达式查找代码行
-
web可视化排查
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/[goroutine/block/heap/mutex...]" -
一般查找优化流程: 配置pprof,使用runtime开启调用追踪-》
使用web查看监控指标[allocs\block\goroutine\mutex\heap...]-》
根据具体的监控指标,调用对应的web页面查看详情-》根据详情溯源代码位置-》
准备好测试数据记录优化前正确的逻辑结果-》
对代码逻辑进行优化-》
重新加载pprof监控,查看优化后的是否有提升-》
重新运行优化后的系统加载准备的测试数据,对优化前后的输出进行对比
-
-
pprof采样大致原理
- CPU:pprof进程启动定时器,每隔10ms,OS向进程发送SIGPROF信号。进程每次接收到SIGPROF都将保存到堆栈。每100ms将记录的调用栈写入输出流。
- Heap:通过采样内存分配器在堆上分配的内存,记录分配和释放的大小和数量。每分配512KB记录一次。记录总分配空间(alloc_space),总分配对象数(alloc_objects),当前持有对象数(inuse_objects),当前占用内存大小(inuse_spaces)
- Gorountine&ThreadCreate:遍历创建链表,输出创建的堆栈。
- Block&Mutex:采样阻塞(争抢锁)的次数和耗时。只有阻塞超时(固定比例锁操作)才记录。
引用参考
高质量编程与性能调优实战 .pptx - 飞书云文档 (feishu.cn)