Go的高质量编程 | 青训营笔记

113 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第3篇笔记,学校事情好多,记的效率太慢了。

高质量编程的简介

编写的代码能够达到正确可靠,简介清晰的目标可称为高质量代码

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

编程原则

应用场景千变万化,各种语言的特性和语法不同

但高质量编程遵循的原则是相同的

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

编码规范

代码格式

go语言官方自己有一个gofmt的工具,实现统一的编码规范

同时也可以使用goimports这个工具

注释

注释的用法:

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

总之,代码是最好的注释,注释应该提供代码来表达出上下文的信息。

命名规范

变量
  • 简洁胜于冗长
  • 缩略词要全大写
函数
  • 函数名无需携带包的上下文信息
  • 函数名尽量简短
  • 只由小写字母组成,不包含大写字母和下划线等其他
  • 简短并包含上下文信息
  • 不与标准库同名

总结,命名规范是为了降低代码的阅读难度。

控制流程

  • 线性原则,处理逻辑尽量走直线,避免复杂的嵌套
  • 正常流程代码沿着屏幕向下移动
  • 提高可读性和可维护性
  • 故障大多出现在复杂的条件语句和循环语句

错误处理

error是简单的错误,比error严重的错误是panic

在业务中尽量不要使用panic,因为panic会导致程序崩溃

性能优化

Benchamrk

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

benchmark.jpg

slice优化

使用slice的时候最好先预分配内存

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

slice的底层是一个数组片段的描述

包括:

  • 一个指向底层数组的指针
  • 数组片段的长度
  • 数组片段的容量(不改变内存分配的情况下的最大长度)
特殊情况

如果原切片是由大量元素组成的,但是我们在原切片的基础上切片,虽然只使用了一小段,但底层数组在内存中仍然存在,占据大量的空间。

解决办法:使用copy代替re-slice

map优化

map跟slice一样,最好也要预分配内存

因为不断向map里添加元素会不断触发map的扩容

提前分配好空间可以减少内存的拷贝和ReHash的消耗

字符串优化

如果需要频繁拼接字符串,不要使用string

最好使用strings.Builder

因为字符串在go中的字符串是常量,占用的内存是固定的

使用string的+拼接会重新分配内存,造成内存浪费,性能最差

bytes.Builder和strings.Builder性能相近,strings.Builder会更快一些

空结构体优化内存

空结构体不占用内存,可作为各种场景下的占位符使用

atomic包

锁的实现是通过操作系统来实现的,属于系统调用

atomic操作时通过硬件来实现的,效率比锁高

sync。Mutex应该用来保护一段逻辑,不仅仅用来保护变量

对于非数值操作可以使用atomic.Value(),能承载一个interface{}

性能优化的层面

  • 业务层优化
  • SDK
  • 基础库
  • 语言运行时优化
  • OS

业务层主要针对特定的场景进行优化,一般可以获得较大的性能收益。

语言运行时的优化主要是解决更加通用的问题,考虑更多的场景,Tradeoffs

数据驱动

  • 自动化性能分析工具——pprof
  • 依靠数据而非猜测
  • 首先优化最大的瓶颈