#cargo #perf #cargo-subcommand #stack-frame #unit-testing

dev bin+lib flamegraph

一个简单的 cargo 子命令,用于生成 flamegraph,底层使用 inferno

24 个版本

0.6.5 2024 年 2 月 8 日
0.6.4 2023 年 10 月 10 日
0.6.3 2023 年 5 月 11 日
0.6.2 2022 年 7 月 25 日
0.1.12 2019 年 3 月 19 日

#21Cargo 插件

Download history 3960/week @ 2024-04-15 2511/week @ 2024-04-22 2560/week @ 2024-04-29 2350/week @ 2024-05-06 1795/week @ 2024-05-13 1667/week @ 2024-05-20 1382/week @ 2024-05-27 1553/week @ 2024-06-03 1443/week @ 2024-06-10 1707/week @ 2024-06-17 1892/week @ 2024-06-24 1405/week @ 2024-07-01 2171/week @ 2024-07-08 2589/week @ 2024-07-15 3094/week @ 2024-07-22 2601/week @ 2024-07-29

每月 10,557 次下载
5 crate 中使用

MIT/Apache

650KB
822

[cargo-]flamegraph

colorized flamegraph output

一个由 Rust 驱动的 flamegraph 生成器,支持额外的 Cargo 项目支持!它可以用于分析任何东西,而不仅仅是 Rust 项目!无需 perl 或管道 <3

如何使用 flamegraph: 什么是 flamegraph,以及我如何用它来指导系统性能工作?

依赖于 Linux 上的 perf 和其他情况下的 dtrace。基于 @jonhoo 的奇妙 Inferno 全 Rust flamegraph 生成库构建!

Windows 正在获得 dtrace 支持,所以如果你尝试了这个,请告诉我们效果如何 :D

注意:如果你在 Linux 上使用 lld,你必须使用 --no-rosegment 标志。否则 perf 将无法生成准确的堆栈跟踪 (解释)。例如

[target.x86_64-unknown-linux-gnu]
linker = "/usr/bin/clang"
rustflags = ["-Clink-arg=-fuse-ld=lld", "-Clink-arg=-Wl,--no-rosegment"]

安装

cargo install flamegraph

这将使 flamegraphcargo-flamegraph 二进制文件可用在你的 cargo 二进制目录中。在大多数系统中,这通常是类似 ~/.cargo/bin 的东西。

Linux 的要求

Debian (x86 和 aarch)

注意:Debian bullseye(截至 2022 年的当前稳定版本)打包了一个不符合 flamegraph 要求的旧版 Rust。你应该使用 rustup 安装一个最新的 Rust 版本,或者升级到 Debian bookworm(当前的测试版本)或更高版本。

sudo apt install -y linux-perf

Ubuntu (x86)

在 aarch 上不工作,请使用 Debian 发行版,或为 Ubuntu 提交一个包含你的解决方案的 PR

sudo apt install linux-tools-common linux-tools-generic linux-tools-`uname -r`

Ubuntu/Ubuntu MATE(树莓派)

sudo apt install linux-tools-raspi

Pop!_OS

sudo apt install linux-tools-common linux-tools-generic

Shell 自动完成

目前,只有flamegraph支持自动补全。支持的Shell有bashfishzshpowershellelvish。由于为自定义cargo子命令实现自动补全并不直观,因此cargo-flamegraph不支持自动补全。有关详细信息,请参阅#153

您如何启用自动补全取决于您的Shell,例如:

flamegraph --completions bash > $XDG_CONFIG_HOME/bash_completion # or /etc/bash_completion.d/

示例

# if you'd like to profile an arbitrary executable:
flamegraph [-o my_flamegraph.svg] -- /path/to/my/binary --my-arg 5

# or if the executable is already running, you can provide the PID via `-p` (or `--pid`) flag:
flamegraph [-o my_flamegraph.svg] --pid 1337

# NOTE: By default, perf tries to compute which functions are
# inlined at every stack frame for every sample. This can take
# a very long time (see https://github.com/flamegraph-rs/flamegraph/issues/74).
# If you don't want this, you can pass --no-inline to flamegraph:
flamegraph --no-inline [-o my_flamegraph.svg] /path/to/my/binary --my-arg 5

