Go高质量编程与性能调优

高质量编程

高质量编程简介

编写的代码能够达到正确可靠、简介清晰的目标可称之为高质量代码

  • 各种边界条件是否考虑完备
  • 异常情况处理、稳定性保证
  • 易读易维护

编程原则

简单性

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

可读性

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

生产力

  • 团队整体工作效率非常重要

编码规范

代码格式

推荐使用gofmt或者goimports工具自动化格式化代码

vscode中可以在settings.json中进行配置

注释

  1. 解释代码作用:注释公共符号(函数、变量)

  2. 解释代码如何做的:注释实现过程

  3. 解释代码实现的原因:解释代码外部因素,提供额外上下文

  4. 解释代码什么情况会出错:解释代码的限制条件

代码是最好的注释,注释应该给出代码为表达出的上下文信息

命名规范

变量

  • 简洁胜于冗长
  • 缩略词全大写,但当其位于变量开头且不需要导出时,使用全小写
  • 变量距离其被使用的地方越远,则需要携带越多的上下文信息

函数

  • 函数名不携带包名的上下文信息,因为包名和函数名总是成对出现
  • 函数名尽量简短
  • 当名为foo的包某个函数返回类型Foo时,可以省略类型信息而不导致歧义
  • 当名为foo的包某个函数返回类型T时(不是Foo),可以在函数名中加入类型信息

package

  • 只由小写字母组成。不包含大写字母和下划线等字符
  • 简短并包含一定的上下文信息
  • 不要与标准库同名
  • 不使用常用变量名作为包名,例如使用bufio而不是buf
  • 使用单数而不是复数
  • 谨慎地使用缩写,在不破坏上下文的情况下比尽可能简短,例如fmt

控制流程

  • 避免嵌套,保持正常流程清晰
  • 尽量保持正常代码路径为最小路径:优先处理错误情况和特殊情况,尽早返回或继续循环来减少嵌套
  • 线性原理,处理逻辑尽量走直线,避免复杂的嵌套分支

错误和异常处理

error

  • 简单错误:仅出现一次的错误,且在其他地方不需要捕获该错误,优先使用errors.New来创建匿名变量直接表示简单错误;如果需要格式化处理,使用fmt.Errorf()
  • 错误的WrapUnwrap:错误的Wrap实际上是提供了一个error嵌套另一个error的能力,从而生成一个error的跟踪链,在fmt.Errorf中使用%w关键字来将一个错误关联至错误链中

panic

  • 不建议在业务代码中使用panic
  • 调用函数不包括recover会造成程序的崩溃
  • 若问题可以被屏蔽或者解决,建议使用error代替panic
  • 如果在程序启动阶段就发生了不可逆的错误时,可以在init或者main函数中使用panic

recover

  • recover只能在被defer的函数中使用
  • 嵌套无法生效
  • 只在当前的goroutine生效
  • defer的语句是后进先出

性能优化建议

预分配内存

对于slicemapstrings.Builderbytes.Buffer这几种在使用时最好都要预分配内存,否则在进行操作时会需要经常进行扩容从而频繁操作内存。

字符串处理

go中存在3中字符串处理方式:用+拼接、strings.Builderbytes.Buffer;可以看出strings.Builderbytes.Buffer两种方式处理字符串的效率更高。

  • 字符串在Go语言中是不可变类型,占用内存大小是固定的
  • 使用+每次都会重新分配内存
  • 另外两种方式的实现底层都是byte[],并且存在内存扩容策略,不需要每次拼接重新分配内存

Java的字符串处理比较类似

对于strings.Builderbytes.Buffer这两种方式,也存在区别,从上图可以看出strings.Builder是更快的,从下面strings.Builderbytes.BufferString()函数源码可以看出一点原因。

1
2
3
4
5
6
7
8
// To build strings more efficiently, see the strings.Builder type
func (b *Buffer) String() string {
if b == nil {
// Special case, useful in debugging.
return "<nil>"
}
return string(b.buf[b.off:])
}
1
2
3
4
// String returns the accumulated string
func (b *Builder) String() string {
return *(*string)(unsafe.Pointer(&b.buf))
}
  • bytes.Buffer转化为字符串时重新申请了一块空间
  • strings.Builder直接将底层的[]byte转换成了字符串类型返回

空结构体

  • 空结构体struct{}实例不占据任何内存空间
  • 可以作为各种场景下的占位符使用
1
2
// 借助空结构体可以通过map来实现set
m := make([int]struct{}) // hash set

atomic包

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

性能调优实战


Go高质量编程与性能调优
http://example.com/2023/01/17/Go/high-quailty-programming-and-performance-tuning/
作者
zhc
发布于
2023年1月17日
许可协议