9.3.4 性能剖析 Profiling

熟悉 Golang 的开发者对 pprof 工具一定不陌生。进行软件性能调试和分析时,利用 pprof 提供的 CPU 和内存分析功能,深入了解 Golang 函数的执行时间和内存使用情况。

在可观测性领域,性能剖析(Profiling)的作用与 Go 语言中的 pprof 类似,都是对运行中的程序进行动态分析,生成程序运行时的详细数据(即 Profiles),全面了解程序资源的使用情况,确定代码和性能瓶颈之间的关联

Profiles 数据一般表示成火焰图、堆栈图或内存分析图等形式,是从“是什么”到“为什么”这一过程中至关重要的依据。例如,通过链路追踪发现延迟产生的位置,然后依靠 Profiles 生成的火焰图进一步定位到具体的代码行。2021 年,国内某站崩溃,工程师们使用火焰图观察到到一处 Lua 代码存在异常,最终定位到问题的源头[1]


图 9-16 Lua 代码的 CPU 火焰图

火焰图分析说明

  • 火焰图的 y 轴代调用栈,每一层都是一个函数调用,栈越深火焰越高,调用关系从上而下,顶部就是正在执行的函数,下方都是它的父函数。
  • x 轴代表抽样数,一个函数在 x 轴占据的宽度越宽,则说明它被抽样的次数越多,也就是说它的执行时间越长。

火焰图颜色没有特殊含义,因为火焰图表示的是 CPU 的繁忙程度,所以一般选择暖色调。分析火焰图,其实就是看顶层的哪个函数占据的宽度最大。只要有“平顶”,就表示该函数可能存在性能问题。

Profiles 数据通常由多种不同的分析器(Profiler)生成,以下是一些常见的分析器类型:

  • CPU 分析器:用于分析哪些函数或方法在运行时消耗了最多的 CPU 时间。例如,通过 CPU Profiler,我们可以确定某个算法的优化是否减少了 CPU 使用率。
  • 堆分析器(Heap Profiler):用于监测程序的内存使用情况,帮助发现内存泄漏或不必要的内存分配。例如,在 Java 应用中,Heap Profiler 可以帮助找到导致内存溢出的具体对象或数据结构。
  • GPU 分析器:用于分析图形处理单元(GPU)的利用情况,常用于游戏开发或图形密集型应用。
  • 互斥锁分析器:用于检测互斥锁的竞争情况,帮助优化多线程程序的并发性能。
  • I/O 分析器:分析 I/O 操作的性能,如用来分析文件读写操作的延迟或网络请求的耗时,从而优化数据传输效率。
  • 特定编程语言的分析器:如 JVM Profiler,专门分析运行在 Java 虚拟机上的应用程序。

过去,由于分析器的资源消耗较大,工程师们往往仅在紧急情况下才会临时使用它们。随着低开销分析技术的发展,如编程语言层面的 Java Flight Recorder 和 Async Profiler,以及操作系统层面的 systemTap 和 eBPF 技术的出现,让生产环境中的持续性能分析(Continuous Profiling)变得越来越可行,捕获偶发故障的现场快照也变得更加容易。


  1. 参见《2021.07.13 我们是这样崩的》https://www.bilibili.com/read/cv17521097/ ↩︎

总字数:901
Last Updated:
Contributors: isno