# cargo support provided through the cargo-flamegraph binary!
# defaults to profiling cargo run --release
cargo flamegraph

# by default, `--release` profile is used,
# but you can override this:
cargo flamegraph --dev

# if you'd like to profile a specific binary:
cargo flamegraph --bin=stress2

# if you want to pass arguments as you would with cargo run:
cargo flamegraph -- my-command --my-arg my-value -m -f

# if you want to use interesting perf or dtrace options, use `-c`
# this is handy for correlating things like branch-misses, cache-misses,
# or anything else available via `perf list` or dtrace for your system
cargo flamegraph -c "record -e branch-misses -c 100 --call-graph lbr -g"

# Run criterion benchmark
# Note that the last --bench is required for `criterion 0.3` to run in benchmark mode, instead of test mode.
cargo flamegraph --bench some_benchmark --features some_features -- --bench

cargo flamegraph --example some_example --features some_features

# Profile unit tests.
# Note that a separating `--` is necessary if `--unit-test` is the last flag.
cargo flamegraph --unit-test -- test::in::package::with::single::crate
cargo flamegraph --unit-test crate_name -- test::in::package::with::multiple:crate
cargo flamegraph --unit-test --dev test::may::omit::separator::if::unit::test::flag::not::last::flag

# Profile integration tests.
cargo flamegraph --test test_name

用法

flamegraph非常简单。cargo-flamegraph则更为复杂

Usage: cargo flamegraph [OPTIONS] [-- <TRAILING_ARGUMENTS>...]

Arguments:
  [TRAILING_ARGUMENTS]...  Trailing arguments passed to the binary being profiled

Options:
      --dev                            Build with the dev profile
      --profile <PROFILE>              Build with the specified profile
  -p, --package <PACKAGE>              package with the binary to run
  -b, --bin <BIN>                      Binary to run
      --example <EXAMPLE>              Example to run
      --test <TEST>                    Test binary to run (currently profiles the test harness and all tests in the binary)
      --unit-test [<UNIT_TEST>]        Crate target to unit test, <unit-test> may be omitted if crate only has one target (currently profiles the test harness and all tests in the binary; test selection can be passed as trailing arguments after `--` as separator)
      --bench <BENCH>                  Benchmark to run
      --manifest-path <MANIFEST_PATH>  Path to Cargo.toml
  -f, --features <FEATURES>            Build features to enable
      --no-default-features            Disable default features
  -r, --release                        No-op. For compatibility with `cargo run --release`
  -v, --verbose                        Print extra output to help debug problems
  -o, --output <OUTPUT>                Output file [default: flamegraph.svg]
      --open                           Open the output .svg file with default program
      --root                           Run with root privileges (using `sudo`)
  -F, --freq <FREQUENCY>               Sampling frequency
  -c, --cmd <CUSTOM_CMD>               Custom command for invoking perf/dtrace
      --deterministic                  Colors are selected such that the color of a function does not change between runs
  -i, --inverted                       Plot the flame graph up-side-down
      --reverse                        Generate stack-reversed flame graph
      --notes <STRING>                 Set embedded notes in SVG
      --min-width <FLOAT>              Omit functions smaller than <FLOAT> pixels [default: 0.01]
      --image-width <IMAGE_WIDTH>      Image width in pixels
      --palette <PALETTE>              Color palette [possible values: hot, mem, io, red, green, blue, aqua, yellow, purple, orange, wakeup, java, perl, js, rust]
      --skip-after <FUNCTION>          Cut off stack frames below <FUNCTION>; may be repeated
      --flamechart                     Produce a flame chart (sort by time, do not merge stacks)
      --ignore-status                  Ignores perf's exit code
      --no-inline                      Disable inlining for perf script because of performance issues
      --post-process <POST_PROCESS>    Run a command to process the folded stacks, taking the input from stdin and outputting to stdout
  -h, --help                           Print help
  -V, --version                        Print version

然后使用浏览器打开生成的flamegraph.svg文件,因为大多数图像查看器不支持交互式svg文件。

为非特权用户启用perf

要在不以root身份运行的情况下启用perf,您可以降低proc中的perf_event_paranoid值到您环境中的适当水平。最宽容的值是-1,但这可能不符合您的安全需求等...

echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid

macOS上的DTrace

在macOS上,没有其他替代方案可以像运行超级用户那样启用dtrace。最简单的方法是使用--root,这样rustc将正常运行,但二进制文件将以超级用户身份运行。

请注意,如果正在测试的二进制文件是用户感知的,这将改变其行为。

使用--release时改进输出

由于优化等...,在分析发布构建时,flamegraph中呈现的信息质量有时会受到影响。

为了在一定程度上解决这个问题,您可以在您的Cargo.toml文件中设置以下内容:

[profile.release]
debug = true

或将环境变量设置为CARGO_PROFILE_RELEASE_DEBUG=true

请注意,测试、单元测试和基准测试在发布模式下使用bench配置文件(请参阅此处)。

与基准测试一起使用

为了对现有基准测试进行perf,您需要设置一些配置。在您的Cargo.toml文件中设置以下内容以运行基准测试:

[profile.bench]
debug = true

为perf和dtrace使用自定义路径

如果设置了PERFDTRACE环境变量,它将用作相应的工具命令。例如,要从~/bin使用perf

env PERF=~/bin/perf flamegraph /path/to/my/binary

基于火焰图的系统性能工作指南

火焰图用于可视化程序中的时间花费。许多次每秒,程序中的线程被中断,并记录代码中的当前位置(基于线程的指令指针),以及到达那里的函数调用链。这被称为堆栈采样。然后处理这些样本,并将共享共同函数的堆栈相加。然后生成一个SVG文件,显示测量的调用堆栈,并将其扩展到包含它们的所有堆栈样本的比例。

Y轴显示堆栈深度编号。在查看火焰图时,您的程序的主要功能将接近底部,调用的函数将堆叠在顶部,然后是它们调用的函数,依此类推...

X轴覆盖所有样本。它显示从左到右的时间流逝。从左到右的顺序没有意义。

每个框的宽度显示该函数在CPU上或在调用堆栈中的总时间。如果一个函数的框比其他框宽,这意味着它在执行时比其他函数消耗更多的CPU,或者它被调用的次数比其他函数多。

每个框的颜色并不重要,是随机选择的。

火焰图非常适合可视化程序在运行时最昂贵的部分,这真是太好了,因为...

人类在猜测性能方面很糟糕!

特别是那些从C和C++转到Rust的人,往往会在LLVM可以自动优化的代码中过度优化。在开始微优化、分配最小化等之前,始终最好是清晰明了地编写Rust代码...

许多看似性能很糟糕的事情实际上在Rust中很便宜或免费。闭包速度快。在移动到Box之前在栈上初始化通常会被编译掉。克隆经常会被编译掉。所以,clone()而不是为了太长时间地争取让编译器满意而争夺所有权!

然后制作火焰图,看看是否有任何东西实际上很昂贵。

火焰图是起点,而不是终点

火焰图显示了占用时间的东西,但它们是一种采样技术,用于对测量系统的初级和高级视图。它们非常适合查找需要更仔细查看的东西,并且通常可以根据其火焰图明显地改善某些东西,但它们实际上更多地用于选择要执行优化的目标,而不是作为优化测量工具本身。它们是粗粒度的,并且难以diff(尽管这可能会很快得到支持)。此外,因为火焰图是基于某件事占用总时间的比例,如果你不小心使其他事情变得非常慢,它将显示在火焰图上其他所有东西都变得更小,即使整个程序运行时间要慢得多,你希望优化的项目看起来也更小。

使用火焰图来弄清楚你想优化什么,然后设置一个测量环境,这样你就可以确定是否真正实现了改进。

  • 使用火焰图找到一组优化目标
  • 为这些优化目标创建基准测试,如果适当的话,使用类似cachegrind和cg_diff的东西测量CPU指令,并将它们与上一个版本进行比较。
  • 在许多情况下,测量CPU指令通常比测量工作负载的运行时间要好,因为有可能你的机器上的后台任务运行,导致某些事情在物理时间上变慢,但如果你实际上使实现更快,它很可能与减少总CPU指令有更强的相关性。
  • CPU上花费的时间并不是全部,因为时间也花在等待I/O完成上,这不会用像perf这样的工具来衡量,这些工具只衡量占用CPU时间的操作。查看Brendan Gregg关于离CPU会计的文章以获取更多信息!

