高质量编程与性能调优
这是我参与「第五届青训营」伴学笔记创作活动的第3天
1.注释:(解释代码作用,实现,实现原因,代码出错情况,提供代码上下文未体现信息)
1 适合注释公共符号
2 适合注释实现过程
3 适合解释代码实现原因
4 适合解释代码的限制条件
2. 命名规范
variable
- 简洁胜于冗长
- 缩略词全大写,但当其位于变量开头且不需要导出时,使用全小写
- 变量距离其被使用的地方越远,则需要携带越多的上下文信息
- 全局变量在其名字中需要更多的上下文信息,使得在不同地方可以轻易辨认出其含义
function
- 函数名不携带包名的上下文信息,因为包名和函数名总是成对出现的
- 函数名尽量简短
- 当名为 foo 的包某个函数返回类型 Foo 时,可以省略类型信息而不导致歧义
- 当名为 foo 的包某个函数返回类型 T 时(T 并不是 Foo),可以在函数名中加入类型信息
package
- 只由小写字母组成。不包含大写字母和下划线等字符
- 简短并包含一定的上下文信息。例如 schema、task 等
- 不要与标准库同名。例如不要使用 sync 或者 strings
3.控制流程
1.避免嵌套
2.保持正常路径为最小缩进(先处理特殊情况)
4.错误和异常处理
1.简单错误
2.错误的Wrap和Unwrap
3.错误判定
4.panic
5.recover-在当前gouroutine的被defer的函数中生效
性能优化建议
benchmark工具
go test -bench=. -benchmem
Slice
预分配内存,使用make()初始化切片提供容量 大内存未释放,用copy代替re-slice(原始切片较大,在基础上建立小切片)
Map
预分配内存,提供容量
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 转换成了字符串类型返回
空结构体
不占内存,可做占位符
atomic包
对比锁,atomic通过硬件实现,效率高
性能调优
- 要依靠数据不是猜测
- 要定位最大瓶颈而不是细枝末节
- 不要过早优化
- 不要过度优化
pprof工具
功能
实践
-
-
前置准备,熟悉简单指标,能够编译运行 pprof 测试项目
-
实际分析排查过程
-
排查 CPU 问题
-
命令行分析
- go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"
-
top 命令
flat当前函数本身耗时
sum上面每一行flat%的总和
cum 当前函数本身加调用函数总耗时 -
list 命令 list + 函数,返回函数中语句执行时间
-
熟悉 web 页面分析
-
调用关系图,火焰图
-
go tool pprof -http=:8080 "[http://localhost:6060/debug/pprof/cpu]"
-
-
排查堆内存问题
- go tool pprof -http=:8080 "[http://localhost:6060/debug/pprof/heap]"
-
排查协程问题
- go tool pprof -http=:8080 "[http://localhost:6060/debug/pprof/goroutine]"
-
排查锁问题
- go tool pprof -http=:8080 "[http://localhost:6060/debug/pprof/mutex]"
-
排查阻塞问题
- go tool pprof -http=:8080 "[http://localhost:6060/debug/pprof/block]"
-
-
-
pprof 的采样过程和原理
- CPU 采样
- 堆内存采样
- 协程和系统线程采样
- 阻塞操作和锁竞争采样
性能调优
基本问题
为什么要性能优化
性能优化的好处
两个层面
业务层
语言形式
可维护性
开发手段