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

122 阅读5分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记

本篇笔记对课上提到的高质量编程规范进行一个总结,对性能调优实战附上自己的实验过程

1.高质量编程

1.1 什么是质量编程?

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

1.2 编码规范

如何编写高质量的Go代码

  • 代码格式:使用gofmt自动格式化代码
  • 注释
  • 命名规范
  • 控制流程
  • 错误和异常处理

1.2.1 注释

  1. 解释代码作用 ①注释公共符号、函数的功能等。
  2. 解释代码如何做的 ①适合注释实现的过程。
  3. 解释代码实现的原因 ①适合解释代码的外部因素。 ②提供额外的上下文。
  4. 解释代码在什么情况下会出错 ①适合解释代码的限制条件。
  5. 公共符号始终要注释 ①包中声明的每个公共符号:变量、常量、函数以及结构都需要添加注释。 ②任何既不明显也不简短的公共功能必须注释 ③无论长度或复杂程度如何,对库中的任何函数都必须进行注释 *不需要注释实现接口的方法

1.2.2 命名

变量命名
  • 简洁>冗长
  • 缩略词全大写,但当其位于变量开头且不需要导出时,使用全小写 例如使用ServeHTTP而不是ServeHttp
  • 变量距离其被使用的地方越远,则需要携带越多的上下文信息
举例

在for循环内部使用 "i"比"index"更为直接明了

在函数传参时,使用带有更多信息的变量更有助于理解功能

函数命名
  • 函数名不携带包名的上下文信息,因为包名和函数名总是成对出现的
  • 函数名尽量简短
  • 当名为"foo"的包的某个函数返回类型为"Foo"时,可以省略类型信息而不导致歧义
  • 当名为"foo"的包的某个函数返回类型T时,可以在函数名中加入类型信息
举例

例如调用http包中的Server方法,代码是http.Server,带有http包名就不需要添加包信息

1.2.3 控制流程

  • 避免嵌套,保持正常流程清晰 ① 如果两个分支中都包含return 语句,则可以去掉冗杂的else
  • 尽量保持正常代码路径为最小缩进
    //“不符合编程规范的代码”
    func OneFunc() error{
    	err :=doSomething()
    	if err==nil{
    		err:=doAnotherThing()
    		if err==nil{
    			return nil
    		}
    		return err
    	}
    	return err
    }
    //改进后 代码
    func OneFunc() error{
    	if err :=doSomething;err!=nil{
    		return err
    	}
    	if err:=doAnotherThing();err!=nil{
    		return err
    	}
    	return nil
    }
    

小结:线性原理,处理逻辑尽量走直线;正常流程代码沿着屏幕向下移动;在复杂的条件语句和循环语句中容易出现故障问题(遗漏部分条件)

1.2.4 错误和异常处理

  1. 简单错误
    • 简单的错误指的是仅出现一次的错误,且在其他地方不需要捕获该错误
    • 优先使用errors.New来创建匿名变量来直接表示简单错误
    • 如果有格式化的需求,使用fmt.Errorf

1.3 性能优化

Slice

经可能在使用make()切片时提供容量信息

字符串处理

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

原因:

  • 字符串在Go语言中是不可变类型,占用内存大小是固定的
  • 每次使用"+"都会重新分配内存
  • 内存扩容策略不需要每次拼接重新分配内存
  • bytes.Buffer转化为字符串时重新申请了一块空间
  • strings.Builder直接将底层的[]byte转换成了字符串类型返回

疑问:

strBuilder程序执行内存分配数目大于ByteBuffer?

空结构体
atomic包

1.3 小结

  • 避免常见的性能陷阱可以保证大部分程序的性能
  • 普通应用代码,不要一位追求程序的性能
  • 越高级的性能优化手段越容易出现问题
  • 在满足正确可靠、简洁气息的质量要求的前提下提高程序性能

2 性能调优实战

2.1 性能调优原则

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

2.2 pprof

pprof是用于可视化和分析性能分析数据的工具

image.png 第一步首先在windows环境中安装 graphviz,并将其添加到环境变量,这里就不过多阐述 复制代码到本地后:终端运行go run main.go

2.2.1 CPU优化

在新的终端中运行go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10" ;此时,先采集10秒的数据到文件中pprof监测工具先采集10秒的数据到文件中再进行性能分析;终端输入 top 查看程序CPU使用情况 image.png

image.png 可知是tiger.(*Tiger).Eat占用了“过多的”CPU,使用命令list Eat

image.png 可知是func (t *Tiger) Eat()函数中使用无意义的for 循环导致大量占用cpu的情况; 使用web可以将调用关系可视化

image.png 将其注释掉并再次运行

image.png

2.2.2 内存优化

终端再次输入 go tool pprof http://localhost:6060/debug/pprof/heap 以及top

image.png

image.png 可知是Steal函数占用内存过多,list Steal查看细节:

image.png 可知是for循环不断给m.buffer追加1MB的内容,并设定上限1GB,推测这是程序占用高内存的原因。 将其注释后再次运行

image.png

以下的锁(mutex),携程(goroutine),阻塞(block)分析方法类似