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

166 阅读6分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记。

前言

如何编写更简洁清晰的代码?学习常用Go语言程序优化手段,学习使用Go程序性能分析工具。最后了解工程中性能优化的流程和其中的原则。

高质量编程

1、高质量编程简介

什么是高质量代码?

1-正确可靠为前提,即核心功能+各种边界条件考虑完善;

2-简洁清晰的目标,即易读易维护(逻辑清晰+命名规范)。

高质量代码有什么用?

1-逻辑清晰,命名规范,就会让扩展维护成本低。

2-正确可靠,无bug,就会让团队合作流畅,团队整体工作效率才是生产力。

高质量代码的原则?

1-简单性,消除“多余的复杂性”,即一些可知的上下文,拒绝冗余信息。

2-可读性,维护扩展代码的基础就是读代码。

2、编码规范

1、代码格式

1-对于自己编写的代码,可用官方工具gofmt,将代码格式化为同一个标准的style。

2-对于依赖包管理,官方提供工具goimports,将自动增删需要和无用的依赖包,并按依赖包的首个字母排序分类。

2、注释

注释的对象是谁?

变量;常量;函数;结构体。

注释的目的?

1-这段代码的上下文大概是什么?

2-这段代码有什么用?

3-这段代码是如何做的?

4-会发生哪些特殊或者值得注意的情况?如错误。

3、命名规范

变量命名原则?

1-简洁胜于冗长,如index int -> i int;count int -> cnt int;t time.Time -> ddl time.Time

2-变量名离使用的地方越远,则需要命名越明确,带更多上下文。但建议不要离太远。

函数名命名原则?

1-缩略词作为函数名时,需要导出就全大写,否则全小写。ServeHttp -> ServeHTTP;XMLHTTPRequest or xmlHTTPRequest;

2-因为包和函数成对出现,函数名不带包名信息。http.ServeHTTP -> httpServe;

package命名原则?

1-只有小写字母组成,不包含大写字母和下划线。

2-简短并包含一定上下文信息,做到顾名思义。如task,strings

3-不要与标准库同名,如sync,time

4-不使用常用变量名作为包名,如buf -> bufio

5-一般情况下,使用单数而不是复数,如encodings ->encoding

6-缩写要顾名思义才能缩写,如fmt -> format;ddl -> deadline

小结:一切目的都是为了扩展维护时更方便,所有需要有顾名思义的特点,没有就加上下文。

4、控制流程

1-避免if嵌套,就要善用continue,return。

image.png

2-优先处理错误,让其提前结束,从而避免嵌套。

image.png

image.png

总结:

1-避免嵌套,走逻辑直线。正常流程代码沿着屏幕向下移动,故障打多出现在复杂的嵌套中。

2-提升代码可读和可维护性。

5、错误和异常处理

1-简单错误,可能只出现一次,我们直接errors.New来创建匿名变量直接表示简单错误。如有格式化需求,可使用fmt.Errorf

image.png

2-复杂错误包装和解包Wrap、Unwrap。Wrap可一个error嵌套在另一个error中,形成一个error跟踪链。在fmt.Errorf中使用%w可将error关联中错误中。

image.png

3-在错误链上可用errors.Is来判断该链上是否含有特定错误。

image.png

4-在错误链上获取特定错误,使用errors.As

image.png

5-非普通错误panic,直接可使程序不继续工作下去。两种情况,早些暴力panic,结束程序;转panic为error,特殊处理。

image.png

6-recover机制处理panic,不能让第三方库的bug抛出的panic影响我们的自身逻辑。

image.png

image.png

3、性能优化建议

1-性能优化指标,时间效率,空间效率,但不一定是同时降,有可能是空间换时间,提高用户体验。

2-性能优化还是需要数据驱动,Go提供了支持基准测试的benchmark工具,go test -bench=. -benchmem

image.png

image.png

3-针对slice的优化--预分配容量防止频繁扩容和内容复制 || 小切片导致大数组内存无法释放。

image.png

image.png

image.png

4-针对map的优化--预分配容量防止频繁扩容、内容复制、rehash操作。

image.png

5-针对字符串的拼接处理,采用strings.Builder拼接字符串。字符串是常量,所以每次拼接都会申请新内存+内容复制。

image.png

image.png

image.png

为什么strings.Buffer比strings.Builder慢一点?

image.png

提前知道字符串大小,是否可进一步提升性能?预分配实现

image.png

6-空结构体的好处与实践--Set实现

image.png

image.png

