性能调优-PProf

Go学习笔记

PProf

pprof 是用于可视化和分析性能分析数据的工具,以 profile.proto 读取分析样本的集合,并生成报告以可视化并帮助分析数据(支持文本和图形报告)

profile.proto 是一个 Protocol Buffer v3 的描述文件,它描述了一组 callstack 和 symbolization 信息, 作用是表示统计分析的一组采样的调用栈,是很常见的 stacktrace 配置文件格式

涉及的工具包有:

  • runtime/pprof:采集程序(非 Server)的运行数据进行分析

  • net/http/pprof:采集 HTTP Server 的运行时数据进行分析(只是使用runtime/pprof包来进行封装,并在http端口上暴露出来)

    #web服务器:
    import (
        "net/http"
        _ "net/http/pprof"
    )
      
    #一般应用程序(实际应用无web交互)
    import (
        "net/http"
        _ "runtime/pprof"
    )
    

使用工具命令bench在源文件目录生成性能采样文件,然后通过pprof工具交互式分析采样文件

go test -bench . -cpuprofile cpu.pprof

## 运行如下会出现交互式命令窗口,可以通过输入help命令获取帮助
## 最简单的方式是输入web,会生成一张svg格式的树形结构图(需要安装Graphviz工具)
go tool pprof cpu.pprof
(pprof) web

采样范围

  • CPU Profiling:采集程序的 CPU 使用情况,按照一定的频率采集所监听的应用程序 CPU(含寄存器)的使用情况,可确定应用程序在主动消耗 CPU 周期时花费时间的位置
  • Memory Profiling:采集程序内存使用情况,在应用程序进行堆分配时记录堆栈跟踪,用于监视当前和历史内存使用情况,以及检查内存泄漏
  • Block Profiling:阻塞分析,记录 goroutine 阻塞等待同步(包括定时器通道)的位置,可以用来分析和查找死锁等性能瓶颈
  • Mutex Profiling:互斥锁分析,报告互斥锁的竞争情况

支持使用模式

  • Report generation:报告生成
  • Interactive terminal use:交互式终端使用
  • Web interface:Web 界面

分析

准备测试文件

// demo.go
import (
	"log"
	"net/http"
	_ "net/http/pprof"
)

func main() {
	go func() {
		for {
			log.Println(add("https://github.com"))
		}
	}()

	http.ListenAndServe("0.0.0.0:8080", nil)
}

// data/d.go
package data

var datas []string

