青训营笔记3 - Go 高质量编程与性能调优
这是我参与「第五届青训营」伴学笔记创作活动的第 三 天
高质量编程简介及编码规范
高质量编程
公共符号始终要注释
推荐使用
gofmt自动化格式代码 或者goimports=gofmt+ 依赖包管理
命名规范
变量
- 简洁
- 缩略词全大写
- 变量距离其背使用的地方越远,则需要携带越多的上下文信息
函数
- 函数名尽量简短
包
- 只由小写字母组成
- 简短并包含一定的上下文信息
- 使用单数而不是复数
控制流程
优先处理错误情况/特殊情况,尽早返回或继续循环来减少嵌套
错误和异常处理
简单的错误
- 简单的错误指的是仅出现一次的错误,在其他地方不需要捕获该错误
- 优先使用
errors.New来创建匿名变量来直接表示简单错误
复杂的错误
Panic不建议使用,表明程序崩溃不可用recover只能在被defer的函数中使用,嵌套无法生效,只在当前goroutine生效 ,defer的语句是后进先出
性能优化指南
性能表现需要实际数据衡量,Go语言提供了支持基准性能测试的 benchmark工具
go test -bench=. -benchmem
复制代码
Slice预分配内存
-
尽可能使用
make()初始化切片时提供容量信息 -
切片本质是一个数组片段的描述
- 包括数组指针
- 片段的长度
- 片段的容量
-
切片操作并不复制切片指向的元素
-
创建一个新的切片会复用原来切片的底层数组
map
-
原理
- 不断向 map 中添加元素的操作会触发 map 的扩容
- 根据实际需求提前预估好需要的空间
- 提前分配好空间可以减少内存拷贝和 Rehash 的消耗
-
使用 strings.Builder
-
常见的字符串拼接方式
-
- strings.Builder
- bytes.Buffer
-
-
strings.Builder 最快,bytes.Buffer 较快,+ 最慢
-
原理
- 字符串在 Go 语言中是不可变类型,占用内存大小是固定的,当使用 + 拼接 2 个字符串时,生成一个新的字符串,那么就需要开辟一段新的空间,新空间的大小是原来两个字符串的大小之和
- strings.Builder,bytes.Buffer 的内存是以倍数申请的
- strings.Builder 和 bytes.Buffer 底层都是 []byte 数组,bytes.Buffer 转化为字符串时重新申请了一块空间,存放生成的字符串变量,而 strings.Builder 直接将底层的 []byte 转换成了字符串类型返回
-
-
使用空结构体节省内存
-
空结构体不占据内存空间,可作为占位符使用
-
比如实现简单的 Set
- Go 语言标准库没有提供 Set 的实现,通常使用 map 来代替。对于集合场景,只需要用到 map 的键而不需要值
-
-
使用 atomic 包
-
原理
- 锁的实现是通过操作系统来实现,属于系统调用,atomic 操作是通过硬件实现的,效率比锁高很多
- sync.Mutex 应该用来保护一段逻辑,不仅仅用于保护一个变量
- 对于非数值系列,可以使用 atomic.Value,atomic.Value 能承载一个 interface{}
-
总结
- 避免常见的性能陷阱可以保证大部分程序的性能
- 针对普通应用代码,不要一味地追求程序的性能,应当在满足正确可靠、简洁清晰等质量要求的前提下提高程序性能
性能调优
主要是 pprof web页面查看各项指标进行评估调优。熟悉pprof使用。