go pprof

[TOC]

roadmap

使用指南

步骤:

  1. 程序开启pprof,收集数据
  2. 拉取go程序profile数据(如果是http/pprof方式)
  3. 分析go程序性能

程序开启pprof的几种常见方式

  • 最常用的使用方式是http/pprof,参考http/pprof/
    ,大概步骤如下:
    1. import _ "net/http/pprof",原理:用http内置的DefaultServeMux,注册了http://host:port/debug/pprof/xxx路由
    2. http.ListenAndServe("localhost:6060", nil) 上个步骤提供的路由,开启端口提供访问
  • 结合go test 开启pprof,参考:runtime/pprof
    。命令:go test -cpuprofile cpu.prof -memprofile mem.prof -bench .
  • 程序内代码实现指定开启profile并输出,如下
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
var memprofile = flag.String("memprofile", "", "write memory profile to `file`")

func main() {
    flag.Parse()
    if *cpuprofile != "" {
        f, err := os.Create(*cpuprofile)
        if err != nil {
            log.Fatal("could not create CPU profile: ", err)
        }
        defer f.Close() // error handling omitted for example
        if err := pprof.StartCPUProfile(f); err != nil {
            log.Fatal("could not start CPU profile: ", err)
        }
        defer pprof.StopCPUProfile()
    }

    // ... rest of the program ...

    if *memprofile != "" {
        f, err := os.Create(*memprofile)
        if err != nil {
            log.Fatal("could not create memory profile: ", err)
        }
        defer f.Close() // error handling omitted for example
        runtime.GC() // get up-to-date statistics
        if err := pprof.WriteHeapProfile(f); err != nil {
            log.Fatal("could not write memory profile: ", err)
        }
    }
}

拉取go程序profile数据

参考:https://golang.org/pkg/net/http/pprof/
如果是使用go test与程序内输出指定的profile文件,则忽略此步骤。
如果是通过http/pprof方式开启的profile,则需要通过http方式拉取profile数据。
拉取命令执行后会阻塞采集数据,采集结束后会生成profile文件。
拉取命令:

  • 获取cpuprofile:go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
  • 获取memprofile:go tool pprof http://localhost:6060/debug/pprof/heap
  • 查看可获取的profile类型,浏览器打开:http://localhost:6060/debug/pprof

分析go程序性能

几种方式:

  • web分析(推荐方式):简单,直观,有🔥图
  • pprof 命令行交互工具分析

web 分析

执行命令:pprof -http=:8080 pprof.xxx.samples.cpu.001.pb.gz

调用图:

image.png

火焰图:

image.png

命令行交互工具分析

example如下:

pprof [binary] pprof.out

(pprof) top10
Total: 2525 samples
     flat  flat%   sum%        cum   cum%
     298  11.8%  11.8%      345  13.7% runtime.mapaccess1_fast64
     268  10.6%  22.4%     2124  84.1% main.FindLoops
     251   9.9%  32.4%      451  17.9% scanblock
     178   7.0%  39.4%      351  13.9% hash_insert
     131   5.2%  44.6%      158   6.3% sweepspan
     119   4.7%  49.3%      350  13.9% main.DFS
      96   3.8%  53.1%       98   3.9% flushptrbuf
      95   3.8%  56.9%       95   3.8% runtime.aeshash64
      95   3.8%  60.6%      101   4.0% runtime.settype_flush
      88   3.5%  64.1%      988  39.1% runtime.mallocgc
  • 采样策略:停止所有goroutine,采集数据。默认只收集100个frames层级的调用栈信息
  • 第一列:采集采样点时落在函数中的个数。
  • 第二列:比例= 第一列 / total samples. eg 298/2525 = 11.8%
  • 第三列:第二列的累积和,用于直观看出占比的百分比。eg: 11.8% + 10.6% = 22.4%
  • 第四列:收集的采样点中该函数出现的个数(出现的意思就是,可能刚好落在这个函数的执行部分,或者落在该函数内调用的子函数里)
    • The fourth and fifth columns show the number of samples in which the function appeared (either running or waiting for a called function to return)
    • Each location contains two values: flat is the value of the location itself, while cum is the value of the location plus all its descendants.
  • 第五列:第四列占总samples的纠比例,345/2525 = 13.7%
