Azure Kubernetes Service (AKS)で GPU プロファイリングフレーム グラフを分析する (プレビュー)

GPU プロファイリングフレーム グラフは、どの関数が最も多くの GPU メモリを消費するかを示します。 この記事では、Azure Kubernetes Service (AKS)用 GPU プロファイリング ツールによって生成された炎グラフを解釈する方法について説明します。

Important

AKS のプレビュー機能は、セルフサービスのオプトイン単位で利用できます。 プレビューは、"現状有姿のまま" および "利用可能な限度" で提供され、サービス レベル アグリーメントおよび限定保証から除外されるものとします。 AKS プレビューは、ベストエフォート ベースでカスタマー サポートによって部分的にカバーされます。 そのため、これらの機能は運用環境での使用を目的としていません。 詳細については、次のサポート記事を参照してください。

開始する前に、 GPU プロファイリングを設定 し、パイロスコープと Grafana でプロファイリング データが使用可能であることを確認します。

Note

最初のプロファイリング実行では、モデルの読み込みなどのスタートアップ操作のシンボルを含むすべてのシンボルが表示されるわけではありませんが、プロファイラーはワークロードを分析してシンボルを解決します。 初期分析の後にワークロードを再度実行して、後続のプロファイルでこれらのシンボルをキャプチャします。

炎のグラフを理解する

フレイムグラフは、プロファイリングで取得したコールスタックを視覚化します。 各バーは関数を表し、積み上げバーは呼び出しチェーン、または別の関数を呼び出した関数を示します。 各バーの幅は、CPU 時間や割り当てられた GPU メモリなど、その関数を通過する測定されたリソースの量を表します。

キー ルール: バーが広いほど、測定されたリソースの多くがその関数を通過します。

以下のサンプルフレイムグラフは、vLLMの推論ワークロードから取得されたものです。

vLLMワークロードについて、Grafanaにおける初期状態の折りたたまれたフレームグラフ表示のスクリーンショット。GPUメモリ割り当てのコールスタックを表す積み重ねられたバーが表示されています。

Tip

Grafana のフレームグラフ パネルで すべてのグループを展開 を使用すると、折りたたむことなく完全なコールスタックを表示できます。 検索 ボックスを 使用して、特定の関数またはキーワードを検索します。 パネルが再び折りたたまれるのを防ぐには、Grafana ダッシュボードの右上隅にある更新間隔を オフ に設定してダッシュボードの自動更新を一時停止し、展開された炎のグラフを調べます。

[すべてのグループを展開] を選択した後の vLLM ワークロードの Grafana の炎グラフのスクリーンショット。GPU メモリ割り当ての個々の関数フレームを含む完全な呼び出し履歴が示されています。

Tip

特定の割り当てパスにフォーカスを設定するには、 Focus ブロック を使用します。

シンボルの読み取り

フレーム グラフ ラベルは、次の規則に従います。

シンボルの形式 Meaning
Foo > bar class Foo: method def bar()
Foo > __init__ クラス Foo のコンストラクター
bar (単独) スタンドアロン def bar() 関数
<interpreter trampoline> CPython のオーバーヘッド。 この記号は無視してかまいません。
<raw-address>、たとえば、0x7f151 Pythonシンボルを使用できないネイティブ C または CUDA コード。

その例は次のとおりです。

  • GPUModelRunner / _allocate_kv_cache_tensors: クラスのメソッド。 この記号を class GPUModelRunner: def _allocate_kv_cache_tensors(self)として読み取る。
  • LlamaMLP / __init__: LlamaMLP(...) オブジェクトの作成時に呼び出されるコンストラクター。
  • _compile_fx_inner: クラス内にないスタンドアロンモジュールレベルの関数。

自己割り当てと合計割り当てについて

[自己] と [合計] の値を使用して、リソースが使用される場所を決定します。

  • Total は、関数によって消費されるリソースと、関数が呼び出すすべてのリソースです。 関数は大きな合計を持つことができますが、呼び出しチェーンの一部であるため、それ自体は何も割り当てられません。
  • Self は、子関数を除き、関数によって直接消費されるリソースです。 自己価値が高いということは、この関数がリソースを消費する場所であることを意味します。

たとえば、 GPUModelRunner._allocate_kv_cache_tensors に 55.1 GB の自己割り当てがある場合、キー値 (KV) キャッシュ テンソルを作成するために torch.empty() を呼び出す関数です。

次のナビゲーション手法を使用します。

  • リーフ ノードから始めます。リーフ ノードは、その上に何もないバーです。 それらの幅全体は、自分自身への割り当てを示します。
  • 割り当てのソースを検索するときに、自己割り当てがゼロのワイド バーをスキップします。 これらの関数は、他の関数の呼び出しを調整します。
  • 自己割り当て率の高いワイド バーを、最適化の候補として調査します。

最大のリソース コンシューマーを検索する

ホットスポットを特定するには、次の手順に従います。

  1. グラフの上部にある最も広いバーを見てください。 これらのリーフ関数はメモリを割り当てます。 バーが広いほど、消費されるメモリが多くなります。

  2. 自己割り当てと合計割り当てを確認します。 self: 0付きの下部のワイド バーは、呼び出しチェーンの一部です。 自己割り当て率の高いバーが見つかるまで、上方向に進みます。

  3. 呼び出し履歴を下から上に読み取り、関数が呼び出された理由を理解します。 次の例は、割り当てを実行する関数で終わります。

    <raw-address>, for example, 0x7f151       -> native code entry
    <interpreter trampoline>                 -> CPython dispatch
    <module>                                 -> script top level
    EngineCoreProc.run_engine_core           -> vLLM engine startup
    EngineCore.__init__                      -> engine initialization
    EngineCore._initialize_kv_caches         -> KV cache setup
    Worker.initialize_from_config            -> worker setup
    GPUModelRunner.initialize_kv_cache       -> model runner
    GPUModelRunner._allocate_kv_cache_tensors -> actual allocation
    

クイック リファレンスとして次の表を使用します。

ゴール 注目すべきポイント
最も多くのメモリを割り当てるものを見つける スタックの上部にある、幅が最も広いリーフ バー
最も多くのメモリを担当するものを見つける スタックの下部にある最も幅の広いバー
最適化ターゲットを特定する 幅の広い自己割り当てのバー
スキップできる関数を特定する 自己割り当てがゼロのワイド バー

次のステップ