Skip to content

プロファイリング

デバッグ情報付きで oxlint を release プロファイルでビルドする

プロファイリングには、oxlint を release 相当でありながらデバッグ情報付きでビルドします。cargo build--profile release-with-debug を付けます。

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

ビルド後のバイナリは ./target/release-with-debug/oxlint です。計測に使うのはこのファイルです。

CPU — Samply

Samply は、Firefox Profiler を UI に使うコマンドライン向け CPU プロファイラです。macOS と Linux で動作します。

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 間隔:

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 instrumentsxcrun xctrace を呼び出します。例:

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

実行すると似たような出力が得られます。

Starting recording with the Time Profiler template. Launching process: oxlint.
Ctrl-C to stop the recording
Target app exited, ending recording...
Recording completed. Saving output file...
Output file saved as: Launch_oxlint_2023-09-03_4.41.45 PM_EB179B85.trace

trace ファイルを開きます: open Launch_oxlint_2023-09-03_4.41.45\ PM_EB179B85.trace

トレースを上から追うとき:

  1. 上部パネルで CPUs をクリック
  2. 左の入力欄で x を押し、Time Profiler を選ぶ
  3. 下のパネルで「Call Tree」を開き、「Invert Call Tree」をオン、スレッド分割はオフ

メモリやディスク I/O には --template 'Allocations'--template 'File Activity' を使います。

L1/L2 キャッシュミス、サイクル・命令数、分岐予測などより細かい CPU 指標には、カスタムの「CPU Counters」テンプレートが必要です。

  1. Instruments を開き「CPU Counters」テンプレートを選ぶ
  2. 「CPU Counters」の設定で「High Frequency Sampling」をオンにする
  3. その下の + からイベントを追加する。例:
    • Cycles — 関数ごとの消費サイクルの大まかな目安
    • Instructions — 命令数とサイクルの関係の目安
    • L1D_CACHE_MISS_LD — メモリロードに伴う L1 ミス
  4. 必要なイベントを有効にしたら「ファイル > テンプレートとして保存...」で名前を付けて保存する
  5. xctrace--template にその名前を渡す: xcrun xctrace record --template 'My Custom CPU Counters' --output . --launch -- /path/to/oxc/target/release-with-debug/oxlint

ヒープ割り当て — dhat

dhat はヒーププロファイラで、リークの当たり付けや割り当てパターンの分析に使えます。

セットアップ

Cargo.toml に依存関係を追加します。

toml
[dependencies]
dhat = "0.3"

バイナリクレートの先頭にグローバルアロケータを置きます。

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

プロファイリング

計測したいスコープでプロファイラを生成します。生成からドロップまでの間のヒープ割り当てが記録されます。

rust
fn main() {
    let _profiler = dhat::Profiler::new_heap();
    // Your code here - all heap allocations will be tracked
}

関数単位だけ追うなら、その関数に _profiler を置きます。

rust
fn my_function() {
    let _profiler = dhat::Profiler::new_heap();
    // Only allocations within this function scope will be tracked
}

ドロップ時に dhat-heap.json が自動で出力されます。

プロファイルの読み方

作業ディレクトリに dhat-heap.json ができます。

  1. ビューアを開く: https://nnethercote.github.io/dh_view/dh_view.html
  2. dhat-heap.json を読み込む
  3. 「Sort metrics」で指標を選ぶ:
    • "At t-gmax (bytes)": ヒープが最大だった時点の割り当て。ピーク時に何がメモリを食っているかを見る
    • "At t-end (bytes)": プロファイラ破棄時点でまだ解放されていないメモリ。リーク候補の探索に有効
    • "Total (bytes)": 実行全体で積算された割り当てバイト数。あとで解放されていても、どこがよく割り当てるかを把握するのに使う

応用: プロファイラの寿命を制御する

クリーンアップを除いた本体だけを計測したいなど、寿命を細かく制御したい場合の例です。

rust
struct MyApp {
    profiler: Option<dhat::Profiler>,
    // other fields
}

impl MyApp {
    fn close(&mut self) {
        // Drop the profiler here to capture the heap state before cleanup
        self.profiler = None;
        // cleanup code
    }
}

このパターンは、実行の特定時点でどのデータ構造がメモリを握っているかを切り出すのに役立ちます。