高质量编程和性能调优实战
高质量
各种边界条件是否考虑完备
异常情况处理,稳定性保证
易读易维护
编程原则
简单性
可读性
生产力
编程规范
代码格式
gofmt自动格式化代码
goimports 依赖包管理
注释
公共符合始终要注释
命名规范
简洁胜于冗长
缩略词全大写,但是当位于变量开头不需要导出,使用全小写
变量距离其被使用的地方越远,越需要携带更多的上下文信息
控制流程
避免嵌套 保持正常流程清晰
线性原理,处理逻辑尽量走直线
错误和异常处理
优先使用errors.New来创建简单错误
如果有格式化需求使用fmt.Errorf
不建议在业务代码中使用panic
recover只能在被defer的函数中使用
多个defer语句后进先出
性能优化建议
BenchMark基准性能测试工具
go test -bench=. -benchmem
Slice预分配内存
尽可能在使用make()初始化切片时提供容量信息
切片本质是一个数组片段的描述
包括数组指针
片段长度
切片容量
和vector类似,容量不够先扩充再插入。
在已有切片的基础上创建切片,不会创建新的底层数组
使用copy代替re-slice
map预分配内存
字符串处理
使用strings.Builder
字符串在GO中是不可变的类型,占用内存大小固定。
使用+每次都会重新分配内存
string.Builder, bytes.Buffer底层都是[]byte数组
内存扩容策略,不需要每次拼接重新分配内存
使用空结构体节省内存
空结构体struct{}实例不占据任何的内存空间
可作为各种场景下的占位符使用
atomic包
锁实现通过操作系统实现,是系统调用
atomic操作通过硬件实现,效率比锁高
sync.Mutex应该用来保护一段逻辑,不仅仅用于保护一个变量
对于非数值操作,可以使用atomic.Value, 能承载一个interface{}
性能调优实战
性能调优原则
要依靠数据而不是猜测
要定位最大瓶颈而不是细枝末节
不要过早优化
不要过度优化
性能分析工具pprof
用于可视化和分析性能分析数据的工具
采样过程和原理
CPU
采样对象:函数调用和他们占用的时间
采样率:100次/秒,固定值
采样时间:从手动启动到手动结束
操作系统 每10ms向进程发送一次SIGPROF信号
进程 每次接受SIGPROF会记录调用堆栈
写缓冲 每100ms读取已经记录的调用栈并写入输出流
Heap-堆内存
采样程序通过内存分配器在堆上分配和释放的内存,记录分配释放的大小和数量
采样率:每分配512KB记录一次,可修改
采样时间:从程序运行开始到采样时
采样指标:alloc_space, alloc_objects, inuse_space, inuse_objects
计算方式: inuse = alloc - free
Goroutine-协程 & ThreadCreate-线程创建
Goroutine
记录所有用户发起且在运行中的goroutine
ThreadCreate
记录程序创建的所有系统线程信息
Block-阻塞&Mutex-锁
阻塞操作
采样阻塞操作的次数和耗时
采样率:阻塞耗时超过阈值才会被记录
锁竞争
采用争抢锁的次数和耗时
采样率:只记录固定比例的锁操作