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 は、Firefox プロファイラをユーザーインターフェースとして使用するコマンドライン形式の CPU プロファイラです。macOS および Linux に対応しています。

oxlint と組み合わせて Samply を使うには、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 instrumentsxcrun 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」をクリックし、「タイムプロファイラ」を選択
  3. 下部パネルで「コールツリー」をクリックし、「コールツリーの逆転」をオンにし、「スレッドごとの分離」をオフにします。

メモリやディスク操作の解析には、--template 'Allocations' および --template 'File Activity' を使用します。

より詳細な CPU プロファイリング(例:L1/L2キャッシュミス、サイクルおよび命令数、ブランチ予測情報など)を行うには、カスタム「CPU カウンター」テンプレートが必要です:

  1. Instruments を開き、「CPU Counters」テンプレートを選択
  2. 「CPU Counters」設定で:
    1. 「高頻度サンプリング」オプションをオンにします。
    2. 「高頻度サンプリング」オプションの下にあるプラスアイコンをクリックし、イベントタイプを選択します。推奨されるイベントタイプ:
      • Cycles:各関数内で費やされたプロセッササイクル数の概算を得るため
      • Instructions:各関数で実行された命令数およびそのサイクル数の概算を得るため
      • L1D_CACHE_MISS_LD:メモリからデータを読み込む際に発生した L1 キャッシュミスの回数
  3. 必要なイベントを有効化したら、「ファイル > テンプレートとして保存...」からテンプレートを保存し、名前を付けてください。
  4. これ以降、xctrace でこのテンプレートを次のように使用できます: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 ビューアを開く:https://nnethercote.github.io/dh_view/dh_view.html
  2. dhat-heap.json ファイルを読み込む
  3. 「並べ替え基準」でメトリクスを選択し、メモリ使用状況の異なる側面を分析します:
    • 「ピークメモリ使用時(バイト)」: ピークメモリ使用時の割り当てを表示します。プログラムが最大ヒープサイズに達したとき、どの部分が最もメモリを消費しているかを特定するのに役立ちます。
    • 「終了時(バイト)」: プロファイラが破棄される前に解放されなかったメモリを表示します。特に、プロファイル範囲の終了時点で残っている割り当てを明らかにするため、メモリリークの特定に有用です。
    • 「合計(バイト)」: 全体実行期間中に割り当てられたバイト数の合計を表示します。メモリは後で解放される可能性もありますが、最も多くの割り当てを行っているコード部分を特定するのに役立ちます。

高度な使い方:プロファイラのライフタイムの制御

プロファイリングを停止するタイミングをより細かく制御するために、プロファイラのライフタイムを明示的に管理できます。たとえば、コアロジックのみをプロファイリングし、クリーンアップ処理を除外する場合:

rust
struct MyApp {
    profiler: Option<dhat::Profiler>,
    // その他のフィールド
}

impl MyApp {
    fn close(&mut self) {
        // クリーンアップ前のヒープ状態をキャプチャするため、ここでプロファイラを破棄
        self.profiler = None;
        // クリーンアップコード
    }
}

このパターンは、プログラム実行中の特定時点でのメモリを保持しているデータ構造を特定するのに役立ちます。