这是我参与「第五届青训营」伴学笔记创作活动的第 4 天
高质量编程与性能调优 | 青训营笔记
今天讲的是高质量编程和性能调优部分,虽然讲的是 Go 语言的,但我觉得其中的很多思想都是全语言通用的。
高质量编程
何为高质量编程,就是使编写的代码能够达到正确可靠,简洁清晰的程度。它有三个简要的原则:
- 简单性
- 可读性
- 生产力
多的理论也不谈,我觉得还是谈谈实践下来该怎么做吧。
编码规范
代码格式
这个说起来难也不难,最重要的就是养成习惯。虽然说 Go 语言有自带的语言格式化工具,好像还是强制的,同一份代码绝不会格式化得不一样。不过这里还是说一下普遍认可的编写格式,毕竟也不是所有的语言或者 IDE 都能自动格式化。
一个非常重要就是加空格,空格的加发也很有讲究,我直到现在也没有研究完,这里就说几个我注意到的:
-
在运算符的前后添加。比如
c=a+b,我们写成c = a + b,会更加好一点。 -
在逗号、冒号、分号后添加。一个经典的例子就是调用函数的时候,把
swap(a,b)写成swap(a, b),这样当参数多的时候也不会出现参数挤在一起的情况,更加好分辨。 -
在一些关键词后添加。比如
forwhileif这种后面往往还会跟一个表达式的。 -
在大括号前添加。这个适用于像 Go 语言这种不换行起大括号的写法,当然如果换行了那肯定没必要了。
-
还有一个比较随性的点就是,如果觉得写得挤了,那就加个空格
另一个值得注意的就是换行,不过换行的学问倒是比较少,而且一般也有比较明确的约定和习惯。唯一一个值得注意的就是在链式调用或者其他情况的时候,如果一行写得特别长,那么适度换行也是有益于身心健康的。
注释
我对注释比较惭愧,我是深受注释恩泽的,但是写注释的经验确实不多。Go 语言的注释似乎真的仅仅只有 // 而已,感觉相对其他语言差着点了。视频也说的不错,注释应该做的有如下四点:
- 解释代码作用
- 解释代码如何做的
- 解释代码实现的原因
- 解释代码什么情况会出错
这个说的确实有参考价值,下面讲一下我编写中我认为必须写注释的地方:
-
函数对外的接口。这个是必须的,不然使用者云里雾里就不好了。按照比较基本的说法来说,有几点是要说的。一个是概述,简要讲讲这个函数的作用,原理也可以讲讲。另一个是参数,主要就是说明参数的意义和用法。还有就是如果有返回值的话可以把返回值的意思也说清楚。
-
类对外的属性,要把每个属性的作用和意义都说清楚。
再其他的地方比如一些比较难理解的实现逻辑、用到了魔数之类的地方,也当然是要补充一下的。我是觉得注释有总比没有好,还是先考虑有没有再考虑写得好不好吧。
命名规范
这个是我比较纠结的地方,经常有起名困难症。按照 Go 语言的要求,还是尽量简短点为好。不过我之前命名的时候也都比较喜欢长命名来着,顺带一提,我是小驼峰党,小驼峰是真的好看呐,有时候甚至会怀疑自己是不是为了看小驼峰而故意起两个词的名字……
不过我记得其中有一点说的挺好的,就是声明的变量离使用的地方越远,命名就要越清晰。以及要根据上下文信息来适当减少或者加长变量名,防止意义重复或者意思缺失。
控制流程
一个是如果一个 if 分支最后是 return,则可以省去 else 不写。虽然以前写的时候隐隐约约有能 retrun 就 return,缩减缩进的想法,但我还以为是奇淫巧技,不入流的。没想到原来是正确的代码编写规范。
另一个受益匪浅的原则就是,尽量保持代码路径为最小缩进。也就是说,尽量先判断错误然后 return,把正确的语句留在下面,最后做到结尾是返回成功运行的值就最好了。这样一般都能优化代码结构,把缩进减到比较小的程度。我曾经听过一个说法就是:如果你的代码的缩进超过了两层以上,就说明的你的代码需要进行进一步的重构和提取了。
错误和异常处理
- 构造错误链
- 错误包含和获取
- 使用
panic和recover对严重错误进行处理。Go 没有 try/catch 语句,因此panic和recover语句组合可以大概实现同样的功能。注:panic能用尽量少用。recover在当前 goroutine 的被 defer 的函数中生效
性能优化
主要是一些 tricks:
-
使用
Benchmark获得性能优化方向或者建议 -
切片的切片使用的是原切片,注意因此导致的内存释放问题,可以使用
copy -
slice和map等构建时预分配内存会更快,即提前指定大小。 -
字符串拼接用
string.Buider更快,具体原理是其底层实现是[]byte,返回时直接将[]byte转换为字符串 -
空结构体
struct{}不占内存,需要占位时用它更省内存, -
对某个数进行原子操作时,使用
atomic包内的函数而不是使用锁会更快。具体原理是锁是系统调用,atomic操作是硬件实现,效率更高。锁更适合保护一段逻辑。
性能调优
性能调优原则
- 依靠数据而不是猜测
- 要抓住最大瓶颈而不是细枝末节
- 不能过早优化
- 也不能过度优化。
性能分析工具 pprof
非常强力,而且可以数据可视化。
业务性能调优
具体可以从以下方面入手:
-
业务服务优化。代码结构优化,整体逻辑简化。
-
基础库优化。对于使用得多的基础库,要进行合理使用,或者更换更高效的库。
-
Go 语言优化。更换编译器,或者使用优化更深的编译参数。有点像 C++ 编译的时候 O1、O2 优化这样的感觉