Skip to content

性能分析

使用调试信息以发布模式编译 oxlint

为了进行性能分析,你需要使用调试信息以发布模式编译 oxlint 二进制文件。可以通过向 cargo build 传递 --profile release-with-debug 参数来实现:

bash
cargo build --profile release-with-debug --bin oxlint

构建完成后,二进制文件位于 ./target/release-with-debug/oxlint。此二进制文件应用于性能分析。

CPU - Samply

Samply 是一个命令行 CPU 性能分析工具,其用户界面使用 Firefox 性能分析器。支持 macOS 和 Linux。

要将 Samply 与 oxlint 配合使用,请运行 samply record,然后跟随 oxlint 命令及其参数:

bash
samply record ./target/release-with-debug/oxlint .

为改善性能分析体验,可考虑以下选项:

  • oxlint: --silent 将抑制诊断输出,使性能分析更加聚焦。
  • oxlint: --threads 1 会以单线程模式运行代码检查器,虽然速度较慢,但有助于分析单线程性能表现。
  • samply record: --rate <number> 以更高频率采样性能数据。默认为 1000Hz(1ms),提高采样率可获得更详细的信息,但会导致性能分析文件更大。

例如,以 0.1ms 采样率单线程运行 oxlint

bash
samply record --rate 10000 ./target/release-with-debug/oxlint --silent --threads 1 .

CPU - Mac Xcode Instruments

cargo instruments 是连接 Mac Xcode Instruments 的首选工具。

以下指令复现了 cargo instruments 的操作流程。

首先,安装 Xcode Instruments 命令行工具:

bash
xcode-select --install

然后,确保 oxlint 二进制文件已正确编译(参见此处)。

在底层,cargo instruments 调用 xcrun xctrace 命令,等价于:

bash
xcrun xctrace record --template 'Time Profile' --output . --launch -- /path/to/oxc/target/release-with-debug/oxlint

运行上述命令会产生如下输出:

使用“时间剖析”模板开始记录。正在启动进程:oxlint。
按 Ctrl-C 可停止记录
目标应用已退出,结束记录...
记录完成。正在保存输出文件...
输出文件已保存为:Launch_oxlint_2023-09-03_4.41.45 PM_EB179B85.trace

打开跟踪文件 open Launch_oxlint_2023-09-03_4.41.45\ PM_EB179B85.trace

若要查看自顶向下的调用栈:

  1. 在顶部面板中点击 CPUs
  2. 在左侧输入框中点击 x,然后选择 Time Profiler
  3. 在底部面板中点击“调用树”,开启“反转调用树”并关闭“按线程分离”

对于内存和磁盘操作分析,使用 --template 'Allocations'--template 'File Activity'

如需更详细的 CPU 性能分析(例如一级/二级缓存未命中、周期数和指令数、分支预测信息等),需使用自定义的“CPU 计数器”模板:

  1. 打开 Instruments 并选择“CPU Counters”模板。
  2. 在“CPU Counters”设置中:
    1. 启用“高频采样”选项。
    2. 在“高频采样”选项下方,点击加号图标并选择事件类型。一些建议的事件类型:
      • Cycles:粗略了解每个函数消耗的 CPU 周期数。
      • Instructions:粗略了解每个函数执行的指令数及其耗时周期数。
      • L1D_CACHE_MISS_LD:从内存加载数据时的一级缓存未命中次数。
  3. 一旦启用所需事件,可通过“文件 > 另存为模板...”保存该模板并命名。
  4. 现在可使用 xctrace 通过 --template 选项传入模板名称,例如:xcrun xctrace record --template 'My Custom CPU Counters' --output . --launch -- /path/to/oxc/target/release-with-debug/oxlint

堆分配 - dhat

dhat 是一个堆性能分析工具,可用于识别内存泄漏并分析堆分配模式。

设置

Cargo.toml 中添加 dhat 依赖项:

toml
[dependencies]
dhat = "0.3"

然后在二进制项目顶部添加全局分配器:

rust
#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;

性能分析

在需要分析的作用域内创建一个性能分析器。性能分析器会追踪从创建到销毁期间的所有堆分配:

rust
fn main() {
    let _profiler = dhat::Profiler::new_heap();
    // 你的代码在此处 —— 所有堆分配都将被追踪
}

你也可以将 _profiler 添加到任意函数中,仅追踪特定函数内的内存使用情况:

rust
fn my_function() {
    let _profiler = dhat::Profiler::new_heap();
    // 仅此函数作用域内的分配会被追踪
}

当性能分析器被销毁时,它会自动生成一个 dhat-heap.json 文件。

加载与读取性能分析报告

程序运行后,会在工作目录下生成一个 dhat-heap.json 文件。

要分析性能报告:

  1. dhat 查看器 中打开网页
  2. 加载 dhat-heap.json 文件
  3. 在“排序指标”中选择一个指标以分析内存使用的不同方面:
    • "At t-gmax (bytes)":显示达到峰值内存使用量时的分配情况。使用此指标可识别程序达到最大堆大小时占用最多内存的部分。
    • "At t-end (bytes)":显示在性能分析器销毁前未释放的内存。此指标特别有助于发现内存泄漏,因为它揭示了在分析范围结束时仍存在的分配。
    • "Total (bytes)":显示整个执行过程中分配的总字节数。使用此指标可识别代码中分配最频繁的部分,即使这些内存之后被释放。

高级用法:控制性能分析器生命周期

如需更精细地控制性能分析的停止时机,可显式管理性能分析器的生命周期。例如,仅对核心逻辑进行分析并排除清理阶段:

rust
struct MyApp {
    profiler: Option<dhat::Profiler>,
    // 其他字段
}

impl MyApp {
    fn close(&mut self) {
        // 在此处丢弃性能分析器以捕获清理前的堆状态
        self.profiler = None;
        // 清理代码
    }
}

这种模式有助于识别程序执行特定阶段时哪些数据结构仍在占用内存。