代码质量规范&&性能优化 |青训营

86 阅读6分钟

高质量代码规范

  1. 注释
  • 库中任何函数都必须注释
  • 任何不明显也不简短的功能必须注释
  • 在一个包中声明的函数、常量、变量和结构体都要注释
  • 函数应当描述自己的输入、作用、预期输出等
    注释应当具有以下作用:
    1.1 注释应当做到的
    • 解释代码做的是什么
    • 解释代码如何运行的
    • 解释代码实现的原因,为什么在这里要这么写
    • 解释调用代码的限制条件和在不遵守调用规范时会出现什么问题
    • 公共符号也要注释
  1. 代码格式

gofmt 自动格式化代码的工具
goimports 包管理工具
3. 命名规范

  • 变量名
    • 简洁胜于冗长
    • 简称全大写,当缩略词位于变量开头且无需导出则可使用全小写
    • 用变量用的越远(越偏僻的变量),应当携带更多的上下文信息
  • 函数名
    • 尽量简短
    • 不必包含包的信息,因为包名和函数类型总会一起出现。
    • 当返回的类型与包名一致时,可省略类型信息,不一致时则需要包含
  • 包名
    • 仅包含小写字母
    • 简短且包含一定的上下文
    • 不能与标准库重名
    • 绕开常用变量名
    • 使用单数
    • 谨慎地使用缩写
  1. 编码规范
  • 优先处理错误和特殊情况,一旦出现直接结束调用并返回err而非判断接判断接判断接判断。。。,减少嵌套。
    4.1 错误处理
  • error
    • 错误相关的wrap和unwrap
      这两个中的wrap提供了一个err嵌套另一个err的能力,生成一个err的跟踪链路,方便措施排查,可使用fmt.Errorf("%w",err)即使用%w关键字关联它。
    • errors.Is(),可判定某个错误是否为特定错误,也可判定错误链上是否含有特定种类的错误
    • errors.As(),可获取特定种类错误的路径
  • panic()
    • 当程序在启动发现了无法逆转的致命错误时,可在初始化(init)或者main函数中使用panic
    • 平时尽量别用

recover:出现pannic时用的(什么东西?)生效于当前的goroutine的被defer的函数

性能优化

保证正确运行
定位主要瓶颈再优化

benchmark

使用 go提供的benchmark工具可得到一个结果
从左到右依次为测试函数名-CPU核数、执行次数、每次执行花费时间、每次执行申请的内存大小、每次执行申请内存的次数

Tips : 尽量减少内存分配的次数

关于切片的优化建议

  1. 尽可能在初始化时指定切片大小(make([]Type,len)),可以极大的减小执行时间(主要是减少内存分配的次数)
  2. 在对切片中部分值进行取用时,尽量先新建一个切片,再使用copy获取对应位置的值再用于返回或者读取;而非直接截取该切片所需位置的值用于使用

原因:直接截取时go会复用原切片的底层数组,多次取用时,如果原切片的大小较大,底层数组所占内存也会很大,这时由于采用的是复用机制,内存占用也会很大且不会被释放,采用cpoy则会明显改善这个问题

tips:关于切片的取用,[:]左右两边的默认值分别为0和切片的当前长度,当对:两边的的数字进行修改时则按修改的数字取对应的位数

关于map

也建议在已知大概容量的情况下预分配大小。map_variable:=make(map[KeyType]ValueType,initialCapacity )

关于字符串

拼接

常见的拼接方式有三种,strings.Builder+ByteByffer
拼接操作中直接使用+号的性能最差,建议使用strings.Builder,bytebuffer稍慢于strings.Builder

使用+号的执行效率很差,原因是在go中,字符串是不可变类型,每次使用+号都会重新分配内存,时间会变得很长,而另外两个底层使用的[]byte数组,故性能更好。

Grow()方法:后两种方法都有这个方法,Grow(n*len(str))相当于预分配大小

关于空结构体

空结构体相较布尔变量还可节省一部分空间,在某些场景的map[][]相关操作可能会用到

不可变类型
变量对应内存地址中的内容不可通过赋值改变。 ## 原子包(atomic包) 和多线程有关,当需要对一个多线程中的变量进行保护时可考虑使用atomic包 对于非数值的操作也可考虑atomic.Value >优化越刁钻越容易出问题

性能分析工具

  • pprof

使用简介? go tool pprof PPROFURL/api

cpu相关

执行

go tool pprof PPROFURL/profile?seconds=<TIME>
#<TIME>就是秒数

top

当正常进入类似(pprof)的软件后
输入top会得到一个列表

flatflat%sum%cumcum%
当前函数本身执行耗时flat占cpu总时间的比例上面每一行flat的总和当前函数本身加上其调用函数的总耗时cum占用cpu的总时间的比例

list

当使用top定位到问题函数后 使用list <Targetfunc>就可以查看该函数中每一行代码的详细占用时间。

内存相关

使用例子

go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"

此时访问localhost:8080就可查看堆内存相关的页面了。

和上面相同,top可查看类似的占用列表,而和cpu不同的是,查看代码的占用内存时需要使用source命令

页面样的SAMPLE选项

localhost:8080/ui上存在一个SAMPLE按钮,按下后会出现alloc_objects(程序累计申请的对象数)、alloc_space(程序累计申请的内存大小)、inuse_objects(程序当前使用的对象数)、inuse_space(程序当前占用的内存大小)

goroutine协程

使用例子

go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/goroutine"

常用火焰图进行直观的动态展示

找到对应的问题函数后使用source可定位到问题代码。

mutex-锁

使用例子

go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/mutex"

依然是使用source进行定位

block-阻塞

同上,只需将pprof后的内容换成block就行

上面可去掉-http选项,此时展示的就是控制台信息了

pprof会过滤掉一些占用很小的函数,若要查看全量的信息,则可以在proof主页面上直接查看