后端与go性能优化 | 青训营笔记

95 阅读2分钟

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

今天的课我很爱。老师引导的很详细了。

1.编程规范(粗略)

是说我咋有印象,原来是这个资料呀

xxjwxc/uber_go_guide_cn: Uber Go 语言编码规范中文版. The Uber Go Style Guide . (github.com)

代码格式

  • gofmt, goimports,用于格式化的小工具
  • 而一般用的go fmt是在调用 gofmt 时添加了-l -w参数,相当于执行了gofmt -l -w *.go
  • goimports需要安装并编译https://github.com/golang/tools.git,才能对包进行管理

注释

  • 用于易于维护,让别人更容易读懂
  • 应该解释代码作用,代码如何做的,代码实现的原因,代码什么情况会出错,
  • 在代码上下文较远时,也需注释
  • 除了实现接口的方法,对公共符号(大写首字母)的都需注释

命名规范

  • 包名只能由小写字母组成,简短并包含一定上下文信息,不与标准库同名。尽量不用常用变量名,不用复数,谨慎缩写
  • 文件名缩写统一大小写,公开的缩写单词都大写
  • 函数名不携带包名的上下文信息,因为包名和函数名总是成对出现的
  • 代码风格,减少缩进,减少嵌套,尽早返回,先判错误,

2.性能优化

工具

查看性能工具,Benchmark,见上篇介绍

性能分析工具,pprof

//用法 在 import 中添加 _ "net/http/pprof" 的引用
import (
 _ "net/http/pprof"
)
 //设置配置文件,测试运行时间10s
 go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"
//然后top查看
(pprof) top
Showing nodes accounting for 4.45s, 96.74% of 4.60s total
Dropped 41 nodes (cum <= 0.02s)
Showing top 10 nodes out of 23
      flat  flat%   sum%        cum   cum%
     3.98s 86.52% 86.52%      4.06s 88.26%  github.com/wolfogre/go-pprof-practice/animal/felidae/tiger.(*Tiger).Eat
     0.27s  5.87% 92.39%      0.27s  5.87%  runtime.stdcall3
     0.08s  1.74% 94.13%      0.08s  1.74%  runtime.asyncPreempt
     0.06s  1.30% 95.43%      0.06s  1.30%  runtime.stdcall6
     0.03s  0.65% 96.09%      0.04s  0.87%  runtime.(*pallocData).findScavengeCandidate
     0.01s  0.22% 96.30%      0.03s  0.65%  runtime.(*pageAlloc).scavengeReserve
     0.01s  0.22% 96.52%      0.03s  0.65%  runtime.runtimer
     0.01s  0.22% 96.74%      0.28s  6.09%  runtime.sysUnused
         0     0% 96.74%      4.06s 88.26%  github.com/wolfogre/go-pprof-practice/animal/felidae/tiger.(*Tiger).Live
         0     0% 96.74%      4.07s 88.48%  main.main
(pprof)
​
//精准定位
(pprof) list Eat
Total: 4.60s
ROUTINE ======================== github.com/wolfogre/go-pprof-practice/animal/felidae/tiger.(*Tiger).Eat in E:\GoProjects\src\go-pprof-practice\animal\felidae\tiger
\tiger.go
     3.98s      4.06s (flat, cum) 88.26% of Total
         .          .     19:}
         .          .     20:
         .          .     21:func (t *Tiger) Eat() {
         .          .     22:   log.Println(t.Name(), "eat")
         .          .     23:   loop := 10000000000
     3.98s      4.06s     24:   for i := 0; i < loop; i++ {
         .          .     25:           // do nothing
         .          .     26:   }
         .          .     27:}
         .          .     28:
         .          .     29:func (t *Tiger) Drink() {
//进入可视化界面         
(pprof)web

//注释掉tiger的Eat方法
(pprof) list Eat
Total: 20ms
(pprof) top
Showing nodes accounting for 20ms, 100% of 20ms total
Showing top 10 nodes out of 17
      flat  flat%   sum%        cum   cum%
      10ms 50.00% 50.00%       10ms 50.00%  runtime.adjusttimers
      10ms 50.00%   100%       10ms 50.00%  runtime.heapBitsSetType
         0     0%   100%       10ms 50.00%  github.com/wolfogre/go-pprof-practice/animal/felidae/cat.(*Cat).Climb (inline)
         0     0%   100%       10ms 50.00%  github.com/wolfogre/go-pprof-practice/animal/felidae/cat.(*Cat).Live
         0     0%   100%       10ms 50.00%  log.(*Logger).Output
         0     0%   100%       10ms 50.00%  log.Println
         0     0%   100%       10ms 50.00%  main.main
         0     0%   100%       10ms 50.00%  runtime.Caller
         0     0%   100%       10ms 50.00%  runtime.CallersFrames (inline)
         0     0%   100%       10ms 50.00%  runtime.checkTimers
(pprof)

//重新执行,heap进入内存分析的有菜单项可视化窗口,换为其他参数可访问其他分析的可视化窗口
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"

参数

flat     当前函数本身的执行耗时
flat%    flat占CPU总时间的比例
sum%/%   上面每一行的flat%总和
cum      当前函数本身加上其调用函数的总耗时
cum%/%   cum占CPU总时间的比例
​
flat==cum,函数中没有调用其他函数
flat==0,函数中只有其他函数的调用

优化措施

  • 对于slice和map尽量根据实际需求提前预估好cap,预分配内存
  • 对于原切片较大的需要其中的一部分,则新建一个切片进行copy,避免内存逃逸(切片操作时原切片和新切片共享底层数组,底层数组在内存有引用,得不到释放)
  • 字符串处理,速度string.Builder>bytes.Buffer>+,若预分配内存更快
func PrestrBuilder(n int, str string) string {
    var builder strings.Builder
    //扩容
    builder.Grow(n * len(str))
    for i := 0; i < n; i++ {
        builder.WriteString(str)
    }
        return builder.String()
}
  • 使用空结构体节省内存,空结构体不占任何内存空间,作为占位符节省资源同时具有很强语义。因此用map实现set时往往将值设为空接口体,bool都会多占一个字节空间。

    • 对于一个变量加锁使用atomic包(通过硬件实现)比加锁(通过操作系统实现,系统调用)效率高。加锁应用于保护一段逻辑。

参考

性能优化指南 - 掘金 (juejin.cn)