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

79 阅读4分钟

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


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

高质量编程


编码规范
  • 原则:简单性、可读性、生产力

  • 格式化编码

    • gofmt: 自动化将代码统一成官方风格
    • goimports:在gofmt的基础之上,自动完成依赖包的引用管理,并将依赖包排序分类
  • 注释

    • 应当实现

      • 解释作用
      • 解释如何实现
      • 解释实现的原因
      • 解释出错的情况
    • 公共符号始终要注释,接口实现方法是一个例外

  • 变量

    • 缩略词全大写
    • 越远使用的变量更应当携带更多的上下文信息
  • 函数

    • 函数名尽量不携带包名的上下文信息
    • 只由小写字母组成
    • 不与标准库同名
    • 包含一定的上下文信息
    • 使用单数而不是复数
    • 不使用常用变量名作为包名
  • 控制流程

    • 优先处理错误和特殊情况,保证正常流程最小缩进
  • 错误和异常

    • 简单错误使用erros.New来新建匿名变量来直接表示
    • 对错误使用Wrap和Unwrap进行嵌套,生成error跟踪链
    • 错误判定:errors.Is, 获取特定错误类型:errors.As
    • 一般业务代码不建议使用panic,只有在程序启动过程中出现不可逆转的错误时,在init或main中使用panic
    • recover只在defer中生效,嵌套无效,且只对当前goroutine生效。 一般使用recover恢复后,在log中记录调用栈
性能优化建议
  • 使用基准测试Benchmark go test -bench . -benchmem

    image-20230117145824603.png

  • Slice

    • 尽可能在make()初始化切片时提供容量信息

    image-20230117150104754.png

    • 大内存未释放,在原有切片的基础之上新建小切片导致原底层数组任有引用,得不到释放

      优化方法:使用copy代替re-slice

      image-20230117150115585.png

  • map

    • 预分配内存,原理同slice
  • 字符串处理

    • 使用strings.Builder代替字符串“+”拼接 原因在于字符串拼接每次都会重新分配内存,而strings.Builder和bytes.Buffer底层都是[]byte数组

      相对而言strings.Builder比bytes.Buffer性能更优,原因后者在String()中转换成字符串时重新申请了一块空间,而前者是直接进行类型转换后返回的

      image-20230117150741473.png

  • 空结构体

    • 空结构体struct{}实际上不占用任何内存空间
    • 空结构体的作用:实现Set 当map的value为空结构体时,value不占用空间,只使用其key,即可实现Set的功能

    image-20230117150845760.png

  • atomic包

    • atomic包是实现并发同步的原子性的包,其底层是通过硬件实现的,效率比锁高。
    • sync.Mutex应当被用于保护一段逻辑而不是一个变量,变量的维护可以使用automic包

性能调优实战


  • pprof

    • CPU监控

      • go tool pprof "http://localhost:6060/debug/pprof/profile?second=10":监控10s内的CPU对程序的处理情况,并转储为文件
      • top:展示占用资源最多的函数
      • list:根据指定的正则表达式查找代码行

      image-20230117220151267.png

    • web可视化排查 go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/[goroutine/block/heap/mutex...]"

    • 一般查找优化流程: 配置pprof,使用runtime开启调用追踪-》

      使用web查看监控指标[allocs\block\goroutine\mutex\heap...]-》

      根据具体的监控指标,调用对应的web页面查看详情-》根据详情溯源代码位置-》

      准备好测试数据记录优化前正确的逻辑结果-》

      对代码逻辑进行优化-》

      重新加载pprof监控,查看优化后的是否有提升-》

      重新运行优化后的系统加载准备的测试数据,对优化前后的输出进行对比

  • pprof采样大致原理

    • CPU:pprof进程启动定时器,每隔10ms,OS向进程发送SIGPROF信号。进程每次接收到SIGPROF都将保存到堆栈。每100ms将记录的调用栈写入输出流。
    • Heap:通过采样内存分配器在堆上分配的内存,记录分配和释放的大小和数量。每分配512KB记录一次。记录总分配空间(alloc_space),总分配对象数(alloc_objects),当前持有对象数(inuse_objects),当前占用内存大小(inuse_spaces)
    • Gorountine&ThreadCreate:遍历创建链表,输出创建的堆栈。
    • Block&Mutex:采样阻塞(争抢锁)的次数和耗时。只有阻塞超时(固定比例锁操作)才记录。

引用参考


‍‬‬‬‬‍⁣‌‬‍⁣⁣‬⁡⁡‌⁤⁢⁤‌⁡⁤‌‬‍‬⁤⁢⁣‌‍‍⁢高质量编程与性能调优实战 .pptx - 飞书云文档 (feishu.cn)

Go 语言高性能编程 | 极客兔兔 (geektutu.com)

wolfogre/go-pprof-practice: go pprof practice. (github.com)