高质量编程与性能优化|青训营笔记

162 阅读6分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第4天。

Go高质量编程

高质量编程:编写的代码能够达到正确可靠、简阶清晰的目标可称之位高质量代码,各种边界情况要考虑完备,异常情况处理,稳定性保障,易读易维护。

编程原则:简单性、可读性、生产力。

简单性:消除“多余的复杂性”,以简单清晰的逻辑编写代码,不理解的代码无法修复改进。

可读性:代码是写给人看的,而不是机器,编写可维护代码的第一步是确保代码可读。

生产力:团队整体工作效率非常重要。

注释:公共符号始终要注释,不明显也不简短的公共功能要注释,函数必须注释,注释应该解释代码作用、代码如何做的、代码实现的原因、代码出错情况。

推荐使用gofmt和goimports,vsc中能自动格式化

命名规范:简洁胜于冗长。缩略词全大写(开头不导出全小写),变量使用距离越远携带信息应越多,函数名不携带上下文,包名只小写,简短包含上下文,与标准库区分。

控制流程:避免嵌套,保持正常流程清晰,尽量保持正常代码路径为最小缩进,提升代码可维护性和可读性。

错误和异常处理:仅出现一次的简单错误优先用errors.New来直接表示,如果需要格式化,使用fmt.Errorf;错误的Wrap是error嵌套error,在fmt.Errorf中使用%w关联之错误链;可以使用errors.Is判定是否为特定错误;可以使用errors.As在错误链上获取特定种类错误;不建议在业务代码中用panic,调用若不包含recover会导致程序崩溃,可以用error代替panic,若错误不可逆转可以在init或main函数中使用panic;recover只能在defer函数中使用,嵌套不生效,只能在当前goroutine生效,defer先进后出,可以在recover后log中记录调用栈。

Go性能优化

性能优化

基准测试工具benchmark

评估性能的代码

go test -bench=.-benchmem

性能评估工具的使用

go mod init pprof

go build

./pprof -cpuprofile cpu.prof -memprofile mem.prof

go tool pprof cpu.prof

性能优化建议:

slice预分配内存尽可能在使用make()初始化切片时提供容量信息,切片操作不复制切片指向的元素,创建新的切片会复用原切片底层数组,可使用copy替代re-slice。

map预分配内存不断向map中添加元素会触发map的扩容,提前分配空间可以减少内存拷贝和Rehash消耗。

字符串使用strings.WriteString()底层为[]byte数组,不需要重新分配内存,拼接字符串比加号和其他方法快得多。

空结构体不占据空间可作为占位符使用,可以用map代替实现Set,不适用map的值能节省空间。

atomic包效率比锁高,sync.Mutex应用来保护一段逻辑而非变量,非数值操作用atomic.Value能承载一个interface{}。

普通应用代码不要一味追求性能。

Go性能优化分析工具

性能调优原则:要依据数据而不是猜测,要定位最大瓶颈而不是细枝末节、不要过早优化、不要过度优化。

pprof是用于可视化和分析性能分析数据的工具。

运行测试时可以打开localhost:6060/debug/pprof/

排查时重点关注allocs,block,heap,mutex,profile。

运行时可以在对应终端输入go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"查看cpu情况

pprof命令top:展示当前函数本身的执行耗时(flat),flat占CPU总时间比例(flat%),上面每一行flat%的和(sum%),当前函数本身加上其调用函数的总耗时(cum),cum占CPU总时间的比例。

若flat==cum说明没有调用其他函数

若flat==0说明只有其他函数的调用

pprof命令list sth:根据正则表达式或字段sth查找代码行

pprof命令web:调用关系可视化

看到占用最多的代码可能需要进行调整,或者直接注释掉

查看执行效果go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"

查看协程情况go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/goroutine"

块越长占用CPU时间越长、

查看锁情况go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/mutex"

查看阻塞情况go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/block"

复杂调用关系只会展示一个占比较大的阻塞

如果要查看直接localhost:6060/debug/pprof/点击即可

Go性能性能优化实战

CPU采样对象:函数调用和它们占用的时间

CPU采样率:100次/秒

CPU采样时间:从手动启动到手动结束

CPU采样过程:开始采样->设置信号处理函数->开启定时器->采样进程->停止采样->取消信号处理函数->关闭定时器

CPU采样时操作系统每10ms向进程发送一次SIGPROF信号

CPU采样进程中每次收到SIGPROF回极调用堆栈

CPU采样时每100ms读取已经记录的调用栈并写入输出流

堆内存采样程序通过内存分配器在堆上分配和释放内存并记录分配和释放的大小和数量

堆内存采样率:每分配512KB记录一次,可在运行开头修改分配多少记录一次

堆内存采样时间:从程序运行开始到采样时

堆内存采样计算:inuse=alloc-free

协程采样:记录所有用户发起且在运行中的协程

阻塞采样:采样阻塞操作的次数和耗时

阻塞采样率:阻塞超过阈值才会被记录,可设定记录的阈值

锁采样:采样争抢锁的次数和耗时

锁采样率:记录固定比例的锁操作,可设定加锁几次记录

业务服务优化流程:建立服务性能评估手段->分析性能数据,定位性能瓶颈->重点优化项改造->优化效果验证

服务:能够单独部署,承载一定功能的程序

依赖:Service A的功能依赖Service B的响应结果,称为Service A依赖Service B

调用链路:能够支持一个接口请求的相关服务集合及其相互之间的依赖关系

基础库:公共的工具包、中间件

benchmark可以分析性能

服务性能评估方式:不同负载情况下性能表现差异

请求流量构造:不同请求参数覆盖逻辑不同和线上真实流量情况

压测范围:单机器压测和集群压测

性能数据采集:单机性能数据和集群性能数据(可以用火焰图交互进行)

性能瓶颈:使用库不规范、高并发场景优化不足

记录请求的返回值是否一致,判断优化是否影响正确性(可以使用工具比对,工具安装网址(76条消息) emacs文本比对工具ediff的使用说明iSuwen的博客-CSDN博客ediff-i

可以对比优化前后压测报告

分析链路,通过业务流程优化提升服务性能

分析优化基础库提升服务性能

优化内存分配策略和代码编译流程提升服务性能