list & weblist
(pprof) list DFS
Total: 2525 samples
ROUTINE ====================== main.DFS in /home/rsc/g/benchgraffiti/havlak/havlak1.go
   119    697 Total samples (flat / cumulative)
     3      3  240: func DFS(currentNode *BasicBlock, nodes []*UnionFindNode, number map[*BasicBlock]int, last []int, current int) int {
     1      1  241:     nodes[current].Init(currentNode, current)
     1     37  242:     number[currentNode] = current
     .      .  243:
     1      1  244:     lastid := current
    89     89  245:     for _, target := range currentNode.OutEdges {
     9    152  246:             if number[target] == unvisited {
     7    354  247:                     lastid = DFS(target, nodes, number, last, lastid+1)
     .      .  248:             }
     .      .  249:     }
     7     59  250:     last[number[currentNode]] = lastid
     1      1  251:     return lastid
  • 第一列:采样点落在该行的个数
  • 第二列:采样点落在该行的个数+ 采样点落在被该行代码调用的函数中的个数(值越大,开销越大,一般只关注这列)。以247行为例:
    • 7:采样点落在该行9个
    • 354:7 + 347(采样点落在DF函数内247个)
  • 第三列:代码行数

相关命令

  • go tool pprof binary profile_file(cpu/memory/...)
  • topN
  • top5 -cum: -cum (for cumulative)
  • go tool pprof --inuse_objects 显示内存申请的次数
    • -inuse_space Display in-use memory size
    • -inuse_objects Display in-use object counts
    • -alloc_space Display allocated memory size
    • -alloc_objects Display allocated object counts
  • web [func]
  • list func 命令行查看每行代码的详细分析
  • weblist func web查看每行代码的详细分析
  • web 命令,查看svg
  • go tool pprof --nodefraction=0.1 havlak4 havlak4.prof, 过虑低的数据, 使用 --nodefraction, 查看超过10%
  • 生成pdf: go tool pprof -pdf bench.test cpu.out > cpu.pdf
  • 对比工具: benchcmp bench0 bench1. (install: go get golang.org/x/tools/cmd/benchcmp)

demo

与benchmark 的结合使用

  1. 写测试用例
  2. benchmark 测试,指定生成相关的profile。 eg:
  • go test -v -run="none" -bench=. -benchtime="30s" -cpuprofile=cpu.pprof -memprofile=mem.pprof, 会生成cpu.pprof, mem.pprof 与xxx.test
  1. 分析pprof文件: go tool pprof xxx.test(binary) mem.pprof(pprof source file)

http/pprof的使用

  1. import _ "net/http/pprof"
  2. http pprof 默认使用的是http.DefaultMux, 设置监听端口
go func() {
    log.Println(http.ListenAndServe("localhost:6060", nil))
}()
  1. 拉取pprof分析:
cpu: go tool pprof http://localhost:6060/debug/pprof/profile?second=60
heap: go tool pprof http://localhost:6060/debug/pprof/heap?second=60
...
  1. 使用相关命令分析pprof

pprof web ui

  1. 拉取profile:go tool pprof http://localhost:6060/debug/pprof/profile?second=60
  2. 启动pprof: pprof -http 0.0.0.0:7070 binaryfile xxx.pb.gz(profile_file)
  3. 访问web界面,查看相关指标:top, svg, graph, flame graph, list, disassemble

或者

  1. 拉取并启动服务:pprof -seconds 60 -http me:6060 http://ts1:32144/debug/pprof/profile

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351

推荐阅读更多精彩内容