性能理论101:定量工程的基础

  • 使用现实的工作负载在现实硬件上,否则你的数据不一定与生产中将要发生的事情有很大关系
  • 我们的所有猜测都有一定程度的错误,所以我们必须测量我们工作的效果。通常,看起来不应该很快的简单代码实际上比看起来优化的代码要快得多。我们需要测量我们的优化,以确保我们没有使代码既难以阅读又变慢。
  • 在改变任何东西之前进行测量,并将结果保存在安全的地方!许多分析工具在再次运行时将覆盖其旧输出,所以确保在开始之前小心保存数据,这样你就可以比较前后。
  • 在未进行其他操作的预热机器上测量,且在上一工作负载后已冷却。当CPU空闲时,会进入省电模式,如果过热,它们也会降频(有时SIMD会因为过热导致运行速度变慢,因为核心必须降频)。

性能理论202:USE方法

USE方法是快速定位性能问题的方法,同时最大限度地减少发现工作。它更多地是关于发现生产问题,而不是直接使用火焰图,但如果你要进行性能分类,它是一个非常好的工具。火焰图可以帮助确定要深入分析队列分析的组件。

计算机中的任何事物都可以被视为具有队列的资源,它可以一次服务一个或多个请求。我们计算机和程序中的各种系统可以在请求开始堆积并排队等待服务之前完成一定量的工作。

一些资源可以在达到最大利用率点之前处理更多的工作,而不会降低性能。网络设备在很大程度上可以视为以这种方式工作。其他资源在达到最大利用率点之前就开始饱和,如磁盘。

磁盘(尤其是旋转磁盘,即使是SSD)如果允许更多的工作排队直到达到工作负载的最大吞吐量,但请求的延迟会在达到100%利用率之前增加,因为磁盘需要更长的时间才能开始处理每个请求。调整磁盘性能通常涉及测量各种IO队列深度,以确保它们足够高,可以获得良好的吞吐量,但又不至于延迟变得不可接受。

无论如何,我们系统中的几乎所有东西都可以根据三个高级特性来分析

  • 利用率是指系统实际进行有用工作服务请求的时间量,可以测量为在可用时间内服务请求的百分比
  • 饱和是指请求在服务之前必须等待。这可以测量为随时间变化的队列深度
  • 错误是指事情开始失败的情况,例如当队列无法再接受任何新的请求时——例如,当TCP连接被拒绝,因为系统的TCP队列已满,其中包含尚未由用户空间程序接受的连接。

这为开始应用USE方法来定位复杂系统中的性能相关问题提供了必要的背景!

方法是

  1. 列出可能表现不佳的各种资源——可能通过创建火焰图并寻找占总运行时间比例大于预期的函数
  2. 选择其中之一
  3. (错误)检查TCP连接失败、其他IO故障、日志中的不良信息等...
  4. (利用率)测量系统的利用率,看其吞吐量是否接近已知的最大值,或者已知会经历饱和的点
  5. (饱和)实际上是否发生了饱和?请求在服务之前是否在排队?在吞吐量保持不变的同时延迟是否上升?

这些探究性问题通常可以迅速识别潜在问题。

如果您想了解更多关于此方面的内容,请查看Brendan Gregg关于它的博客文章。我倾向于建议任何即将成为SRE的人应该将Brendan的《系统性能》书籍作为他们阅读的第一本书,以了解如何在生产系统中快速测量这些指标。

USE 方法源自于一个被称为 排队论 的研究领域,该领域对计算世界以及人类所进行的大量其他物流活动产生了巨大影响。

性能法则

如果你想要深入研究理论,了解相关法则!

  • 可扩展性通用法则 是关于并发收益、排队和协调成本之间的关系。
  • Amdahl 定律 是关于通过并行化可以获得的负载理论最大收益。
  • Little 定律 是一个看似简单但实际上包含排队论微妙含义的定律,它允许我们针对系统的适当队列长度进行推理。

依赖关系

~5–37MB
~600K SLoC