プロファイリング
デバッグ情報付きで oxlint を release プロファイルでビルドする
プロファイリングには、oxlint を release 相当でありながらデバッグ情報付きでビルドします。cargo build に --profile release-with-debug を付けます。
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 と引数を続けます。
samply record ./target/release-with-debug/oxlint .結果を読みやすくするオプション例:
oxlint:--silentで診断出力を抑え、プロファイルを本筋に寄せるoxlint:--threads 1でシングルスレッドにすると遅いが、単スレッド性能の解釈がしやすいsamply record:--rate <number>でサンプリングレートを上げる。既定は 1000Hz(1ms)で、上げると詳細になる代わりにプロファイルが大きくなる
例: シングルスレッド + 0.1ms 間隔:
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 のコマンドラインツールを入れます。
xcode-select --install続けて、デバッグ情報付き oxlint ができていることを確認します。
内側では cargo instruments は xcrun xctrace を呼び出します。例:
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.tracetrace ファイルを開きます: open Launch_oxlint_2023-09-03_4.41.45\ PM_EB179B85.trace
トレースを上から追うとき:
- 上部パネルで CPUs をクリック
- 左の入力欄で
xを押し、Time Profilerを選ぶ - 下のパネルで「Call Tree」を開き、「Invert Call Tree」をオン、スレッド分割はオフ
メモリやディスク I/O には --template 'Allocations' や --template 'File Activity' を使います。
L1/L2 キャッシュミス、サイクル・命令数、分岐予測などより細かい CPU 指標には、カスタムの「CPU Counters」テンプレートが必要です。
- Instruments を開き「CPU Counters」テンプレートを選ぶ
- 「CPU Counters」の設定で「High Frequency Sampling」をオンにする
- その下の + からイベントを追加する。例:
- Cycles — 関数ごとの消費サイクルの大まかな目安
- Instructions — 命令数とサイクルの関係の目安
L1D_CACHE_MISS_LD— メモリロードに伴う L1 ミス
- 必要なイベントを有効にしたら「ファイル > テンプレートとして保存...」で名前を付けて保存する
xctraceの--templateにその名前を渡す:xcrun xctrace record --template 'My Custom CPU Counters' --output . --launch -- /path/to/oxc/target/release-with-debug/oxlint
ヒープ割り当て — dhat
dhat はヒーププロファイラで、リークの当たり付けや割り当てパターンの分析に使えます。
セットアップ
Cargo.toml に依存関係を追加します。
[dependencies]
dhat = "0.3"バイナリクレートの先頭にグローバルアロケータを置きます。
#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;プロファイリング
計測したいスコープでプロファイラを生成します。生成からドロップまでの間のヒープ割り当てが記録されます。
fn main() {
let _profiler = dhat::Profiler::new_heap();
// Your code here - all heap allocations will be tracked
}関数単位だけ追うなら、その関数に _profiler を置きます。
fn my_function() {
let _profiler = dhat::Profiler::new_heap();
// Only allocations within this function scope will be tracked
}ドロップ時に dhat-heap.json が自動で出力されます。
プロファイルの読み方
作業ディレクトリに dhat-heap.json ができます。
- ビューアを開く: https://nnethercote.github.io/dh_view/dh_view.html
dhat-heap.jsonを読み込む- 「Sort metrics」で指標を選ぶ:
- "At t-gmax (bytes)": ヒープが最大だった時点の割り当て。ピーク時に何がメモリを食っているかを見る
- "At t-end (bytes)": プロファイラ破棄時点でまだ解放されていないメモリ。リーク候補の探索に有効
- "Total (bytes)": 実行全体で積算された割り当てバイト数。あとで解放されていても、どこがよく割り当てるかを把握するのに使う
応用: プロファイラの寿命を制御する
クリーンアップを除いた本体だけを計測したいなど、寿命を細かく制御したい場合の例です。
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
}
}このパターンは、実行の特定時点でどのデータ構造がメモリを握っているかを切り出すのに役立ちます。