1.高质量编程
什么是质量
- 各种边界条件是否考虑完备
- 异常情况处理,稳定性保证
- 易读易维护
编程原则
- 简单性
- 可读性
- 生产力
编程规范
-
代码格式
- 推荐使用gofmt自动格式化代码
-
注释
- 解释代码作用 (注释公共符号(变量、函数))
- 解释代码是如何做的 (解释实现过程)
- 解释代码实现的原因(解释外界因素)
- 解释代码在什么情况下可能会出错(解释限制条件)
-
命名规范
-
variable
- 简洁胜于冗长
- 缩略词全大写
- 变量距离被使用的距离越远,名字应该携带更多的上下文信息
-
function
- 不携带包名信息,因为总是成对出现的
- 函数名尽量简短
- 当foo包中的某个函数返回类型Foo时,可以省略信息而不导致歧义
- 当foo包中的某个函数返回类型T(T不是Foo)时,可以在函数名中加入类型信息
-
package
-
只由小写字母组成,不包括大写字母和下划线
-
简短并包含一定的上下文信息
-
不要与标准库同名
- 不使用变量名作为包名
- 使用单数而不是复数
- 谨慎使用缩写
-
-
-
流程控制
- 避免嵌套
- 尽量保持正常代码路径为最小缩进(优先处理错误和特殊情况,不让正常情况嵌套很深)
- 正常流程沿着屏幕向下移动
- 故障问题通常出在复杂的条件语句和循环之中
-
错误和异常处理
-
简单错误
- 仅出现一次的错误
- 优先使用errors.New()来创建匿名错误类型
- 如果有格式化的需求,使用fmt.Errorf
-
错误的Wrap和Unwrap
- 错误的wrap其实就是一个错误包装了另一个错误
- 在fmt.Errorf中使用:%w关键字关联至错误链中
-
错误判定
- 判断是否是错误 errors.Is()
- 判断错误分类 errors.As()
-
panic
- 不建议在业务代码中使用panic
- 如果函数中不包含recover会引起程序崩溃
- 如果错误可以屏蔽或者解决,尽量使用error
-
recover
- recover只能在defer中使用
- 只在当前goroutine中生效
- defer语句是先进后出
-
2.性能优化
指南
在满足正确可靠、简洁清晰的质量要求的前提下提高程序性能
-
Slice
- 内存预分配 尽可能在make的时候提供容量信息
- 建议用copy替代slice[m:n],因为如果只取了slice里面的很小一部分,但是其他部分不会被回收,就造成了很大的内存浪费,copy一部分走,大的切片就有机会被回收
-
map
- 内存预分配 提前分配好空间会减少内存拷贝和Rehash的消耗
-
strings.Builder
- 使用这样一个方式拼接字符串可以有效提升性能
- 如果预知长度,也可以提前分配容量,提升性能
-
空结构体
-
空结构体struct{}不占用任何内存
-
在各个场景下的占位符使用
- 节省资源
- 不需要任何值,仅作为占位符 比如 map[string]struct{}
-
实现Set(即使是设置为bool类型, 也会占用一个字节的空间)
-
-
atomic包
- 原子操作 通过硬件实现的,效率比锁更高
- 锁通过操作系统来实现,属于系统调用
- sync.Mutex应该来保护一段逻辑而不是一个变量
- 对于非数值操作,可以使用atomic.Value可以承载一个interface{}
原则
要依靠数据而不是猜测
要定位最大瓶颈而不是细枝末节
不要过早优化
不要过度优化
工具 pprof
- 希望知晓应用在什么地方耗费了多少CPU,Memory
- pprof
克隆一个pprof-practice项目 github.com/wolfogre/go… 并运行
打开http://localhost:6060/debug/pprof/看到如下界面
可以点击查看详细信息
可以在终端上查看更友好的信息
go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10" 监控十秒,进入pprof控制台
输入top命令,得到以下结果
flat是当前函数调用的时间,cum是当前函数包括了所调用函数的运行时间 (flat=cum说明没有调用其他函数,flat=0说明只调用了其他函数)
看到tiger.Eat方法耗时较多,输入命令 list Eat
可以看到是在一个for循环中耗时明显,即可以找到问题所在
定位问题之后,就可以对其进行优化