高质量Go代码及性能优化| 青训营笔记

99 阅读3分钟
这是我参与「第五届青训营 」笔记创作活动的第3天

本节课重点

高质量代码

是什么

编写的代码能够达到正确可靠、简洁清晰的目标可称之为高质量代码
  1. 各种边界条件考虑完备。
  2. 异常情况处理,稳定性保证。
  3. 易读易维护。

为什么

  1. 简洁
    1. 消除“多余的复杂性”,逻辑应追求简单清晰
    2. 不理解的代码无法修复改进
  2. 可读性
    1. 代码面向程序员的
    2. 可维护代码的基石是具有可读性
  3. 生产力
    1. 团队整体工作效率非常重要

怎么做

代码格式

代码格式化的工具:

  1. gofmt
  2. goimports
注释
Good code has lots of comments, bad code requires lots of comments.
应该做什么
  1. 应该解释代码的作用
  2. 应该解释代码如何做的
    • 注释实现过程
  3. 应该解释代码实现的原因
    1. 解释代码的外部因素
    2. 提供额外上下文
  4. 应该解释代码的边界条件、什么情况会出错
    • 解释代码的限制条件
应该注意什么
  1. 对于包中的变量,常量,函数及其结构添加合理的注释
  2. 对于功能不明显也不简短的代码必须给予注释
  3. 对库中的任何函数都必须进行注释
  4. 对于实现接口的方法不需要注释
命名规范
Good naming is like a good joke. If you have to explain it, it's not funny.
variable
  1. 简洁胜于冗长
  2. 缩略词全大写, 但当是变量开头且不需要导出,使用全部小写
  3. 变量声明距离被使用的地方越远越要携带更多上下文信息
functino
  1. 函数名不携带包名的上下文信息,因为包名和函数名总是成对出现
  2. 函数名尽量简短
  3. 当包名和包中函数返回类型一致时可以省略类型信息
package
  1. 小写
  2. 简短且需要包含上下文信息
  3. 不与标准库重名
  4. 不使用常用变量名作为包名
  5. 单数
  6. 缩写要尽量不丢失信息
控制流程
  1. 避免嵌套
good
if foo {
    return x
} else {
    return nil
}

better
if foo {
    return x
}
return nil
  1. 优先处理特殊情况,使代码保持最小缩进 原则
  2. 处理逻辑尽量走直线,避免复杂的分支嵌套(事故高发区)
  3. 正常流程代码沿着屏幕向下移动
  4. 提升代码可维护性和可读性
错误和异常处理
  1. 优先使用erros.Now创建匿名变量来表示错误
  2. 如果需要格式化使用fmt.Errorf
  3. 使用errors.Is进行错误类型的判断
  4. 获取特定种类的错误使用errors.As
  5. 在程序启动阶段发生不可逆转的错误时在init/main使用panic
  6. recover在当前goroutine的被defer的函数中生效

性能优化建议

Benchmark go test -bench =. -benchmem

  1. 预分配大小
    1. slice (不会创建一个新数组)
    2. map
  2. 字符串拼接
    • +、strings.Builder, bytes.Buffer、strings.Buffer 性能递增
  3. 空结构体节省内存
    • 空结构体(struct{})不占用任何内存空间
func EmptySructMap(n int) {
    m := make(map[int] struct{})
    for i := 0; i < n; i ++ {
        m[i] = struct{}{}
    }
}

for BoolMap(n int) {
    m := make(map[int] bool)
    for i := 0; i < n; i ++ {
        m[i] = flase
    }
}
  1. 使用atomic包
//high 
type atomicCounter strucrt {
    i int32
}

func AtomicAddOne(c *atomicCounter) {
    atomic.AddInt32(&c, i, 1)
}
// low
type mutexCounter struct {
    i int32
    m sync.Mutex//保护一段逻辑
}

func MutexAddOne(c *mutexCounter) {
    c.m.Lock()
    c.i++
    c.m.Unlock()
}

实例

工具

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

排查实战

使用浏览器查看指标。

  1. CPU查看,go tool pprof "address"
    • topN查看占用最多的函数
    • web调用关系可视化
  2. heap堆内存
    • go tool pprof -http=:8080 "address/heap"
  3. 协程
    • go tool pprof -http=:8080 "address/goroutine"
  4. mutex-锁
    • go tool pprof -http=:8080 "address/mutex"
  5. block
    • go tool pprof -http=:8080 "address/goroutine"

image.png