Go 程序的性能监控与分析 pprof

摘要:
您是否考虑过go运行时系统如何调度您的goroutine?您是否试图理解为什么在程序中添加了并发性,但没有带来更好的性能?go执行跟踪器可以帮助回答这些问题,以及其他与性能相关的问题,例如延迟、竞争和低并行性。

你有没有考虑过,你的goroutines是如何被go的runtime系统调度的?是否尝试理解过为什么在程序中增加了并发,但并没有给它带来更好的性能?go执行跟踪程序可以帮助回答这些疑问,还有其他和其有关性能的问题,例如延迟、竞争和较低的并行效率。

该工具是Go 1.5版本加入的,通过度量go语言特定事件的运行时,例如:

  • 创建,启动和终止goroutines

  • 阻塞/非阻塞goroutines(syscalls, channels, locks)

  • 网络 I/O

  • Syscalls

  • 垃圾回收

Go 开箱就提供了一系列的性能监控 API 以及用于分析的工具, 可以快捷而有效地观察应用各个细节的 CPU 与内存使用概况, 包括生成一些可视化的数据(需要额外安装 Graphviz).

例子 gist 来自之前的 Trie 的实现, Ruby vs Go.

main 函数加上了下面几行:

1
2
3
4
5
6
import "runtime/pprof"
// ...
cpuProfile, _ := os.Create("cpu_profile")
pprof.StartCPUProfile(cpuProfile)
defer pprof.StopCPUProfile()
// ...

这里 os.Create("cpu_profile") 指定生成的数据文件, 然后 pprof.StartCPUProfile 看名字就知道是开始对 CPU 的使用进行监控. 有开始就有结束, 一般直接跟着 defer pprof.StopCPUProfile() 省的后面忘了. 编译执行一次以后会在目录下生成监控数据并记录到 cpu_profile. 接着就可以使用 pprof 来解读分析这些监控生成的数据.

When CPU profiling is enabled, the Go program stops about 100 times per second and records a sample consisting of the program counters on the currently executing goroutine’s stack.

CPU Profiling
1
2
3
4
5
6
$ go tool pprof cpu_profile
Type: cpu
Time: Jan 22, 2019 at 3:02pm (CST)
Duration: 518.52ms, Total samples = 570ms (109.93%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)

因为是在多核环境, 所以, 取样时间(Total samples) 占比大于 100% 也属于正常的. 交互操作模式提供了一大票的命令, 执行一下 help 就有相应的文档了. 比如输出报告到各种格式(pdf, png, gif), 方块越大个表示消耗越大.

Go 程序的性能监控与分析 pprof第1张

又或者列出 CPU 占比最高的一些(默认十个)运行结点的 top 命令, 也可以加上需要的结点数比如 top15

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
(pprof) top
Showing nodes accounting for 480ms, 84.21% of 570ms total
Showing top 10 nodes out of 67
    flat  flat%   sum%        cum   cum%
    200ms 35.09% 35.09%      210ms 36.84%  main.NewNode (inline)
    70ms 12.28% 47.37%      170ms 29.82%  runtime.scanobject
    60ms 10.53% 57.89%       70ms 12.28%  runtime.greyobject
    30ms  5.26% 63.16%       30ms  5.26%  runtime.memclrNoHeapPointers
    30ms  5.26% 68.42%       30ms  5.26%  runtime.memmove
    20ms  3.51% 71.93%      250ms 43.86%  main.(*Node).insert
    20ms  3.51% 75.44%       20ms  3.51%  runtime.findObject
    20ms  3.51% 78.95%      230ms 40.35%  runtime.gcDrain
    20ms  3.51% 82.46%       20ms  3.51%  runtime.pthread_cond_wait
    10ms  1.75% 84.21%       10ms  1.75%  runtime.(*gcWork).tryGetFast (inline)
  • flat: 是指该函数执行耗时, 程序总耗时 570ms, main.NewNode 的 200ms 占了 35.09%
  • sum: 当前函数与排在它上面的其他函数的 flat 占比总和, 比如 35.09% + 12.28% = 47.37%
  • cum: 是指该函数加上在该函数调用之前累计的总耗时, 这个看图片格式的话会更清晰一些.

可以看到, 这里最耗 CPU 时间的是 main.NewNode 这个操作.

除此外还有 list 命令可以根据匹配的参数列出指定的函数相关数据, 比如:

Go 程序的性能监控与分析 pprof第2张

Memory Profiling
1
2
3
// ...
memProfile, _ := os.Create("mem_profile")
pprof.WriteHeapProfile(memProfile)

类似 CPU 的监控, 要监控内存的分配回收使用情况, 只要调用 pprof.WriteHeapProfile(memProfile)

Go 程序的性能监控与分析 pprof第3张

