高质量代码

103 阅读3分钟

什么是高质量代码

一句话来说就是:编写的代码能够达到正确可靠、简洁清晰的目标可称之为高质量代码

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

编程原则

  • 简单性
    • 消除多余的复杂性
    • 不理解代码无法跟进修复
  • 可读性
    • 代码要可以让别人读懂
  • 生产力
    • 团队整体工作效率要高

编码规范

  • 代码格式
    • gofmt
    • goimport
  • 注释
    • 注释应该解释代码功能
    • 注释应该解释代码实现的原因
    • 注释应该解释代码在什么时候会出错
    • 公共符号始终要注释
  • 命名规范
    • 变量名
      • 简介胜于冗长
      • 缩略词全部大写,但当其位于变量开头且不需要导出时,应该使用小写
      • 变量距离离变量使用的地方越远,越需要上下文信息
    • 函数名
      • 函数名不携带包名的上下文信息
      • 函数名尽量简短
    • 包名
      • 只由小写组成
      • 不要与标准库同名
      • 简短且包含一定的上下文信息
  • 控制流程
    • 避免嵌套,保证流程清晰
    • 尽量保证代码为最小缩进
  • 错误和异常处理
    • 简单错误
      • 简单的错误指的是仅出现一次的错误,且在其他地方不需要捕获该错误
      • 优先使用 errors.New 来创建匿名变量来直接表示简单错误
      • 如果有格式化的需求,使用 fmt.Errorf
    • 错误的Wrap和Unwrap
    • 错误判定
    • Panic
      • 不建议在业务中使用panic
      • 如果问题可以解决的话,尽量使用error代替panic
    • Recover
      • recover只能在defer中使用
      • 嵌套无法生效
      • 只在当前协程中生效
      • defer时后进先出

性能优化建议

Benchmark测试效率

image.png

Slice预分配内存

尽可能在使用make初始化切片时提供内容信息可以减少切片的创建(节省时间)

另外一个问题:大内存没有释放

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

Map预分配内存

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

字符串处理

  • 常见的字符串拼接方式
    • 直接使用String 加法效率最差
    • 使用String Builder时 效率更好
    • 使用Strung Buffer 效率最好
  • 分析
    • 字符串在 Go 语言中是不可变类型,占用内存大小是固定的
    • 使用+每次都会重新分配内存
    • strings.Builder, bytes.Buffer 底层都是 []byte 数组
    • 内存扩容策路,不需要每次拼接重新分配内存

使用空结构体来节省空间

  • 可以用Map加空结构体来实现Set

Atomic包

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

性能优化分析

性能调优原则

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

pprof

image.png

image.png