查看源代码 性能分析

永远不要猜测性能瓶颈

即使是经验丰富的软件开发人员,也常常会错误地猜测程序中的性能瓶颈所在。因此,请分析您的程序,找出性能瓶颈所在,并专注于优化它们。

Erlang/OTP 包含多个工具来帮助查找瓶颈

  • tprof 是一个跟踪分析器,可以测量每次函数调用的调用次数、调用时间或堆分配。
  • fprof 提供有关程序时间花费在哪里的最详细信息,但它会显著减慢所分析的程序的速度。
  • dbg 是通用的 Erlang 跟踪前端。通过使用 timestampcpu_timestamp 选项,它可以用来计时实时系统中函数调用所花费的时间。
  • lcnt 用于查找 Erlang 运行时系统内部锁定机制中的争用点。当寻找进程、端口、ETS 表以及其他可以并行运行的实体之间的交互瓶颈时,它非常有用。

这些工具在 工具 中有进一步的描述。

Erlang/OTP 之外也有几个开源工具可以用来帮助性能分析。其中一些是:

  • erlgrind 可以用来在 kcachegrind 中可视化 fprof 数据。

  • eflame 是 fprof 的替代品,它将性能分析输出显示为火焰图。

  • recon 是 Erlang 性能分析和调试工具的集合。此工具附带一本名为 Erlang in Anger 的电子书。

  • perf 是一个用于 Linux 的采样分析器,它提供类似于 fprof 的功能,但开销要低得多。当模拟器以 +JPperf true 模拟器标志启动时,可以对 Erlang 代码进行性能分析,并且仅在启用 JIT 时可用。

    有关如何运行 perf 的更多详细信息,请参阅 BeamAsm 内部文档中的 perf 支持 部分。

内存分析

eheap_alloc: Cannot allocate 1234567890 bytes of memory (of type "heap").

上述标语是 Erlang 终止的更常见原因之一。由于未知原因,Erlang 运行时系统未能分配内存来使用。发生这种情况时,会生成一个崩溃转储,其中包含有关系统在内存耗尽时状态的信息。使用 crashdump_viewer 来查看正在使用的内存。查找具有大量堆或许多消息、大型 ETS 表等的进程。

在查看正在运行的系统中的内存使用情况时,获取信息的最基本函数是 erlang:memory()。它返回系统当前内存使用情况。instrument 可用于更详细地了解内存的使用位置。

然后可以使用它们各自的信息函数来检查进程、端口和 ETS 表,即 process_info/2erlang:port_info/2ets:info/1

有时,系统会进入这样一种状态,即 erlang:memory(total) 报告的内存与操作系统报告的内存差异很大。其中一个原因是 Erlang 运行时系统内的内部碎片。可以使用 erlang:system_info(allocator) 来检索有关如何分配内存的数据。从该函数获得的数据是原始的且难以阅读。recon_alloc 可用于从 system_info 统计计数器中提取有用的信息。

大型系统

对于大型系统,首先在模拟和有限的场景中运行性能分析可能很有意思。但是,瓶颈往往只有在许多事情同时发生并且涉及许多节点时才会出现或导致问题。因此,最好在真实目标系统上的系统测试工厂中运行性能分析。

对于大型系统,您不想在整个系统上运行性能分析工具。相反,您希望专注于占执行大部分时间的中心进程和模块。

还有一些工具可以用来查看整个系统,开销或多或少。

  • observer 是一个 GUI 工具,可以连接到远程节点并显示有关正在运行的系统的各种信息。
  • etop 是一个命令行工具,可以连接到远程节点并显示类似于 UNIX 工具 top 显示的信息。
  • msacc 允许用户查看 Erlang 运行时系统正在花费时间做什么。具有非常低的开销,这使得它在负载重的系统中运行以了解从哪里开始进行更细粒度的性能分析很有用。

要查找的内容

在分析性能分析活动的结果文件时,请查找被调用多次且具有较长“自身”执行时间(不包括调用其他函数的时间)的函数。被调用很多次的函数也可能很有意思,因为即使是很小的事情,如果重复多次,也可能会累积起来。还要问问自己,您可以做些什么来减少这段时间。以下是您应该问自己的适当类型的问题:

  • 是否可以减少函数被调用的次数?
  • 如果更改测试顺序,是否可以减少任何测试的运行频率?
  • 是否可以删除任何冗余测试?
  • 是否有任何计算表达式每次都给出相同的结果?
  • 是否有其他等效且更有效的方法来执行此操作?
  • 是否可以使用另一种内部数据表示来提高效率?

这些问题并不总是那么容易回答。可能需要一些基准测试来支持您的理论,并避免在您的理论错误时使事情变慢。有关详细信息,请参阅 基准测试

工具

fprof

fprof 测量每个函数的执行时间,包括自身时间,即函数自身执行所花费的时间,以及累积时间,即包括被调用函数的时间。这些值按进程显示。您还可以知道每个函数被调用的次数。

fprof 基于跟踪到文件以尽量减少运行时性能影响。使用 fprof 只需要调用一些库函数,请参阅 Tools 中的 fprof 手册页。

eprof

eprof 基于 Erlang trace_info BIF。 eprof 显示每个进程使用了多少时间,以及这些时间花在了哪些函数调用中。时间以总时间的百分比和绝对时间显示。有关更多信息,请参阅 Tools 中的 eprof 手册页。

cprof

就功能而言,cprof 介于 fprofcover 之间。它在程序运行时按模块计算每个函数被调用的次数。cprof 的性能下降影响较低(与 fprof 相比),并且不需要重新编译任何模块来进行性能分析(与 cover 相比)。有关更多信息,请参阅 Tools 中的 cprof 手册页。

工具摘要

工具结果结果大小对程序执行时间的影响记录调用次数记录执行时间记录被调用者记录垃圾回收
fprof每个进程到屏幕/文件显著减慢总时间和自身时间
eprof每个进程/函数到屏幕/文件中等小幅减慢仅限总计
cprof每个模块到调用者小幅减慢

表:工具摘要

dbg

dbg 是一个通用的 Erlang 跟踪工具。通过使用 timestampcpu_timestamp 选项,它可以作为精密仪器来分析特定进程的函数调用所花费的时间。当试图了解时间在负载重的系统中花费在哪里时,这非常有用,因为可以将性能分析的范围限制得非常小。有关更多信息,请参阅 Runtime Tools 中的 dbg 手册页。

lcnt

lcnt 用于分析并行运行的实体之间的交互。例如,如果您的某个进程需要系统中所有其他进程与之交互(也许它有一些全局配置),则可以使用 lcnt 来确定与该进程的交互是否存在问题。

在 Erlang 运行时系统中,仅当有多个调度器时,实体才会并行运行。因此,lcnt 将在多核上使用多个调度器的系统上显示更多争用点(因此更有用)。

有关更多信息,请参阅 Tools 中的 lcnt 手册页。