go 性能分析

  1. 使用默认 pprof 工具 : go tool pprof cpu.prof
  2. 图形化界面依赖于graphviz
  3. 常用命令:
    • top: 列出最高调用
    • list: 列出问题代码片段
    • peek: 查询具体函数的调用关系
    • web: 图形化界面
1
go tool pprof mem.out

benchmark 测试

运行选项:

  • bench: 输入正则表达式,匹配才会执行
  • benchmem: 输出内存分配
  • benchtime: 运行时间,默认1秒,可以设置次数,比如 500x
  • -count: 轮速, 执行多次轮
  • 生成profile 文件,用于 pprof 分析
  • -cpuprofile=cpu.count
  • -memprofile=mem.out
  • -blockfile=block.out

运行结果: 运行次数,单词运行耗时,总耗时

google pprof 工具安装

web 界面展示

  1. 使用 pprof工具分析: go get -u github.com/google/pprof
  2. 安装 graphviz
  3. web界面: pprof -http=:8080 cpu.prof

web 界面查看 最红最粗的就是 关键的性能瓶颈

图 1

使用反射会导致内存分配, 性能优化尽量减少反射的使用

如何查看火焰图

图 2

白色的是代码运行本身占用,有颜色红色,黄色等就是性能瓶颈(性能开销)

内存和cpu 分析 – view trace

  • timeline: 时间线,表达执行时机
  • Heap: 内存占用,分析 goroutine哪个消耗比较大,可以用来辅助分析内存逃逸
  • Goroutines: 执行中的goroutine 和可以执行的goroutine
  • Threads: 系统线程
  • PROCS: go概念中的 processor

采样的概念

名字 含义
InSyscall 处于系统调用
running 运行状态

GO 逃逸分析

在go里面,对象可以被分配在栈或者堆上。 分配在堆上的,被称为内存逃逸

可能的原因 ( 不是必然逃逸,而是可能逃逸。 是否逃逸和 执行上下文有关)

  1. 指针逃逸: 方法返回局部变量指针
  2. interface{} 逃逸: 如使用 interface{} 作为参数或者返回值
  3. 接口逃逸: 如以接口作为返回值
  4. 大对象: 大对象会直接分配到堆上
  5. 栈空间不足
  6. 闭包: 闭包内部引用了外部变量
  7. channel 传递指针

分析内存逃逸 gcflags=-m

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// gcflags=-m 用来分析内存逃逸
//case1: 指针逃逸
func returnpointer() *User { // 用结构体可以防止逃逸
    return &User{} //&User{...} escape to heap 
}
//case2: 输入 interface{} 会 让 str逃逸,命中第二个规则
func interface_escape() {
    str := "escaped !"
    // str escapes to heap , --> 原因 println 参数是 []interface{} {...}
    fmt.Println(str)
}

//case3: 接口逃逸
/*
//直接返回具体类型不会逃逸
func returnUser_interface() RepoImpl {
    return RepoImpl{}
}
*/
// 越是面向接口编程,会导致内存逃逸越严重,设计原则和性能在这里 就很冲突
func returnUser_interface() Repo {
    var u RepoImpl  // RepoImpl 实现接口 Repo ,会导致逃逸
    return u
}
//case6: 闭包引用外部变量
func case6() func() int {
    i := 0
    return func() int {
        i++
        return i  // i moved to heap , func liternal escapes to heap
    }
}