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

88 阅读6分钟

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

本篇文章主要讲述在编程过程中需要注意的相关规则,并通过主要的实战例子进行讲解。 主要分为两块:高质量编程、性能调优实战

1.高质量编程

所谓高质量编程,即:编写的代码能够达到正确可靠、简洁清晰的目标可称之为高质量代码。

1.1 编码原则介绍

简单性

消除“多余的复杂性”,以简单清晰的逻辑编写代码。

不理解的代码无法修复改进。

可读性

代码是写给人看的,而不是机器。

编写可维护代码的第一步是确保代码可读性。

生产力

团队整体工作效率非常重要。

1.2 编码规范

该节主要介绍在编写代码过程中我们需要如何将代码写的更加规范,方便后续编程人员的理解和解读。因此,将主要从以下五个方面进行介绍。

1.2.1 代码格式

  • 第一种格式为:gofmt

该格式是Go语言官方提供的工具,能够自动格式化Go语言代码为官方统一风格。

  • 第二种方式为:goimport

该格式也为Go语言官方提供的工具,实际等于gofmt加上依赖包管理,可以自动增删依赖的包引用、将依赖包按字母序排序并分类。

1.2.2 注释

1、注释需要适合公共符号

2、注释在代码编写的过程中需要简洁且适合实现过程

3、注释可以解释代码的外部因素,并提供额外的上下文,方便读者进行理解

4、注释要适合代码的限制条件

5、注释过程中需要将公共符号进行注释,如:

  • 包中声明的每个公共的符号:变量、常量、函数以及结构都需要添加注释;

  • 任何既不明显也不简短的公共功能必须予以注释,对于具体的例子,如;LimitedReader.Read本身没有注释,但它紧跟LimitedReader结构的声明,明确它的作用;

  • 无论代码的长度或复杂度如何,对库中的任何函数都必须进行注释

1.2.3 命名规范

  • 对于简单的语句中,代码命名的小规则:

i和index作用在for循环中时,可以直接用i进行命名,因为index的额外冗长几乎不会增加对程序的进一步理解。

  • 对于具体的函数的命名,需要注意的小规则:

函数名不携带包名的上下文信息,因为包名和函数名总是成对出现的

函数名尽量简短

当名为foo的包某个函数返回类型Foo时,可以省略类型信息而不导致歧义

当名为foo的包某个函数返回类型T时(T并不是Foo),可以在函数名中加入类型信息

  • 对于具体的包的命名,需要注意的小规则:
  1. 只有小写字母组成,不包含大写字母和下划线等字符

  2. 简短并包含一定的上下文信息,例如schema、task等

  3. 不要与标准库同名,例如不要使用sync或者sttings

  4. 不使用常用变量名作为包名,例如使用bufio而不是buf

  5. 使用单数而不是复数,例如使用encoding而不是encodings

  6. 谨慎使用缩写,例如使用fmt在不破坏上下文的情况下比format更加简短

1.2.4 控制流程

1、在嵌套语句中,若两个分支中都包含return语句,则可以去除冗余的else,即:避免嵌套,保持正常流程清晰

2、优先处理错误情况/特殊情况,尽早返回或继续循环来减少嵌套,即:尽量保持正常代码路径为最小缩进

注意:故障问题大多出现在复杂的条件语句和循环语句中

1.2.5 错误和异常处理

  • error尽可能提供简明的上下文信息链,方便定位问题

  • panic用于真正异常的情况

  • recover生效范围,在当前goroutine的被defer的函数中生效

1.3 性能优化建议

本节主要对常见的几个需要进行优化的问题进行阐述,提出相应的优化改进建议。

1.3.1 切片

切片本质是一个数组片段的描述,包括数组指针,片段的长度,片段的容量,切片操作并不复制切片指向的元素,创建一个新的切片会复用原来切片的底层数组

注意:当大内存未释放时,若已有切片,则不会创建一个新的底层数组。可以使用copy替代re-slice。

1.3.2 Map

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

2.性能调优实战

2.1 简单介绍

在性能调优实际排查项目中,主要用到的工具是PProf.

这里主要用到的项目代码如下:

import {
    "log"
    "net/http"
    _"net/http/pprof"
}

func main(){
    log.SetFlags(log.Lshortfile | log.LstdFlags)
    log.SetOutput(os.Stdout)
    
    runtime.GOMAXPROCS(1)
    runtime.SetMutexProfileFraction(1)
    runtime.SetBlockProfileRate(1)
    
    go func() {
        if err := http.ListenAndServe(":6060",nil); err != nil {
            log.Fatal(err)
        }
        os.Exit(0)
    }()
}

对系统的调优主要从CPU、Heap-堆内存、Goroutine-协程和ThreadCreate-线创建、Block-阻塞和Mutex-锁等方面进行操作。

2.2 业务服务优化

基本概念

  • 服务:能单独部署,承载一定功能的程序

  • 依赖:Service A的功能实现依赖Service B的响应结果,称为Service A依赖Service B

  • 调用链路:能支持一个接口请求的相关服务集合及其相互之间的依赖关系

  • 基础库:公共的工具包、中间件

流程

  • 建立服务性能评估手段

  • 分析性能数据,定位性能瓶颈

  • 重点优化项改造

  • 优化效果验证

总结

通过本次课程的学习,我了解了编程过程中需要注意的相关规则,也对自己今后写代码有了一定的约束和参考。同时还了解到关于在工作中,即实际项目中,我们需要对代码进行优化,不仅可以提高代码的运行速度,同时能够减少内存的存储。当然,在程序方面自己还有很大的不足,需要不断改进和提升。

引用

本文的内容来自于字节内部课程。