day03-高质量编程与性能调优
-
原则
-
简单性
-
消除“多余的复杂性”,以简单清晰的逻辑编写代码
-
不理解的代码无法修复改进
-
可读性
-
代码是写给人看的,而不是机器
-
编写可维护代码的第一步是确保代码可读
-
生产力
- 团队整体工作效率非常重要
-
-
编码规范
-
推荐使用gofmt自动格式化
-
注释
-
解释代码作用
-
解释代码如何做的
-
解释代码实现的原因
-
解释代码什么情况会出错
-
命名规范
- 缩略词全大写
- function
- package
-
-
Go性能优化
- slice预分配内存
为slice预分配内存,与slice原理有关
* 切片本质是一个数组片段的描述
* 包括数组指针
* 片段的长度
* 片段的容量(不改变内存分配情况下的最大长度)
* 切片操作并不复制切片指向的元素
* 创建一个新的切片会复用原来的切片的底层数组
大内存未释放
* 在已有切片的基础上创建切片,不会创建新的底层数组
* 场景
* 原切片较大,代码在原切片的基础上新建小切片
* 原底层数组在内存中有引用,得不到释放
* 可用
copy代替re-slice
2. map预分配内存
* 不断向map中添加元素的操作会触发map的扩容
* 提前分配好空间可以减少内存拷贝和Rehash的扩容
* 根据实际需求提前预估好需要的时间
3. 字符串处理
**使用strings.Builder**,使用`+`拼接性能最差,`strings.Builder`,`bytes.Buffer`相近,`strings.Buffer`更快
* 字符串在Go中是不可变类型,占用内存大小是固定的
* 使用+每次都会重新分配内存
* string.Builder,bytes.Buffer底层都是\[]byte数组
* 内存扩容策略,不需要每次拼接重新分配内存
4. 空结构体
**使用空结构体节省内存**,空结构体`struct{}`不占据任何内存空间,仅作为占位符
* 实现`Set`,可以考虑用`map`来代替
* 对于这个场景,只需要用到`map`的键,而不需要值
* 即使是将map的值设置为bool类型,也会多占据1个字节空间
5. 使用`atomic`包
* 锁的实现是通过操作系统来实现,属于系统调用
* `atomic`操作是通过硬件实现,效率比锁高
* `sync.Mutex`应该用来保护一段逻辑,不仅仅用于保护一个变量
* 对于非数值操作,可以使用`atomic.Value`,能承载一个`interface{}`
4. 性能分析工具pprof
go tools pprof "url/debug/pprof/profile?seconds=xxx"
1. CPU
使用top命令查看看占用资源最多的函数
* flat 当前函数本身的执行耗时
* `flat%` flat占CPU总时间的比例
* `sum%` 上面每一行的flat%总和
* `cum` 指当前函数本身加上其调用函数的总耗时
* `cum%` cum占CPU总时间的比例
当`flat == cum`,函数中没有调用其他函数,`flat == 0`函数中只有其他函数的调用
`web`命令可以查看调用关系可视化
2. Heap-堆内存
go tools pprof -http=:8080 "url/debug/pprof/heap"c
访问url:8080/ui即可查看heap
- goroutine-协程
go tools pprof -http=:8080 "url/debug/pprof/goroutine"
火焰图
- 由上到下表示调用顺序
- 小方块代表函数 方块越长函数占用CPU时间越多
-
mutex-锁
go tools pprof -http=:8080 "url/debug/pprof/mutex" -
block-阻塞
go tools pprof -http=:8080 "url/debug/pprof/block"