高质量编程及性能调用| 青训营笔记

71 阅读3分钟

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

青训营掘金课程Day03-高质量编程及性能调用

高质量编程

高质量编程简介

何为高质量?

  • 各种边界条件是否考虑完备
  • 异常情况处理,稳定性保证
  • 易读易维护

我们需要遵循的编程原则:

  1. 简单性: 消除“多余的复杂性”,以简单清晰的逻辑编写代码
  2. 可读性: 代码给人看的
  3. 生产力:团队整体工作效率非常重要

编码规范

编码规范 : 如何写出高质量的Go代码

  • 代码格式
  • 注释
  • 命名规范
  • 控制流程
  • 错误和异常处理

注释:

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

image.png

代码格式:

推荐使用gofmt自动格式化代码

注释:

  • 适合注释实现过程
  • 提供额外上下文,适合解释代码的外部因素
  • 适合注释代码的限制条件

命名规范:

  • 简洁胜于冗长
  • 缩略词全大写,但当其位于变量开头全小写

性能优化建议

Benchmark

go自带的性能评估工具

  • 性能表现需要实际数据衡量
  • Go语言提供了支持基准性能测试的benchmark工具 go test -bench=. -benchmen

slice 预分配内存

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

image.png

为什么预分配使用的内存少些? 原因: 切片本质是一个数组片段的描述

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

image.png

陷阱:大内存未释放

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

Map 预分配内存

image.png

image.png 分析:

  • 不断向map中添加元素的操作会触发 map的扩容
  • 提前分配好空间可以减少内存拷贝 和 Rehash的消耗
  • 建议根据实际需求提前预估好需要的空间

使用 strings.Builder

  • 常见的字符串拼接方式(类似于java的stringBuilder

image.png

image.png

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

分析:

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

使用空结构体

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

使用atomic包

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

性能调优实战

性能调优简介

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

性能分析工具pprof实战

说明

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

image.png

image.png

说明:关于调优案例那块没有记笔记了,都是实战,而且对我这种javar感觉有点深,需要深究。