原文地址:https://blog.pickme.lk/how-to-get-profiling-right-with-go-813ff89d4757
Go 是谷歌开发的一种编程语言,它使我们能够在性能、控制和开发人员体验之间做出正确的权衡。它本机利用多核 CPU 平台及其丰富的并发编程模型,在现代云基础架构中运行应用程序时提供更大的灵活性和性能。Go 是我们在 PickMe Engineering 中使用的主要编程语言,它为 100 多个微服务提供支持,这些微服务每天处理数百万条 Kafka 消息/HTTP 请求。因此,我们在开发过程中遵循性能驱动的方法,以便充分利用 Go。
应用程序分析的理由
在不了解其运行时行为的情况下优化应用程序可能会导致不必要且可能无效的优化。相反,性能优化应该由特定于应用程序的运行时指标驱动。分析是收集这些见解并确保优化具有针对性和有效性的有用工具。
什么是profile
profile:使用chatgpt翻译为性能剖析。在软件开发中,应用程序profile是一组数据样本,可以深入了解应用程序在运行时的性能。通过分析这些数据,开发人员能够更好地了解他们的应用程序在运行时的行为方式,并确定需要改进的潜在领域。例如:如何使用 CPU 时间。程序在特定时间段内分配了多少内存。Go 编程语言有一个高度成熟的标准库,它提供了用于收集运行时指标和生成应用程序配置文件的工具。目前,支持以下配置文件类型。
-
heap(堆):活动对象的内存分配样本。您可以指定 gc GET 参数以在获取堆样本之前运行 GC。 -
profile:CPU 的采样。它是一种应用程序profile,可捕获程序在每个跟踪点花费的 CPU 时间量。启用 CPU 分析后,运行时将每 10 毫秒自行中断一次,并记录当前正在运行的 go-routines 的堆栈跟踪。 -
block:导致同步原语阻塞的堆栈跟踪; -
allocs:所有过去内存分配的样本; -
cmdline:当前程序的命令行调用; -
goroutine:所有当前go-routines的堆栈跟踪; -
mutex(互斥量):竞争互斥量持有者的堆栈痕迹; -
threadcreate:导致创建新操作系统线程的堆栈跟踪;
在本文中,我们将探讨 Go 中提供的用于生成和分析应用程序配置文件的工具支持。首先,第一步是在运行时启用分析。
如何创建profile?
有多种创建应用程序配置文件的方法 01.使用go test命令 02.手动使用runtime/pprof包 03. 使用 HTTP 处理程序
使用go test命令
Go 测试实用程序内置了对分析的支持。我们可以只传递一些命令行参数来生成 CPU/内存/阻塞的profile文件。查看 go test 命令的以下变体。
手动使用runtime/pprof包
Go 标准库包含一个包(runtime/pprof
),可用于生成各种配置文件。采集到profile后,可以使用go tool pprof
对其进行解析分析。这是一个示例代码片段来解释实现。
使用 HTTP 处理程序
pprof 包提供了通过 HTTP 服务器导出profile的能力。这是检索应用程序调试信息的最简单方法,因为我们只需调用 HTTP 端点来下载配置文件。这在生产环境中创造了更大的灵活性。上面的程序将在端口 6060 上打开一个 HTTP 服务器,我们可以用它来生成/下载应用程序profile文件。您可以通过导航到以下链接查看支持的profile类型列表。http://localhost:6060/debug/pprof
示例程序
让我们编写一个小程序来生成一些配置文件。该程序将打印给定数字的斐波那契和,直到它退出。该程序使用
pprof
HTTP 处理程序打开具有可用配置文件的 Web 服务器。通过导航到 http://localhost:6060/debug/pprof 链接可以看到以下输出。我们可以通过 HTTP 下载上述任何profile文件。
下一步是可视化和解释下载的profile文件,以便我们可以观察应用程序在运行时的行为。
可视化分析数据的方法有哪些?
我们已经讨论过 go tool pprof
为分析配置文件提供支持。它支持文本和图形格式等可视化选项,让我们更好地解读数据。以下接口可用于与该工具进行交互。
-
pprof
CLI -
pprof
网络界面(使用 pprof 工具的最简单方法之一。网络界面提供了一种在单个位置访问所有工具/功能的便捷方式)
在探索 Web 界面之前,让我们先看一下 pprof 的命令行界面。
使用 pprof CLI 解释profile文件
我们可以将配置文件传递给 go tool pprof 命令。所以这个工具会让我们进入它的交互式命令行。该命令将为您提供初始输出,其中包含有关收集的样本数量和分析持续时间的一些详细信息。我们运行分析器 30.19 秒,配置文件包含 29.89 秒的采样数据(总分析时间的 99.00%)。以下是我们可以在交互模式下使用的最有用的命令。
01. top
此命令列出终端中最昂贵的堆栈跟踪。我们可以在以下输出中看到,main.FIb()
是最昂贵的函数,它占用了几乎所有的 CPU 时间,因为我们递归调用该函数以获得最终的斐波那契和。我们可以在输出中看到 3 列,分别是 flat、cum 和 sum%;Flat and Cumulative?Flat 表示函数本身使用的资源量。Cumulative是函数中使用的资源总量和调用堆栈中的调用函数。
在上面的例子中,函数 A 的平面值是 5 秒。累计值为 9 秒。sum% 反映了前几行中使用了多少内存/CPU。基本上是flat%的总和。查看以下“pprof”输出以了解 sum% 的计算
02. list
list 命令显示与给定 reg-ex 模式匹配的函数的注释源。我们可以将正则表达式模式传递给 list 命令,这样它将在终端中打印带注释的源代码以匹配堆栈跟踪。举例:
list main.Fib
它显示了自从我们使用 CPU 配置文件以来每行执行所花费的 CPU 时间量。如果我们使用内存profile文件,它将显示内存分配的大小/数量。
03. The web
Web 命令生成并打开调用图的 SVG 输出。上面的命令将在 web 视图中打开调用图
04. The weblist
weblist
命令在 Web 窗口中显示带注释的源代码。与 list 命令一样,它显示了在每一行代码中收集的样本数量。weblist
命令将打开带有反汇编源代码的 Web 视图。pprof
CLI 工具中提供了许多其他选项。我们可以输入帮助命令并获取可用选项列表。
★
但阅读配置文件的最简单方法是使用
pprof
网络界面。”
使用 pprof 网络界面解释profile文件
正如我们之前所讨论的,Web 界面是阅读配置文件最方便的方式。我们可以将 http host:port 传递给 pprof
命令以获取 web 视图。最初,它提供了一个类似于下图的调用图。我们可以使用它来浏览应用程序调用堆栈并识别我们可以用红色看到的热执行路径。
让我们更深入地讨论调用图以及如何阅读和理解它们。
解释调用图
图中的每个框代表一个函数,框的大小根据在该特定跟踪点上收集的样本确定。从一个框到另一个框的边表示函数调用。我们可以根据以下特征进一步理解调用图。
01.节点颜色
-
大的正值是红色的。 -
大的负值是绿色的。 -
接近零的累积值是灰色的。

