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

77 阅读3分钟

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

# Go高质量编程和编码规范

高质量编程

标准:正确可靠,简洁清晰

  1. 边界条件是否完备
  2. 异常情况处理,稳定性保证
  3. 易读易维护

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

如何编写高质量Go代码:

  • 代码格式

    • gofmt和goimports
  • 注释

    • 公共符号注释,向外部暴露的方法要详细注释
    • 实现接口方法,是不需要注释的
    • 注释应注释代码作用,如何做,实现的原因,什么情况会出错
  • 命名规范

    • 简洁胜于冗长

    • 缩略词全大写,位于开头但不需要导出时,要全小写

    • 全局变量多带上下文信息

    • Go语言中包和函数名是成对出现的,所以函数名应该不包括上下文信息,尽量简短。

    • 包名尽可能简短,包含一定上下文信息,不包括大写字幕和下划线;不要和标准库同名。使用单数而不是复数。

  • 控制流程

    • 避免嵌套,如果两个分支均包含return,可以去掉冗余else
    • 优先处理错误和特殊情况,代码尽早返回减少嵌套
  • 错误处理和异常处理

    • 简单错误,可以errors.New创建匿名错误;或者fmt.Errorf进行错误格式化输出
    • 复杂错误,错误Warp提供error嵌套error的能力,可以通过fmt.Error("%w")来将错误关联到错误链
    • 错误判断,errors.Is(err, 错误类型),判断错误链上是否有指定类型的错误
    • 错误提取,可以定义一个错误类型的指针,将指针的地址传入error.As(err, &pointer)来将错误提取到pointer中
    • panic,尽量少用;一般是init时,启动失败提前panic;程序运行中尽量不要
    • recover,只能再defer中使用,且只在当前的协程生效;可以通过recover获取debug中的调用栈。

性能调优实战

性能评估:benchmark

slice预分配内存,尽量在make的时候就分够容量

在已有切片创建切片,会导致原切片引用的数组迟迟得不到释放,解决方法是使用copy

map预分配内存

字符串处理使用string.Builder拼接字符串性能更好;strings.Builder也可以使用通过builder.Grow(大小)来提前预分配内存

空结构体,struct{}不占用内存空间,可以作为占位符;比如使用map[keyType]struct{}来实现一个集合Set。此处值是不占用空间的。

多线程计数器,除了传统的加锁sync.Mutex的方式,还可以synb/atomic。atomic硬件实现,效率比锁高,更适合做计数器。sync.Mutex更适合保护一段代码逻辑。

性能优化分析工具 pprof

  • 依靠数据而不是猜测
  • 定位最大拼接而不是细枝末节
  • 不宜过早优化
  • 不应过度优化

pprof

  • cpu,堆内存,协程,锁,阻塞,线程数据采样
  • 网页,终端分析工具,各种可视化展示
  • runtime/pprof,net/http/pprof

go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"

top,查看占用情况,类似linux中的top web,调用关系可视化

go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"

go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/goroutine"

go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/mutex"

go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/block"