Go - 性能

pprof 性能分析

  • CPU Profiling
  • Memory(heap) Profiling
  • Block Profiling
  • Goroutine Profiling
  • Mutex Profiling

基础代码优化

slice

  • 使用 make([]T, len, cap) 预分配内存
    • 切片本质是对一个数组片段的引用,当使用 append 函数时,如果切片的容量不足,会重新分配一个新的数组,并将原数组的元素复制到新数组中
  • 使用 copy 函数复制 slice
    • copy 函数会在底层使用 memcpy 函数来复制内存,性能更高

map

  • 使用 make(map[T]T, cap) 预分配内存
    • map 的底层实现是一个哈希表,当使用 make 函数时,可以预分配内存,避免频繁的扩容和内存拷贝

string

  • 使用 strings.Builder 构建字符串
    • string 是不可变类型,每次修改都会创建一个新的字符串对象,性能较低
    • strings.Builder 底层使用一个可变的 byte 数组
    • bytes.Buffer 也可以使用,但是需要转换为 string 类型,转换时需要重新申请内存
    • strings.Builder 底层直接把 byte 数组转换为 string 类型,性能更高
    • strings.Builder 也可以使用 builder.Grow() 函数预分配内存,避免频繁的扩容和内存拷贝

struct

  • struct{} 空结构体不占用任何内存
    • 可以用来作为 map 的 value 或者 channel 的值

atomic

  • atomic 依赖于 CPU 指令直接在硬件层面实现,性能更高。多数情况下通过自旋完成,Goroutine 不会挂起。
  • mutex 依赖调度机制实现,当 Mutex 被占用时,尝试获取锁的 Goroutine 可能被调度器挂起,会伴随上下文切换和用户态/内核态切换的开销
  • mutex 用于保护一段代码逻辑,而 atomic 用于保护特定变量

优化注意事项

  • 在保证现有功能稳定的前提下改进具体实现
  • 测试用例需覆盖尽可能多的场景
  • 在文档中记录存在的问题、改进的原因、实现方法和预期效果
  • 通过选项控制是否启用新实现
  • 保证必要的日志输出,以便于后续排查问题
Licensed under CC BY-NC-SA 4.0
最后更新于 Feb 06, 2021 00:00 UTC