7-性能优化之并发安全的快速实现--atomic替代加锁。

image.png

image.png

小结

1-避免常见陷阱

2-不需要一味追求程序的性能,不要破坏程序的正确可靠性。

性能调优实战

1、性能调优简介

性能调优原则?

1-数据驱动

2-定位大瓶颈热点代码,保证程序正确可靠。

3-不要过早优化,过度优化,因为产品要迭代,特别是前期可变因素很多。

2、性能分析工具pprof

pprof是一个可视化什么地方花费了多少CPU、内存的一个数据分析工具。

1、pprof 功能简介

image.png

2、pprof排查实战

1-CPU 相关

image.png

image.png

image.png

1、topN

image.png

image.png

Windows PowerShell
版权所有(C) Microsoft Corporation。保留所有权利。

安装最新的 PowerShell,了解新功能和改进!https://aka.ms/PSWindows

PS D:\Users\86158\Desktop\青训营\示例代码\go-pprof-practice> go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"
Fetching profile over HTTP from http://localhost:6060/debug/pprof/profile?seconds=10
Saved profile in C:\Users\86158\pprof\pprof.samples.cpu.001.pb.gz
Type: cpu
Time: May 12, 2022 at 6:00pm (CST)
Duration: 10.20s, Total samples = 3.41s (33.43%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 3.19s, 93.55% of 3.41s total
Dropped 30 nodes (cum <= 0.02s)
Showing top 10 nodes out of 34
      flat  flat%   sum%        cum   cum%
     2.50s 73.31% 73.31%      2.53s 74.19%  github.com/wolfogre/go-pprof-practice/animal/felidae/tiger.(*Tiger).Eat
     0.38s 11.14% 84.46%      0.38s 11.14%  runtime.stdcall3
     0.15s  4.40% 88.86%      0.16s  4.69%  runtime.stdcall6
     0.03s  0.88% 89.74%      0.03s  0.88%  runtime.asyncPreempt
     0.03s  0.88% 90.62%      0.19s  5.57%  runtime.bgscavenge
     0.02s  0.59% 91.20%      0.02s  0.59%  runtime.(*addrRanges).removeLast
     0.02s  0.59% 91.79%      0.44s 12.90%  runtime.(*pageAlloc).scavengeRangeLocked
     0.02s  0.59% 92.38%      0.03s  0.88%  runtime.dodeltimer0
     0.02s  0.59% 92.96%      0.23s  6.74%  runtime.findrunnable
     0.02s  0.59% 93.55%      0.25s  7.33%  runtime.schedule
(pprof)   

image.png

2、list Eat

image.png

(pprof) list Eat
Total: 3.41s
ROUTINE ======================== github.com/wolfogre/go-pprof-practice/animal/canidae/dog.(*Dog).Eat in D:\Go\bin\pkg\mod\github.com\wolfogre\go-pprof-practice@v0.0.0-20190402114113-8ce266a210ee\animal\canidae\dog\dog.go
         0       10ms (flat, cum)  0.29% of Total
         .          .     21:   d.Run()
         .          .     22:   d.Howl()
         .          .     23:}
         .          .     24:
         .          .     25:func (d *Dog) Eat() {
         .       10ms     26:   log.Println(d.Name(), "eat")
         .          .     27:}
         .          .     28:
         .          .     29:func (d *Dog) Drink() {
         .          .     30:   log.Println(d.Name(), "drink")
         .          .     31:}
ROUTINE ======================== github.com/wolfogre/go-pprof-practice/animal/felidae/tiger.(*Tiger).Eat in D:\Go\bin\pkg\mod\github.com\wolfogre\go-pprof-practice@v0.0.0-20190402114113-8ce266a210ee\animal\felidae\tiger\tiger.go
     2.50s      2.53s (flat, cum) 74.19% of Total
         .          .     19:}
         .          .     20:
         .          .     21:func (t *Tiger) Eat() {
         .          .     22:   log.Println(t.Name(), "eat")
         .          .     23:   loop := 10000000000
     2.50s      2.53s     24:   for i := 0; i < loop; i++ {
         .          .     25:           // do nothing
         .          .     26:   }
         .          .     27:}
         .          .     28:
         .          .     29:func (t *Tiger) Drink() {

3、web

image.png

2-Heap堆内存相关

image.png

image.png

3-goroutine相关

image.png

image.png

image.png

4-mutex-锁

image.png

5-block阻塞

image.png

3、pprof的采样过程和原理

image.png

image.png

image.png

image.png

image.png