青训营笔记3 - GO工程进阶

67 阅读3分钟

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

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

高质量编程简介及编码规范

高质量编程

公共符号始终要注释

推荐使用gofmt自动化格式代码 或者 goimports = gofmt + 依赖包管理

命名规范

变量

  • 简洁
  • 缩略词全大写
  • 变量距离其背使用的地方越远,则需要携带越多的上下文信息

函数

  • 函数名尽量简短

  • 只由小写字母组成
  • 简短并包含一定的上下文信息
  • 使用单数而不是复数

控制流程

优先处理错误情况/特殊情况,尽早返回或继续循环来减少嵌套

错误和异常处理

简单的错误

  • 简单的错误指的是仅出现一次的错误,在其他地方不需要捕获该错误
  • 优先使用 errors.New来创建匿名变量来直接表示简单错误

复杂的错误

  • Panic不建议使用,表明程序崩溃不可用
  • recover只能在被defer的函数中使用,嵌套无法生效,只在当前 goroutine生效 , defer的语句是后进先出

性能优化指南

性能表现需要实际数据衡量,Go语言提供了支持基准性能测试的 benchmark工具

go test -bench=. -benchmem
复制代码

Slice预分配内存

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

  • 切片本质是一个数组片段的描述

    • 包括数组指针
    • 片段的长度
    • 片段的容量
  • 切片操作并不复制切片指向的元素

  • 创建一个新的切片会复用原来切片的底层数组

map

  • 原理

    • 不断向 map 中添加元素的操作会触发 map 的扩容
    • 根据实际需求提前预估好需要的空间
    • 提前分配好空间可以减少内存拷贝和 Rehash 的消耗
  • 使用 strings.Builder
    • 常见的字符串拼接方式

      • strings.Builder
      • bytes.Buffer
    • strings.Builder 最快,bytes.Buffer 较快,+ 最慢

    • 原理

      • 字符串在 Go 语言中是不可变类型,占用内存大小是固定的,当使用 + 拼接 2 个字符串时,生成一个新的字符串,那么就需要开辟一段新的空间,新空间的大小是原来两个字符串的大小之和
      • strings.Builder,bytes.Buffer 的内存是以倍数申请的
      • strings.Builder 和 bytes.Buffer 底层都是 []byte 数组,bytes.Buffer 转化为字符串时重新申请了一块空间,存放生成的字符串变量,而 strings.Builder 直接将底层的 []byte 转换成了字符串类型返回
  • 使用空结构体节省内存
    • 空结构体不占据内存空间,可作为占位符使用

    • 比如实现简单的 Set

      • Go 语言标准库没有提供 Set 的实现,通常使用 map 来代替。对于集合场景,只需要用到 map 的键而不需要值
  • 使用 atomic 包
    • 原理

      • 锁的实现是通过操作系统来实现,属于系统调用,atomic 操作是通过硬件实现的,效率比锁高很多
      • sync.Mutex 应该用来保护一段逻辑,不仅仅用于保护一个变量
      • 对于非数值系列,可以使用 atomic.Value,atomic.Value 能承载一个 interface{}
总结
  • 避免常见的性能陷阱可以保证大部分程序的性能
  • 针对普通应用代码,不要一味地追求程序的性能,应当在满足正确可靠、简洁清晰等质量要求的前提下提高程序性能

性能调优

主要是 pprof web页面查看各项指标进行评估调优。熟悉pprof使用。