Go很适宜用来开发高性能网络应用,但依然须要借助无效的工具进行性能剖析,优化代码逻辑。本文介绍了如何通过go test benchmark和pprof进行性能剖析,从而实现最优的代码效力。原文: Profiling Go Applications in the Right Way with Examples
什么是性能剖析?
性能剖析(Profiling) 是剖析应用程序从而辨认妨碍利用性能的瓶颈的根本技术,有助于检测代码的哪些局部执行工夫太长或耗费太多资源(如CPU和内存)。
分析方法
有三种分析方法。
- Go test(蕴含基准测试)
- 基于runtime/pprof的运行时剖析
- 基于net/http/pprof的Web剖析
剖析类型
- CPU (收集应用程序CPU应用状况的数据)
- 堆(Heap)/内存(Memory) (收集应用程序内存应用状况的数据)
- Goroutine (辨认创立最多Goroutine的函数)
- 阻塞 (辨认阻塞最多的函数)
- 线程 (辨认创立线程最多的函数)
- 互斥锁 (辨认有最多锁竞争的函数)
本文将次要关注应用上述办法进行CPU和内存剖析。
1. 基准测试(Benchmarking)
我想实现驰名的两数之和算法,这里不关注实现细节,间接运行:
go test -bench=.
-bench参数运行我的项目中的所有基准测试。
依据下面的输入,与其余办法相比,TwoSumWithBruteForce
是最无效的办法。别忘了后果取决于函数输出,如果输出一个大数组,会失去不同的后果。
如果输出go help testflag
,将看到许多参数及其解释,比方count
、benchtime
等,前面将解释最罕用的参数。
- 如果要运行特定函数,能够通过如下形式指定:
go test -bench='BenchmarkTwoSumWithBruteForce'
- 默认状况下,基准测试函数只运行一次。如果要自定义,能够应用
count
参数。例如,
go test -bench='.' -count=2
输入如下所示。
- 默认状况下,Go决定每个基准测试操作的运行工夫,能够通过自定义
benchtime='2s'
指定。
能够同时应用count
和benchtime
参数,以便更好的度量基准函数。请参考How to write benchmarks in Go。
示例代码请参考Github。
在事实世界中,函数可能既简单又长,计时毫无作用,因而须要提取CPU和内存剖析文件以进行进一步剖析。能够输出
go test -bench='.' -cpuprofile='cpu.prof' -memprofile='mem.prof'
而后通过pprof工具对其进行剖析。
1.1 CPU剖析
如果输出
go tool pprof cpu.prof
并回车,就会看到pprof交互式控制台。
咱们来看看最次要的内容。
- 输出
top15
查看执行期间排名前15的资源密集型函数。 (15示意显示的节点数。)
为了解释分明,假如有一个A
函数。
func A() { B() // 耗时1s DO STH DIRECTLY // 耗时4s C() // 耗时6s}
flat值和cum值计算为: flat值为A=4, cum值为A=11(1s + 4s + 6s) 。
- 如果要基于cum进行排序,能够键入
top15 -cum
。也能够别离应用sort=cum
和top15
命令。 - 如果通过
top
取得更具体的输入,能够指定granularity
选项。例如,如果设置granularity=lines
,将显示函数的行。
得益于此,咱们能够辨认导致性能问题的函数的特定行。
- 输入还显示了运行时函数和用户自定义函数。如果只想关注本人的函数,能够设置
hide=runtime
并再次执行top15
。
能够通过输出hide=
来重置。
- 此外,能够应用
show
命令。例如,输出show=TwoSum
- 如果只关注指定节点,能够应用
focus
命令。例如关注TwoSumOnePassHashTable
,显示为
能够输出focus=
来重置。
- 如果须要获取该性能的详细信息,能够应用
list
命令。例如,想取得对于TwoSumWithTwoPassHashTable
函数的详细信息,输出list TwoSumWithTwoPassHashTable
- 如果要查看图形化的调用栈,能够键入
web
。
前面将提供更多对于剖析图表的细节。
- 还能够键入
gif
或pdf
以与别人共享相应格局的剖析数据。
1.2 内存剖析
如果输出go tool pprof mem.prof
并回车
留神,下面提到的flat和cum是雷同的货色,只是测量不同的货色(CPU单位ms,内存单位MB)。
- list命令
- web命令
能够应用CPU剖析局部中提到的所有命令。
上面看一下另一个办法,runtime/pprof。
2. 基于runtime/pprof的运行时剖析
基准测试对单个函数的性能很有用,但不足以了解整体状况,这时就须要用到runtime/pprof。
2.1 CPU剖析
基准测试内置CPU和内存剖析,但如果须要让应用程序反对运行时CPU剖析,必须首先显示启用。
如果执行go run .
,将看到生成的cpu.prof
文件,能够通过基准测试局部提到的go tool pprof cpu.prof
对齐进行剖析。
本节将介绍我最喜爱的个性之一pprof.Labels
。此个性仅实用于CPU和goroutine剖析。
如果要向特定函数增加一个或多个标签,能够应用pprof.Do
函数。
pprof.Do(ctx, pprof.Labels("label-key", "label-value"), func(ctx context.Context) { // 执行标签代码})
例如,
在pprof交互式控制台中,键入tags
,将显示带了有用信息的标记函数。
能够用标签做很多事件,浏览Profiler labels in Go能够取得更多信息。
pprof还有很棒的web界面,容许咱们应用各种可视化形式剖析数据。
输出go tool pprof -http=:6060 cpu.prof
,localhost:6060
将被关上。 (为了更分明,我去掉了pprof.Labels)
让咱们深入探讨图形示意。
节点色彩、字体大小、边缘粗细等都有不同含意,参考pprof: Interpreting the Callgraph获取更多细节。可视化使咱们可能更容易辨认和修复性能问题。
单击图中的节点,能够对其进行细化,咱们能够依据本人的抉择对可视化进行过滤。上面展现了局部内容(focus、hide等)。
还能够看到其余可视化选项。
下面呈现了peek和source(作为list命令),因而上面将介绍火焰图(Flame Graph)")。火焰图提供了代码工夫破费的高级视图。
每个函数都用一个黑白矩形示意,矩形的宽度与该函数破费的工夫成正比。
能够拜访Github获取源码。
2.2 内存剖析
如果须要向应用程序增加运行时内存剖析,必须显式启用。
能够拜访Github获取源码。
如果执行go run .
,将看到生成的mem.prof
文件,能够用之前基准测试局部提到的go tool pprof mem.prof
对齐进行剖析。
上面将介绍两个更有用的命令tree
和peek
。
tree
显示了执行流的所有调用者和被调用者。
从而帮忙咱们辨认执行流并找出耗费最多内存的对象。 (不要遗记应用granularity=lines
,它提供了更可读的格局。)
- 如果心愿查看特定函数的执行流程,能够应用
peek
命令。例如,peek expensiveFunc
显示如下
- 还能够应用pprof web界面进行内存剖析。输出
go tool pprof -http=:6060 mem.prof
,关上localhost:6060
。
3. 基于net/http/pprof的Web剖析
runtime/pprof包提供了Go程序性能剖析的低级接口,而net/http/pprof为剖析提供了更高级的接口,容许咱们通过HTTP收集程序剖析信息,所须要做的就是:
输出localhost:5555/debug/pprof
,就能在浏览器上看到所有可用的剖析文件。如果没有应用stdlib,能够查看fiber、gin或echo的pprof实现。
文档里记录了所有用法和参数,咱们看一下最罕用的。
获取CPU剖析数据及技巧
go tool pprof http://localhost:5555/debug/pprof/profile?seconds=30
在CPU剖析期间,请留神
runtime.mallogc
→ 示意能够优化小堆调配的数量。
syscall.Read
或者syscall.Write
→ 示意应用程序在内核模式下破费了大量工夫,为此能够尝试I/O缓冲。
获取堆(采样沉闷对象内存调配)剖析数据及技巧
go tool pprof http://localhost:5555/debug/pprof/heapgo tool pprof http://localhost:5555/debug/pprof/heap?gc=1
就我集体而言,我喜爱用GC参数诊断问题。例如,如果应用程序有内存透露问题,能够执行以下操作:
- 触发GC(浏览器拜访/debug/pprof/heap?gc=1)
- 下载堆数据,假如下载文件名为file1
- 期待几秒或几分钟
- 再次触发GC(浏览器拜访/debug/pprof/heap?gc=1)
- 再次下载堆数据,假如下载文件名为file2
- 应用diff_base进行比拟
go tool pprof -http=:6060 -diff_base file2 file1
获取内存调配(抽样过来所有的内存调配)剖析数据及技巧
go tool pprof http://localhost:5555/debug/pprof/allocs
在内存调配剖析期间,能够这样做
- 如果看到
bytes.growSlice
,应该思考应用sync.Pool
。 - 如果看到自定义函数,请查看是否在切片或映射中定义了固定容量。
延长浏览
- pprof Github Readme
- Profiling Go Programs by Russ Cox
- pprof man page
- GopherCon 2019: Dave Cheney — Two Go Programs, Three Different Profiling Techniques
- GopherCon 2021: Felix Geisendörfer — Go Profiling and Observability from Scratch
- GopherConAU 2019 — Alexander Else — Profiling a go service in production
- Practical Go Lessons Profiling Chapter
你好,我是俞凡,在Motorola做过研发,当初在Mavenir做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI等技术始终保持着浓重的趣味,平时喜爱浏览、思考,置信继续学习、一生成长,欢送一起交流学习。为了不便大家当前能第一工夫看到文章,请敌人们关注公众号"DeepNoMind",并设个星标吧,如果能一键三连(转发、点赞、在看),则能给我带来更多的反对和能源,激励我继续写下去,和大家独特成长提高!
本文由mdnice多平台公布