这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
青训营掘金课程Day03-高质量编程及性能调用
高质量编程
高质量编程简介
何为高质量?
- 各种边界条件是否考虑完备
- 异常情况处理,稳定性保证
- 易读易维护
我们需要遵循的编程原则:
- 简单性: 消除“多余的复杂性”,以简单清晰的逻辑编写代码
- 可读性: 代码给人看的
- 生产力:团队整体工作效率非常重要
编码规范
编码规范 : 如何写出高质量的Go代码
- 代码格式
- 注释
- 命名规范
- 控制流程
- 错误和异常处理
注释:
- 包中声明的每个公共的符合:变量、常量、函数以及结构都需要添加注释
- 任何既不明显也不简短的公共功能必须予以注释
- 无论长度或复杂程度如何,对库中的任何函数都必须进行注释
- 有一个例外,不需要注释实现接口的方法。
代码格式:
推荐使用gofmt自动格式化代码
注释:
- 适合注释实现过程
- 提供额外上下文,适合解释代码的外部因素
- 适合注释代码的限制条件
命名规范:
- 简洁胜于冗长
- 缩略词全大写,但当其位于变量开头全小写
性能优化建议
Benchmark
go自带的性能评估工具
- 性能表现需要实际数据衡量
- Go语言提供了支持基准性能测试的benchmark工具
go test -bench=. -benchmen
slice 预分配内存
尽可能在使用make() 初始化切片时提供容量信息
为什么预分配使用的内存少些? 原因: 切片本质是一个数组片段的描述
- 包括数组指针
- 片段的长度
- 片段的容量(不改变内存分配情况下的最大长度)
- 切片操作并不复制切片指向的元素
- 创建一个新的切片会复用原来切片的底层数组
陷阱:大内存未释放
- 在已有切片基础上创建切片,不会创建新的底层数组
- 场景
- 原切片较大,代码在原切片基础上新建小切片
- 原底层数组在内存中引用,得不到释放
- 可使用copy 替代 re-slice
Map 预分配内存
分析:
- 不断向map中添加元素的操作会触发 map的扩容
- 提前分配好空间可以减少内存拷贝 和 Rehash的消耗
- 建议根据实际需求提前预估好需要的空间
使用 strings.Builder
- 常见的字符串拼接方式(类似于java的stringBuilder)
使用 + 拼接性能最差,strings.Builder,bytes.Buffer相近,strings.Buffer更快
分析:
- 字符串在Go语言中是不可变类型,占用内存大小是固定的
- 使用+ 每次都会重新分配内存
- strings.Builder,bytes.buffer 底层都是[] byte数组
- 内容扩容策略,不需要每次拼接重新分配内存
使用空结构体
- 空结构体struct{}实例不占据任何的内存空间
- 可作为各种场景下的占位符使用
- 节省资源
- 孔结构体本身具备很强的语义,这里不需要任何值,作为占位符
使用atomic包
- 锁的实现是通过操作系统来实现,属于系统调用
- atomic 操作是通过硬件实现,效率比锁高
- sync.Mutex 应该是用保护一段逻辑,不仅仅用与保护一个变量
- 对于非数值操作,可以使用atomic.Value,能承载一个interface{}
性能调优实战
性能调优简介
- 要依靠数据而不是猜测
- 要定位最大瓶颈而不是细枝末节
- 不要过早优化
- 不要过度优化
性能分析工具pprof实战
说明
- 希望知道应用在什么地方消耗了多少CPU、Memory
- pprof 是用于可视化和分析性能分析数据的工具
说明:关于调优案例那块没有记笔记了,都是实战,而且对我这种javar感觉有点深,需要深究。