关于程序员:Go应用性能分析实战

61次阅读

共计 4512 个字符,预计需要花费 12 分钟才能阅读完成。

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,将看到许多参数及其解释,比方countbenchtime 等,前面将解释最罕用的参数。

  • 如果要运行特定函数,能够通过如下形式指定:
go test -bench='BenchmarkTwoSumWithBruteForce'
  • 默认状况下,基准测试函数只运行一次。如果要自定义,能够应用 count 参数。例如,
go test -bench='.' -count=2

输入如下所示。

  • 默认状况下,Go 决定每个基准测试操作的运行工夫,能够通过自定义 benchtime='2s' 指定。

能够同时应用 countbenchtime参数,以便更好的度量基准函数。请参考 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=cumtop15命令。
  • 如果通过 top 取得更具体的输入,能够指定 granularity 选项。例如,如果设置granularity=lines,将显示函数的行。

得益于此,咱们能够辨认导致性能问题的函数的特定行。😌

  • 输入还显示了运行时函数和用户自定义函数。如果只想关注本人的函数,能够设置 hide=runtime 并再次执行top15

能够通过输出 hide= 来重置。

  • 此外,能够应用 show 命令。例如,输出show=TwoSum
  • 如果只关注指定节点,能够应用 focus 命令。例如关注TwoSumOnePassHashTable,显示为

能够输出 focus= 来重置。

  • 如果须要获取该性能的详细信息,能够应用 list 命令。例如,想取得对于 TwoSumWithTwoPassHashTable 函数的详细信息,输出list TwoSumWithTwoPassHashTable
  • 如果要查看图形化的调用栈,能够键入web

前面将提供更多对于剖析图表的细节。

  • 还能够键入 gifpdf以与别人共享相应格局的剖析数据。😃
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.proflocalhost:6060 将被关上。(为了更分明,我去掉了 pprof.Labels)

让咱们深入探讨图形示意。

节点色彩、字体大小、边缘粗细等都有不同含意,参考 pprof: Interpreting the Callgraph 获取更多细节。可视化使咱们可能更容易辨认和修复性能问题。

单击图中的节点,能够对其进行细化,咱们能够依据本人的抉择对可视化进行过滤。上面展现了局部内容 (focushide 等)。

还能够看到其余可视化选项。

下面呈现了 peek 和 source(作为 list 命令),因而上面将介绍火焰图(Flame Graph)”)。火焰图提供了代码工夫破费的高级视图。

每个函数都用一个黑白矩形示意,矩形的宽度与该函数破费的工夫成正比。

能够拜访 Github 获取源码。

2.2 内存剖析

如果须要向应用程序增加运行时内存剖析,必须显式启用。

能够拜访 Github 获取源码。

如果执行 go run .,将看到生成的mem.prof 文件,能够用之前基准测试局部提到的 go tool pprof mem.prof 对齐进行剖析。

上面将介绍两个更有用的命令 treepeek

  • 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/heap

go 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
  • 如果看到自定义函数,请查看是否在切片或映射中定义了固定容量。

延长浏览

  1. pprof Github Readme
  2. Profiling Go Programs by Russ Cox
  3. pprof man page
  4. GopherCon 2019: Dave Cheney — Two Go Programs, Three Different Profiling Techniques
  5. GopherCon 2021: Felix Geisendörfer — Go Profiling and Observability from Scratch
  6. GopherConAU 2019 — Alexander Else — Profiling a go service in production
  7. Practical Go Lessons Profiling Chapter

你好,我是俞凡,在 Motorola 做过研发,当初在 Mavenir 做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI 等技术始终保持着浓重的趣味,平时喜爱浏览、思考,置信继续学习、一生成长,欢送一起交流学习。为了不便大家当前能第一工夫看到文章,请敌人们关注公众号 ”DeepNoMind”,并设个星标吧,如果能一键三连(转发、点赞、在看),则能给我带来更多的反对和能源,激励我继续写下去,和大家独特成长提高!

本文由 mdnice 多平台公布

正文完
 0