func Add(str string) string {
    data := []byte(str)
    sData := string(data)
    datas = append(datas, sData)

    return sData
}
  • 通过web界面

    访问http://localhost:8080/debug/pprof/

    /debug/pprof/
      
    Types of profiles available:
    Count	Profile
    20	allocs
    0	block
    0	cmdline
    5	goroutine
    20	heap
    0	mutex
    0	profile
    13	threadcreate
    0	trace
    full goroutine stack dump
    

    这个路由中有许多子页面

    /debug/pprof/profile(CPU Profiling):访问这个链接会自动30s 进行CPU profiling,,并生成一个分析用的 profile 文件下载

    /debug/pprof/cmdline :获取程序启动时的命令及参数

    /debug/pprof/symbol: 根据传入的程序计数器(PC)的值,获取对应的函数的名称信息,调用了runtime包的FuncForPC获取对应的函数信息。可以传入多个PC值,以加号作为连接符号,比如访问:http://127.0.0.1:8080/debug/pprof/symbol?0x4e667d+0x6ec770, 返回PC值与函数对应名称的信息

    /debug/pprof/trace: 此接口用于获取程序执行中的事件跟踪信息,比如协程、系统调用、GC、堆大小改变等事件,大多数事件的跟踪精确度能达到纳秒级别,后端调用了runtime包的StartTrace,会进行一个STW的操作。获取数据的时长默认为1秒,可以通过seconds参数进行修改。先通过以下命令获取数据:wget http://127.0.0.1:8080/debug/pprof/trace?seconds=10 -O tracefile

    然后执行命令go tool trace tacefile进行数据分析

    /debug/pprof/block(Block Profiling):查看导致阻塞同步的堆栈跟踪,会生成一个block的Profiling结果文件

    /debug/pprof/goroutine:查看当前所有运行的 goroutine列表,以及堆栈调用关系,会生成一个goroutine的Profiling结果文件

    /debug/pprof/heap(Memory Profiling): 查看活动对象的内存分配情况,访问这个链接会得到一个内存 Profiling 结果文件

    /debug/pprof/mutex(Mutex Profiling):查看导致互斥锁的竞争持有者的堆栈跟踪

    /debug/pprof/threadcreate:查看创建新OS线程的堆栈跟踪

  • 通过交互式终端使用

    profile

    go tool pprof http://localhost:8080/debug/pprof/profile\?seconds\=60
    Fetching profile over HTTP from http://localhost:8080/debug/pprof/profile?seconds=60
    Saved profile in /Users/xue.zeng/pprof/pprof.samples.cpu.001.pb.gz
    Type: cpu
    Time: Sep 22, 2019 at 11:25am (CST)
    Duration: 1mins, Total samples = 21.74s (36.12%)
    Entering interactive mode (type "help" for commands, "o" for options)
    (pprof) top
    (pprof) top
    Showing nodes accounting for 20260ms, 93.19% of 21740ms total
    Dropped 72 nodes (cum <= 108.70ms)
    Showing top 10 nodes out of 44
          flat  flat%   sum%        cum   cum%
       14300ms 65.78% 65.78%    14930ms 68.68%  syscall.syscall
        1360ms  6.26% 72.03%     1370ms  6.30%  runtime.nanotime
        1280ms  5.89% 77.92%     1280ms  5.89%  runtime.usleep
         730ms  3.36% 81.28%     1220ms  5.61%  runtime.notetsleep
         630ms  2.90% 84.18%      630ms  2.90%  runtime.exitsyscallfast
         470ms  2.16% 86.34%      470ms  2.16%  runtime.pthread_cond_timedwait_relative_np
         470ms  2.16% 88.50%     1530ms  7.04%  runtime.scanobject
         390ms  1.79% 90.29%      390ms  1.79%  runtime.memmove
         320ms  1.47% 91.77%      320ms  1.47%  runtime.pageIndexOf
         310ms  1.43% 93.19%      370ms  1.70%  runtime.findObject
    

    执行该命令后,需等待 60 秒(可调整 seconds 的值),pprof 会进行 CPU Profiling。结束后将默认进入 pprof 的交互式命令模式,可以对分析的结果进行查看或导出。同时也会生成profile压缩gz文件(一般在/Users/[username]/pprof目录下)。具体可执行 pprof help 查看命令说明

    flat:给定函数上运行耗时

    flat%:同上的 CPU 运行耗时总比例

    sum%:给定函数累积使用 CPU 总比例

    cum:当前函数加上它之上的调用运行总耗时

    cum%:同上的 CPU 运行耗时总比例

    最后一列为函数名称,在大多数的情况下,可以通过这五列得出一个应用程序的运行情况,加以优化

    查看历史调试文件信息,通过指定的profile文件进入(pprof)即可

    go tool pprof /Users/xue.zeng/pprof/pprof.samples.cpu.001.pb.gz
    

    heap

    ➜  pprof git:(master) ✗ go tool pprof http://localhost:8080/debug/pprof/heap                
    Fetching profile over HTTP from http://localhost:8080/debug/pprof/heap
    Saved profile in /Users/xue.zeng/pprof/pprof.alloc_objects.alloc_space.inuse_objects.inuse_space.001.pb.gz
    Type: inuse_space
    Time: Sep 22, 2019 at 11:37am (CST)
    Entering interactive mode (type "help" for commands, "o" for options)
    (pprof) top
    Showing nodes accounting for 340.43MB, 100% of 340.43MB total
          flat  flat%   sum%        cum   cum%
      340.43MB   100%   100%   340.43MB   100%  main.add
             0     0%   100%   340.43MB   100%  main.main.func1
    

    -inuse_space:分析应用程序的常驻内存占用情况

    -alloc_objects:分析应用程序的内存临时分配情况

  • Pprof可视化界面

    编写测试

    package data
      
    import "testing"
      
    const url = "https://github.com"
    func TestAdd(t *testing.T) {
        s := Add(url)
        if s == "" {
            t.Errorf("Test.Add error!")
        }
    }
      
    func BenchmarkAdd(b *testing.B) {
        for i := 0; i < b.N; i++ {
            Add(url)
        }
    }
    

    执行测试用例

    go test -bench . -cpuprofile cpu.prof
    goos: darwin
    goarch: amd64
    pkg: training/pprof/data
    BenchmarkAdd-8           5000000               421 ns/op
    PASS
    ok      training/pprof/data     2.770s
    

    开启PProf 可视化界面

    # 方法一
    go tool pprof -http :8080 cpu.prof
    # 方法二
    go tool pprof cpu.prof
    Type: cpu
    Time: Sep 22, 2019 at 12:37pm (CST)
    Duration: 2.69s, Total samples = 2.94s (109.22%)
    Entering interactive mode (type "help" for commands, "o" for options)
    (pprof) web
    

    如果出现 Failed to execute dot. Is Graphviz installed? Error: exec: "dot": executable file not found in $PATH,就是提示需要安装 graphviz(brew install graphviz)

    Graph(框越大,线越粗代表它占用的时间越大)

    Graph

    Top

    Top

    Flame Graph

    Flame Graph

    最大优点是动态的。调用顺序由上到下(A -> B -> C -> D),每一块代表一个函数,越大代表占用 CPU 的时间更长,同时它也支持点击块深入进行分析

    Go 1.11开始,火焰图形可视化go tool pprof直接可用,如果没有使用Go 1.11及以上,可以获得最新的pprof工具并使用它

    # Get the pprof tool directly
    $ go get -u github.com/google/pprof
      
    $ pprof -http=":8081" [binary] [profile]
    

    还有包括Peek,Source等,可自行查看分析

Go 

See also