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

77 阅读4分钟

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

一、本堂课重点知识

本节课主要学习内容是编码规范以及性能调优。

二、详细知识点介绍

  • 如何编写更简洁清晰的代码
  • 常用Go语言程序优化手段
  • 熟悉Go程序性能分析工具
  • 了解工程中性能优化的原则和流程

1. 高质量编程

1.1 简介

  • 什么是高质量

    • 编写的代码能够达到正确可靠、简洁清晰的目标可称之为高质量代码
  • 各种边界条件是否考虑完备

  • 异常情况处理,稳定性保证

  • 易读易维护

  • 编程原则

    • 简单性
    • 可读性
    • 生产力

1.2 编码格式

  • 如何编写高质量的Go代码

    • 代码格式
    • 注释
    • 命名规范
    • 控制流程
    • 错误和异常处理
  • 公共符号始终要注释

    • 包中声明的每个共欧诺个的符号:变量、常量、函数以及结构都需要添加注释
    • 任何既不明显也不简短的公共功能都必须予以注释
    • 无论长度或复杂程度如何,对库中的任何函数都必须进行注释
    • 不需要注释实现接口的方法
  • 代码格式

    • gomft
    • goimports
  • 注释应该做的

    • 注释应该解释代码作用
    • 注释应该解释代码如何做的
    • 注释应该解释代码实现的原因
    • 注释应该解释代码什么情况会出错

Good code has lots of comments, bad code requires lots of comments

  • 命名规范

    • variable
      • 简洁胜于冗长
      • 缩略词全大写,但当其位于变量开头且不需要导出时,使用全小写
      • 变量距离其被使用的地方越远,则需要携带越多的上下文信息
    • function
      • 函数名不携带包名的上下文信息,因为包名和函数名总是成对出现的
      • 函数名尽量简短
      • 当名为foo的包某个函数返回类型Foo时,可以省略类型信息而不导致歧义
      • 当名为foo的包某个函数返回类型T时(T并不是Foo),可以在函数名中加入类型信息
    • package
      • 只由小写字母组成,不包含大写字母和下划线等字符
      • 简短并包含一定的上下文信息
      • 不要与标准库同名
      • 不使用常用变量名作为包名
      • 使用单数而不是复数
      • 谨慎的使用缩写
  • 控制流程

    • 避免嵌套,保持正常流程清晰
    • 尽量保持正常代码路径为最小缩进
  • 错误和异常处理

    • 简单错误
    • 错误的Warp和Unwrap
    • 错误判定
    • panic
    • recover

1.3 性能优化建议

  • 性能优化的前提是满足正确可靠、简洁清晰等质量因素

  • 性能优化是综合评估,有时候时间效率和空间效率可能对立

  • 针对Go语言特性,介绍Go相关的性能优化建议

  • Benchmark

go test -bench=. -benchmem

  • slice预分配内存

    • 切片本质是一个数据片段的描述
      • 包括数组指针
      • 片段的长度
      • 片段的容量(不改变内存分配情况下的最大长度)
    • 切片操作并不复制切片指向的元素
    • 创建一个新的切片会复用原来切片的底层数组
  • 大内存未释放

    • 在已有切片基础上创建切片,不会创建新的底层数组
    • 场景
      • 原切片较大,代码在原切片基础上新建小切片
      • 原底层数组在内存中有引用,得不到释放
    • 可使用copy替代re-slice
  • map预分配内存

  • 使用+拼接性能最差,strings.Builder,bytes.Buffer相近,strings.Buffer更快

    • 字符串在Go语言中是不可变类型,占用内存大小是固定的
    • 使用+每次都会重新分配内存
    • strings.Builder,bytes.Buffer底层都是[]byte数组
    • 内存扩容策略,不需要每次拼接重新分配内存
  • 空结构体

    • 空结构体struct{}实例不占据任何的内存空间
    • 可作为各种场景下的占位符使用
      • 节省资源
      • 空结构体本身具备很强的语义,即这里不需要任何值,仅作为占位符
  • atomic包

    • 锁的实现是通过操作系统来实现,属于系统调用
    • atomic操作是通过硬件实现,效率比锁高
    • sync.Mutex应该用来保护一段逻辑,不仅仅用于保护一个变量
    • 对于非数值操作,可以使用atomic.Value,能承载一个interface{}

2. 性能调优实战

2.1 性能调优原则

  • 要依靠数据不是猜测
  • 要定位最大瓶颈而不是细枝末节
  • 不要过早优化
  • 不要过度优化

2.2 性能分析工具 pprof

  • 说明
    • 希望知道应用在什么地方耗费了多少CPU、Memory
    • pprof是用于可视化和分析性能分析数据的工具

三、实践练习例子

pprof排查实战

四、课后个人总结

本节课最主要还是在保证代码质量的前提上,适度对代码进行优化。

五、引用参考

golang pprof 实战 | Wolfogre's Blog