然后是跟上面一样的生成图片:

Go 程序的性能监控与分析 pprof第4张

Type: inuse_space 是监控内存的默认选项, 还可以选 -alloc_space, -inuse_objects, -alloc_objects

inuse_space 是正在使用的内存大小, alloc_space是从头到尾一共分配了的内存大小(包括已经回收了的), 后缀为 _objects 的是相应的对象数

net/http/pprof

对于 http 服务的监控有一些些的不同, 不过 Go 已经对 pprof 做了一些封装在 net/http/pprof

例子 gist 来自从 net/http 入门到 Gin 源码梳理

引入多一行 _ "net/http/pprof", 启用服务以后就可以在路径 /debug/pprof/ 看到相应的监控数据. 类似下面(已经很贴心的把各自的描述信息写在下边了):

用 wrk (brew install wrk) 模拟测试

wrk -c 200 -t 4 -d 3m http://localhost:8080/hello

Go 程序的性能监控与分析 pprof第5张

还是没有前面的那些可视化图形 UI 直观, 不过可以通过 http://localhost:8080/debug/pprof/profile (其他几个指标也差不多, heap, alloc…)生成一个类似前面的 CPU profile 文件监控 30s 内的数据. 然后就可以用 go tool pprof来解读了.

 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
33
34
35
36
37
38
39
40
Type: cpu
Time: Jan 22, 2019 at 4:22pm (CST)
Duration: 30.13s, Total samples = 1.62mins (321.66%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 93.78s, 96.76% of 96.92s total
Dropped 270 nodes (cum <= 0.48s)
Showing top 10 nodes out of 52
      flat  flat%   sum%        cum   cum%
    81.42s 84.01% 84.01%     81.45s 84.04%  syscall.Syscall
     3.45s  3.56% 87.57%      3.45s  3.56%  runtime.kevent
     2.31s  2.38% 89.95%      2.31s  2.38%  runtime.pthread_cond_wait
     2.06s  2.13% 92.08%      2.07s  2.14%  runtime.usleep
     1.93s  1.99% 94.07%      1.93s  1.99%  runtime.pthread_cond_signal
     1.10s  1.13% 95.20%      1.10s  1.13%  runtime.freedefer
     0.85s  0.88% 96.08%      0.87s   0.9%  runtime.nanotime
     0.59s  0.61% 96.69%      4.07s  4.20%  runtime.netpoll
     0.04s 0.041% 96.73%      0.67s  0.69%  runtime.newproc1
     0.03s 0.031% 96.76%     44.18s 45.58%  net/http.(*conn).readRequest
(pprof)

Type: alloc_space
Time: Jan 22, 2019 at 4:26pm (

免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇MacOS command+tab 无法打开最小化应用的烦恼springboot SFTP 文件上传下载功能下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

查看进程命令

ps命令 [root@i-ka ~]#ps -l F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 4 S 0 17236 17233 0 80 0 - 28946 wait pts/0 00:00:00 bash 0 R 0 17...

内存, 硬盘, CPU是拿什么材料制作的? 电子管, 晶体管与计算机硬件的发展史.

0.引子 从<<深入理解计算机系统>>一书上看到: CPU内寄存器读取速度比内存速度快100倍, 硬盘读取时间开销是内存的1000万倍. 从寄存器(也就是L0)->L1高速缓存->L2高速缓存->内存->硬盘->分布式硬盘, 造价逐渐低, 速度逐渐减慢, 容量逐渐增大; 反之则造价逐渐昂贵, 速度逐渐提...

ubuntu cpu频率控制

安装cpufrequtils:  sudo apt-get install cpufrequtils 查看cpu:                 sudo cpufreq-info  设置cpu模式:          cpufreq-set -g {powersave, userspace, ondemand, conservative, perfor...

Js计算时间差

var runTime = (t1.getTime() - t2.getTime())/1000; var year = Math.floor(runTime / 86400 / 365);...

Java中Error和Exception的异同以及运行时异常(Runtime exception)与检查型异常(checked exception)的区别

一:Error和Exception的基本概念: 首先Exception和Error都是继承于Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。 Exception 和 Error 体现了 Java 平台设计者对不同异常情况的分类,Exception和...

ucosiii任务管理相关知识

只要学习和理解:任务状态 任务堆栈 任务控制块 任务就绪表 任务调度和切换 关于任务状态: 任务堆栈是任务的重要部分,堆栈是在RAM中按照“先进先出(FIFO)”的原则组织的一块连续的内存空间。为了满足任务切换和相应中断时保存CPU寄存器中的内容及任务调用其他函数时的需要,每个任务都应该有自己的堆栈。 1、任务堆栈的创建: UCOSIII中任务堆栈的创建...