02.节点字体大小
-
较大的字体大小意味着较大的绝对平面值。 -
较小的字体大小意味着较小的绝对平面值。

03. Arrow Weight
-
较粗的箭头表示沿该路径使用了更多资源。 -
较细的箭头表示沿该路径使用的资源较少。

04.箭头颜色
-
大的正值是红色的。 -
大的负值是绿色的。 -
接近零的值是灰色的。
05.虚线箭头
为了提高图表的清晰度,删除了两个连接位置之间的一些位置。
06.实心箭头
一个位置直接调用另一个位置。
07.“(内联)”边缘标记
调用已内联到调用者中。(内联是我们在 Go 中拥有的一种编译器优化技术)
火焰图?
火焰图是我们在 go pprof
网络界面中拥有的最重要的可视化之一。火焰图突出显示了据报告在样本收集期间执行的堆栈跟踪。它从一个名为 root 的节点开始,并根据堆栈中的函数调用进一步分解。火焰图功能仅在 pprof
网络界面中可用。我们可以通过点击 view->Flame Graph 菜单项来生成火焰图。以下是解释我们的一个生产应用程序的堆栈跟踪的火焰图。
如何阅读火焰图?
火焰图中的每个水平段都显示一个堆栈框架。段的宽度表示函数的累积值。它进一步分解为子栈帧,这些子栈帧是更高层栈帧调用的函数的栈帧。(从上到下的调用)Y 轴显示堆栈深度,颜色不重要,因为它是随机选取的以区分帧。看看我们在 Fibonacci 系列应用程序上创建的以下火焰图。当我们递归调用 Fib() 函数时,它显示了 40 个链接在一起的堆栈帧。
不同profile的比较
如果您在没有使用太多内存的应用程序中遇到高 CPU 使用率。那么您绝对应该检查 CPU 配置文件并解决这些瓶颈。如果我们在应用程序中看到高内存使用率而没有极端的 CPU 使用率,那么我们必须检查内存配置文件并解决不必要的内存分配。我们可以在这里使用一些工具/技术。例如:逃逸分析 在某些情况下,应用程序的 CPU/内存利用率很低,但性能却达不到标准。在那些场景中,我们必须检查阻塞profile,以便我们可以识别同步原语(互斥锁、通道)的阻塞时间。在确保应用程序中没有 CPU、内存问题之前,我们不应该检查profile。否则,我们最终会得到一些真正不必要的过早优化,并给代码带来更多的复杂性。
性能优化过程
-
开启go profiler -
对应用程序施加一些负载并收集配置文件 -
定位CPU/内存/阻塞时间热点 -
分析热点位置的源码 -
如果优化很明显,跳到第8步 -
将热点代码隔离成专用函数 -
为隔离函数编写基准 -
优化代码 -
运行基准测试并收集/分析配置文件(重复运行第 8 步和第 9 步,直到获得满意的结果) -
再次加载测试并与上次运行结果进行比较
以上是我们可以在性能调整中遵循的一个小程序。我们可以通过上述步骤来识别和解决性能问题。在生产环境中启用应用程序矩阵和分析也非常重要,这样我们就可以随时监控它。
原文始发于微信公众号(小唐云原生):【翻译】GO如何进行性能分析
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/270893.html