golang 性能分析

性能分析

  • 从业务目标角度,通常我们的瓶颈出现在业务的通量和时延两个问题上
    • 比如一个MySQL数据库,你从其他机器上对它发请求,每秒它能处理10万个请求,这个就是通量性能;每个请求的反应时间是0.5ms,这个就是时延性能
    • 通量和时延是相互相承的两个量,当通量达到系统上限,时延就会大幅提高
  • 一般而言,性能分析主要关注 CPU、内存、磁盘 IO、网络这些指标

pprof

三种采样方式

runtime/pprof

  • 适用于程序只运行一次的,例如每天只跑一次的离线预处理程序

  • 采样

    • 运行程序后生成cpu.profmem.prof两个文件
import "runtime/pprof"

func main() {
    f, _ := os.Create("cpu.prof")
    pprof.StartCPUProfile(f)
	defer pprof.StopCPUProfile()
    
    ff, _ := os.Create("mem.prof")
    pprof.WriteHeapProfile(ff)
    
    // 待测试的程序
}

net/http/pprof

  • 适用于程序长期运行的线上服务
    • 底层也是调用的 runtime/pprof 提供的函数,封装成接口对外提供网络访问
  • 如果是使用了HTTP包的路由,则只需要import _ "net/http/pprof"即可,会自动注册路由
  • 如果使用了第三方路由(例如gin),则需要手动注册路由
    • gin还可以直接使用github.com/DeanThompson/ginpprof
    • 只需要ginpprof.Wrap(router)即可
import _ "net/http/pprof"

// 如果不是使用默认的 http.DefaultServeMux而是自定义的MUX,则需要手动注册路由
r.HandleFunc("/debug/pprof/", pprof.Index)
r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
r.HandleFunc("/debug/pprof/profile", pprof.Profile)
r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
r.HandleFunc("/debug/pprof/trace", pprof.Trace)

benchmark pprof

  • 命令行
    • 直接运行 go test -bench <bench_func> -run none -cpuprofile <cpu.prof>
    • 除了-cpuprofile, 还可-memprofile
  • Goland

在这里插入图片描述

报告查看

# 命令行交互模式
go tool pprof http://<address:port>/debug/pprof/<profile>
# cpu profile默认采样30s,可以自行修改
go tool pprof http://<address:port>/debug/pprof/<profile>?seconds=120
# 在浏览器汇总交互
go tool pprof --http=:<port> http://<address:port>/debug/pprof/<profile> 
```
  • <profile>包括profile(CPU), heap(内存),goroutine,block,mutex

命令行

  • 总的思路就是通过topweb 找出关键函数,再通过list Func 查看函数代码,找到关键代码行并确认优化方案

  • top <num>查看占用最高的num项

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hMEpZJ6A-1582979745691)(C:\Users\35135\AppData\Roaming\Typora\typora-user-images\1582978820785.png)]

img

  • list <func name>查看匹配的具体函数CPU和内存等占用数据

    • 支持正则匹配
    • 如果函数名不明确,会进行模糊匹配
    • 使用list命令的前提是程序的源码在当前机器

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nCJzWFvK-1582979745701)(C:\Users\35135\AppData\Roaming\Typora\typora-user-images\1582979123139.png)]

  • traces打印所有调用栈,以及调用栈的指标信息

    • 每个- - - - - 隔开的是一个调用栈

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8HQhc6Bo-1582979788042)(C:\Users\35135\AppData\Roaming\Typora\typora-user-images\1582979362653.png)]

浏览器

  • 需要先安装 graphviz 工具
    在这里插入图片描述

指标

CPU

  • CPU 性能分析启用后,Go runtime 会每 10ms 就暂停一下,记录当前运行的 goroutine 的调用堆栈及相关数据

在这里插入图片描述

内存

  • 内存性能分析则是在一段时间内对在堆分配进行采样

    • 栈分配由于会随时释放,因此不会被内存分析所记录
    • 内存profiling是基于抽样和它跟踪的是已分配的内存,而不是使用中的内存
      • 比如有些内存已经分配,但实际以及不使用的内存,比如内存泄露的那部分
      • 所以不能使用内存profiling衡量程序总体的内存使用情况
        在这里插入图片描述
  • 有两种内存分析策略:

    • 一种是当前的(这一次采集)内存或对象的分配,称为 inuse
    • 另一种是从程序运行到现在所有的内存分配,不管是否已经被 gc 过了,称为 alloc
    • 加上 -sample_index 参数后,可以切换内存分析的类型
    • 例如 go tool pprof -sample_index=alloc_space http://<address:port>/debug/pprof/heap
-inuse_space      未释放空间数
-inuse_objects    未释放对象数
-alloc_space      累计分配空间数
-alloc_objects    累计分配对象数

goroutine

goroutine profile: total 32023
32015 @ 0x42e15a 0x42e20e 0x40534b 0x4050e5 0x6d8559 0x6d831b 0x45abe1
#    0x6d8558    main.alloc2.func1+0xf8    /home/ubuntu/heap/leak_demo.go:53
#    0x6d831a    main.alloc2+0x2a    /home/ubuntu/heap/leak_demo.go:54
  • goroutine?debug=1
    • goroutine profile: total 32023
      • 32023是goroutine的总数量
    • 32015 @ 0x42e15a 0x42e20e 0x40534b 0x4050e5 ...
      • 32015代表当前有32015个goroutine运行这个调用栈,并且停在相同位置
    • # 0x6d8558 main.alloc2.func1+0xf8 /home/ubuntu/heap/leak_demo.go:53
      • goroutine的调用栈,列出了函数和所在文件的行数
goroutine 20 [chan send, 2 minutes]:
main.alloc2.func1(0xc42015e060)
    /home/ubuntu/heap/leak_demo.go:53 +0xf9 
main.alloc2(0xc42015e060)
    /home/ubuntu/heap/leak_demo.go:54 +0x2b
created by main.alloc1
    /home/ubuntu/heap/leak_demo.go:42 +0x3f
  • goroutine?debug=2
    • 和第1种方式是互补的,可以看到每个goroutine的信息
    • goroutine 20 [chan send, 2 minutes]
      • 20是goroutine的id,
      • []中是当前goroutine的状态: 阻塞在写channel,并且阻塞了2分钟

火焰图

  • y轴表示调用方法的先后
  • x轴表示在每个采样调用时间内,方法所占的时间/空间百分比
    • 越宽代表占据cpu时间/mem大小越多

参考

trace

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章