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

86 阅读4分钟

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

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

一、本堂课重点内容

  • 高质量代码
  • 性能调优
  • pprof

二、详细知识点介绍

高质量的代码

go 语言开发者对于编程原则的看法

  • 简单性:代码要简单,不要过度设计,不要过度优化,不要过度抽象
  • 可读性:代码要易读,易懂,易维护
  • 生产力:代码要高效,要能够提高生产力

注释

对于所用公共符号,都应该有注释,包括:函数、类型、常量、变量、结构体字段、接口、枚举值等

  • 注释应该解释代码作用
  • 注释应该解释代码如何做的
  • 注释应该解释代码实现的原因
  • 注释应该解释代码什么情况会出错(我更倾向于将错误展现在测试文件中)

注释也要简介,不要提供无用的注释。注释也要保持最新,不要提供过时的注释。

高质量的代码不应该依赖于注释来解释代码的意图,而是应该通过代码本身来解释代码的意图。通过变量名、函数名、类型名等来解释代码的意图。

命名

在变量名表达意思明确的条件下,尽可能使用短变量名。

函数命名中尽量写的更多信息,但不要与包名所携带的信息重复。

包名小写,不要使用下划线。简短并包含上下文信息,但要谨慎使用缩写。

控制流程

尽量避免嵌套

优先处理错误和异常,保证正常代码路径为最小路径。

错误处理

简单错误

errors.New 用于创建一个错误对象,fmt.Errorf 用于创建一个错误对象并格式化错误信息。

复杂错误

errors.Wrap 用于创建一个错误对象并包装一个错误对象,errors.Wrapf 用于创建一个错误对象并包装一个错误对象并格式化错误信息。

errors.Is 用于判断一个错误对象是否是另一个错误对象,errors.As 用于判断一个错误对象是否是另一个错误对象的类型。

⚠️ 要谨慎使用 panic,因为它会导致程序崩溃,而且不利于调试。

性能优化

基准性能测试

Benchmark 用于测试函数的性能。go test -bench=. -benchmem

2023-01-17-14-22-43.png

Slice 预分配内存

在使用 make 创建 slice 时,可以预分配内存,这样可以减少内存分配的次数,从而提高性能。

因为切片的底层实现是数组,所以切片的长度和容量都是固定的,当切片的容量不够时,会重新分配一个更大的数组,然后将原来的数组拷贝到新的数组中,这个过程会消耗大量的时间。

切片拷贝时尽量使用 copy 函数,而不是赋值。避免长切片因被小切片引用而无法释放。

Map 预分配内存

map 和 slice 一样,也是动态分配内存的,所以在使用 make 创建 map 时,也可以预分配内存,这样可以减少内存分配的次数,从而提高性能。

字符串拼接

使用 + 拼接字符串时,会创建一个新的字符串对象,这个过程会消耗大量的时间。

建议使用 strings.Builder ⭐ 或 bytes.Buffer 来拼接字符串。

可以通过 Grow 方法预分配内存,这样可以减少内存分配的次数,从而提高性能。

空结构体

struct{}{}

空结构体的内存占用为 0,可以用来实现集合(Set)。

atomic

  • 锁的实现是通过操作系统来实现,属于系统调用
  • atomic 操作是通过硬件实现,效率比锁高
  • sync.Mutex 应该用来保护一段逻辑,不仅仅用于保护一个变量
  • 对于非数值操作,可以使用 atomic.Value,能承载一个 interface

三、实践练习例子-性能调优

  • 不要过早优化
  • 不要过渡优化

pprof 是 Go 语言自带的性能分析工具,可以分析 CPU、内存、堆栈、阻塞、互斥锁等。

2023-01-17-17-33-21.png

go tool pprof

可视化命令:go tool pprof -http=:8080

可视化需要的依赖:Download | Graphviz

四、课后个人总结

最好的注释就是代码本身,尽量用代码来阐述它本身的意义,注释来说明业务之间的逻辑和思路。

在实践中要注意性能问题,避免掉入性能陷阱。后期优化要适当,看到问题后及时优化即可。

五、引用参考