このページの改善にご協力ください
このユーザーガイドに貢献するには、すべてのページの右側のペインにある「GitHub でこのページを編集する」リンクを選択してください。
Amazon EKS 上でモデルのロードを高速化する
Amazon EKS に大規模言語モデル (LLM) をデプロイすると、モデルのロード時間は、Pod が推論リクエストの処理を開始する速度に直接影響します。これは、新しい Pod またはノードがトラフィックを処理する前にモデルをロードする必要があるスケールアップイベントに特に当てはまります。モデルのスタートアップには、チューニングとコンパイルアーティファクトのキャッシュで改善できる 2 つのフェーズがあります。
-
重みのロード – Run:ai Model Streamer
を使用して Amazon S3 から GPU メモリにモデルの重みファイルをストリーミングします。 -
torch.compile – モデルの計算グラフを最適化された融合 CUDA/Triton カーネルにコンパイルします。このコンパイルは最初のスタートアップ時に実行され、モデルサイズに応じて起動時間に大きな影響を与える可能性があります。
このトピックでは、Run:ai Model Streamer のパフォーマンスと torch.compile キャッシュを最適化して、モデルのロードプロセスの両方のフェーズを短縮する方法について説明します。推論のために vLLM を Amazon EKS にデプロイする詳細な手順については、「Amazon EKS でのモデルのロードと提供」を参照してください。
EKS Auto Mode 上で S3 ネットワークパスを最適化する
プライベートサブネットに GPU ノードがある EKS Auto Mode 上で実行している場合は、S3 のゲートウェイ VPC エンドポイントを使用して、ノードと S3 間のネットワークパスを最適化することをお勧めします。Gateway VPC エンドポイントでは、S3 へのトラフィックは AWS ネットワーク上にとどまり、NAT Gateway を完全にバイパスするため、共有帯域幅の上限や GB あたりの NAT データ処理料金はありません。Gateway VPC エンドポイントがない場合、トラフィックが NAT Gateway を通過すると、複数のノードが同時にモデルをプルする際に、スケールアップ中に NAT Gateway が共有ボトルネックになります。
EKS Auto Mode は通常、プライベートサブネットにノードを配置するため、S3 へのトラフィックはデフォルトで NAT ゲートウェイを通過します。NAT Gateway は、送信先ごとに最大 100 Gbps の帯域幅と 55,000 の同時接続を提供します。ただし、その帯域幅はプライベートサブネット内のすべてのノード全体で共有されます。スケールアップイベント中、一度に完全なモデルをダウンロードしている複数のノードが同じ NAT Gateway 帯域幅に対して競合するため、すべてのノードでモデルの重みのロードが遅くなる可能性があります。
Run:ai Model Streamer のパフォーマンスチューニング
vLLM や SGLang などの推論エンジンは、推論の起動中に重みをロードするための代替メカニズムとして Run:ai Model Streamer を使用します。
デフォルトでは、Run:ai Model Streamer は、S3 からモデルの重みファイルをダウンロードするときに保守的な同時実行設定を使用します。ダウンロードの同時実行とチャンクサイズを増やすと、より多くのデータを並行してダウンロードすることで、モデルの重みのロード時間を短縮できます。
最適な同時実行を計算する
最適な同時実行値を次のように計算します:
concurrency = ceil(total_model_size_gb / chunk_size_gb)
使用する concurrency 値をモデルサイズとチャンクサイズに基づいて置き換えます。いくつかの例については、次の表を参照してください。例えば、67 GB のモデルと 4 GB のチャンクサイズの場合: ceil(67 / 4) = 17。
| モデルサイズ | チャンクサイズ | 同時実行 |
|---|---|---|
|
10 GB |
4 GB |
3 |
|
67 GB |
4 GB |
17 |
|
140 GB |
4 GB |
35 |
設定を適用します
推論コンテナ仕様に次の引数と環境変数を追加します:
-
--tensor-parallel-size– テンソル並列 (TP) の度数は通常、モデルサイズに基づいてモデルを GPU メモリに収めるために必要な GPU の最小数です。例えば、p5.48xlargeインスタンスタイプの 67 GB モデルには少なくとも 2 GPU が必要なため、2に設定します。 -
concurrencyおよびdistributed(--model-loader-extra-config内) – モデルの計算値にconcurrencyを設定します。テンソル並列処理 (TP > 1) を使用する場合にのみdistributedをtrueに設定します。有効にすると、各テンソル並列ランクは、ランク 0 がすべての重みをロードして他のランクにブロードキャストするのではなく、Amazon S3 から直接独自の重みシャードをストリーミングします。これにより、マルチ GPU デプロイのロードパフォーマンスが大幅に向上します。TP=1 の場合はメリットがないため、設定解除 (またはfalse) のままにしておきます。このオプションには、vLLM V1 アーキテクチャが必要です。これは V0 パスを強制する--enforce-eagerと互換性がありません。両方を使用すると、エラーになるか、分散されていないロードにサイレントにフォールバックします。 -
RUNAI_STREAMER_CHUNK_BYTESIZE– 4 GB のチャンクサイズ。この値は、ベンチマーク全体で一貫して最高のパフォーマンスを示しています。チャンクが大きいほど、S3 リクエストの数が減少し、高帯域幅インスタンスでのスループットが向上します。 -
RUNAI_STREAMER_S3_REQUEST_TIMEOUT_MS– ミリ秒単位のリクエストごとのタイムアウト。遅い S3 レスポンスでより高速に再試行できます。 -
RUNAI_STREAMER_S3_LOW_SPEED_LIMIT– リクエストが遅いと判断されて再試行される前の、1 秒あたりの最小転送速度 (バイト単位)。
containers: - name: vllm-inference image: vllm/vllm-openai:v0.21.0 command: - python3 - -m - vllm.entrypoints.openai.api_server args: # ... your existing args ... - --model=s3://<MODEL_PATH> - --tensor-parallel-size=2- --load-format=runai_streamer - --model-loader-extra-config={"concurrency":17,"distributed":true} env: - name: RUNAI_STREAMER_CHUNK_BYTESIZE value:"4294967296"- name: RUNAI_STREAMER_S3_REQUEST_TIMEOUT_MS value:"3000"- name: RUNAI_STREAMER_S3_LOW_SPEED_LIMIT value:"1048576"
torch.compile コールドスタートの最適化
推論処理プロセスの torch.compile ステップは、モデルの計算グラフ (数学演算のシーケンス) をトレースし、最適化されたフュージョン CUDA/Triton カーネルにコンパイルします。モデルの重みはコンパイルされません。モデルの重みを変換するオペレーションのみがコンパイルされます。
推論エンジンは torch.compile を使用します。これは、モデルアーキテクチャごとにカスタムカーネルエンジニアリングを行わなくても、スループットが自動的に大幅に向上するからです。
-
カーネルフュージョン – 複数の小さなオペレーション (残差加算、レイヤーノーム、アクティベーション) が 1 つのカーネルにフュージョンされ、GPU メモリのラウンドトリップが削減されます。
-
カーネルの起動数の低減 – トランスフォーマーレイヤーは、約 15~30 個の個別の CUDA カーネルから、約 10 個のフュージョンカーネルに削減され、起動あたりの CPU オーバーヘッドが削減されます。
-
ホットパスから Python を削除 – フォワードパス全体が C++ 実行プランになり、オペレーション間の Python インタープリタのオーバーヘッドがなくなります。
-
CUDA グラフの互換性の向上 – コンパイルされた静的グラフは、ほぼゼロの CPU オーバーヘッドでキャプチャおよび再生されます。
-
自動最適化 – 任意のモデルアーキテクチャで機能します。スループットは、eager モードよりも 5~30% 向上します。
次の表は、torch.compile を使用する一般的な推論サービングエンジンを示しています。
torch.compile コールドスタートの問題
torch.compile のトレードオフは、最初の推論ポッドがリクエストを処理する前にコンパイルする必要があることです。このコンパイルは、モデルサイズに応じて数分かかる場合があります。コンパイルされたアーティファクトは小さい (60 GB モデルでは約 15 MB) ですが、コールドスタート時間が大幅に増加します。アーティファクトは小さく、特定の設定に対して決定的であるため、キャッシュして再利用することで、後続の Pod とノードの起動時のコールドスタートペナルティを排除できます。
アーティファクトは以下で構成されます:
-
生成された Triton カーネルソースファイル
-
コンパイルされたカーネルバイナリ (
.cubin) -
カーネル呼び出しをシーケンスするグラフ構造
--enforce-eager トレードオフ
vLLM はデフォルトで torch.compile と CUDA グラフキャプチャを有効にします。--enforce-eager フラグは両方をオフにし、モデルを eager モードで実行します。各オペレーションは Python インタープリタを介してすぐに実行されます。eager モードはコンパイルとグラフキャプチャの両方をスキップするため、Amazon EKS でのモデルのロードと提供 のベースデプロイを含むいくつかのクイックスタートガイドでは、--enforce-eager を使用して Pod をより迅速に開始します。
--enforce-eager は、デバッグ、メモリ制約のあるデプロイ、またはクリーンにコンパイルされないモデルアーキテクチャに対する有効な選択肢であり、これらの理由により本番環境で使用できます。torch.compile コールドスタートのペナルティは軽減できますが、本番環境でランタイムパフォーマンスを維持するために、このページの以降のセクションで説明されているような他のアプローチをお勧めします。
本番環境にデプロイする前に、--enforce-eager のスタートアップ時間とランタイムパフォーマンスのトレードオフを理解します。
| 側面 | --enforce-eager を使用 (eager モード) |
デフォルト (torch.compile + CUDA グラフ) |
|---|---|---|
|
スタートアップ時間 |
速い – コンパイルまたはグラフキャプチャステップなし |
遅いコールドスタート (同じノードで torch.compile アーティファクトをキャッシュする と 新しいノード上で torch.compile キャッシュを事前ウォーミングする によって解決された問題) |
|
定常状態スループット |
ベースライン |
カーネルフュージョンから最大 5~30% 高い |
|
トークンあたりのレイテンシー (スモールバッチ) |
高い CPU 起動オーバーヘッド |
はるかに低い – CUDA グラフはカーネルの起動を 1 ユニットとして再生します |
|
GPU メモリ |
低く、予測可能 |
高い – グラフキャプチャはバッファプールを事前に割り当てます |
|
デバッグ可能性 |
オペレーションごとのクリーンなスタックトレース |
生成されたカーネル内にエラーが表示される |
同じノードで torch.compile アーティファクトをキャッシュする
この手法は、vLLM (デフォルトで有効) や SGLang (--enable-torch-compile 経由で有効) など、torch.compile をサポートする推論エンジンを使用する場合に適用されます。torch.compile がアクティブなとき、つまり --enforce-eager が設定されていないときにのみ機能します。
重要
torch.compile が無効になっている場合、この改善は効果がありません。vLLM では、--enforce-eager フラグは torch.compile を完全に無効にするため、アーティファクトはコンパイルまたはキャッシュされません。Amazon EKS でのモデルのロードと提供 で --enforce-eager を使用したベースデプロイに従った場合、vLLM はキャッシュディレクトリを作成しますが、それに書き込むことはありません。この手法を適用する前に、--enforce-eager を削除します。
推論エンジンが最初のスタートアップ時にモデルの計算グラフをコンパイルすると、生成された最適化されたカーネルをノードのローカルストレージにキャッシュできます。同じノード上の後続の Pod はキャッシュされたアーティファクトを再利用し、コンパイルステップを完全にスキップするため、スタートアップ時間を大幅に短縮できます。
キャッシュ環境変数の追加
次の環境変数を推論コンテナ仕様に追加して、torch.compile キャッシュと Triton キャッシュを永続的ホストパスに送信します。hostPath のルート Amazon Elastic Block Store (Amazon EBS) ボリュームではなく、ノードのローカル NVMe インスタンスストアを使用することをお勧めします。例については、次のセクションを参照してください。
containers: - name: vllm-inference env: # torch.compile cache - name: XDG_CACHE_HOME value:"/compile-cache"- name: TORCHINDUCTOR_CACHE_DIR value:"/compile-cache/inductor"- name: TRITON_CACHE_DIR value:"/compile-cache/triton"volumeMounts: - name: compile-cache mountPath: /compile-cache volumes: - name: compile-cache hostPath: path:/mnt/k8s-disks/0/compile-cachetype: DirectoryOrCreate
キャッシュパスを NVMe インスタンスストアに設定する
コンパイルされたアーティファクトとストリーミングされた重みは、高速ローカルストレージからメリットが得られます。NVMe インスタンスストアを持つ GPU インスタンス (G ファミリーインスタンスや P ファミリーインスタンスなど) では、ルート Amazon EBS ボリュームの約 1 GB/秒と比較して、インスタンスストアは約 30 GB/秒のスループットを提供します。キャッシュ hostPath を NVMe マウントポイントに誘導してスループットを最適化します。
重要
hostPath ボリュームは type: DirectoryOrCreate を使用します。NVMe インスタンスストアによってバックアップされていないパスを指定すると、Kubernetes は代わりにルート Amazon EBS ボリュームにディレクトリをサイレントに作成します。キャッシュは引き続き機能しますが、エラーや警告なしに NVMe パフォーマンスのメリットが失われます。
NVMe マウントポイントとその有効化方法は、EKS Auto Mode ノードとセルフマネージドノードで異なります。
| コンピューティング | NVMe マウントポイント | NVMe インスタンスストアを有効にする方法 |
|---|---|---|
|
EKS 自動モード |
|
リクエストされたエフェメラルストレージに基づいて動的に有効にします。EKS Auto Mode は、NodeClass でリクエストされた |
|
セルフマネージド Karpenter |
|
Karpenter |
EKS Auto Mode では、コンテナ仕様で hostPath を /mnt/.ephemeral/compile-cache に設定します。
volumes: - name: compile-cache hostPath: path: /mnt/.ephemeral/compile-cache type: DirectoryOrCreate
セルフマネージド Karpenter では、コンテナ仕様で hostPath を /mnt/k8s-disks/0/compile-cache に設定します。
volumes: - name: compile-cache hostPath: path: /mnt/k8s-disks/0/compile-cache type: DirectoryOrCreate
サンプル結果
-
ノード上の最初の Pod は torch.compile を実行し、コンパイルされたカーネルをホスト上の
/compile-cacheに書き込みます。 -
同じノード上の後続の Pod は既存のキャッシュをマウントし、コンパイルを完全にスキップするため、torch.compile コールドスタートが約 50~80 秒から約 4~6 秒に短縮されます。
次の表は、異なるモデルサイズの同じノードキャッシュによる改善を示しています。
| モデルサイズ | 最初の Pod torch.compile (キャッシュなし) | 後続の Pods torch.compile (同じノード) |
|---|---|---|
|
60 GB |
約 53 秒 |
約 6 秒 |
|
140 GB |
約 60 秒 |
約 6 秒 |
|
640 GB |
約 80 秒 |
約 6 秒 |
新しいノード上で torch.compile キャッシュを事前ウォーミングする
この手法は、ノード間で同種の GPU、テンソル並列処理、モデル、PyTorch バージョンを使用してマルチノード推論を実行する場合に適用されます。同じノードで torch.compile アーティファクトをキャッシュする の手法は単一ノードのケースに焦点を当てていますが、スケールアップイベント中に追加された新しいノードは空の torch.compile キャッシュで始まります。新しく初期化されたノードのコールドスタート時間をさらに短縮するために、コンパイルされた torch.compile アーティファクトを S3 に保存し、クラスターを結合するときに新しいノードにそれらを事前ダウンロードするキャッシュメカニズムを実装できます。
一般的なアプローチは次のとおりです:
-
最初の Pod が最初のノード上でモデルをコンパイルしたら、torch.compile アーティファクト (~15 MB) を S3 バケットにアップロードします。
-
新しいノードがクラスターを結合したら、推論 Pod がスケジュールされる前に、キャッシュされたアーティファクトをノードのローカルストレージにダウンロードします。
例えば、torch.compile キャッシュをパッケージ化して S3 にアップロードする GPU ノード上で実行される DaemonSet を実装できます。また、推論 Pod が新しいノード上でスケジュールされる前に、そのキャッシュをローカルノードストレージに同期することもできます。
クロスノードキャッシュに関する考慮事項
torch.compile アーティファクトをノード間でキャッシュする場合、コンパイルされたカーネルは、キャッシュを生成したノードとそれを使用するノードの間でこれらのパラメータが一致する場合にのみ有効です。キャッシュメカニズムは、これらすべてを考慮する必要があります。パラメータが一致しない場合、再コンパイルを適用したり、ランタイムエラーを発生させたりする無効なキャッシュが生成されます。キャッシュツールは、S3 オブジェクトキーやキャッシュディレクトリ構造に組み込むなど、これらのパラメータによってアーティファクトを区別する必要があります。
| パラメータ | 重要な理由 |
|---|---|
|
GPUタイプ |
コンパイルされたカーネルは GPU アーキテクチャ固有です (例えば、H100 では sm_90、L4 では sm_89)。 |
|
テンソル並列性 (TP) |
TP の度数が異なると、計算グラフのパーティションが異なります。 |
|
モデル |
各モデルアーキテクチャとサイズは、異なるカーネルにコンパイルされます。 |
|
PyTorch バージョン |
torch.compile および Triton コンパイラの内部では、バージョン間で重大な変更が発生する可能性があります。 |
サンプル結果
クロスノードキャッシュ事前ウォーミング (Qwen3-6-35B-A3B、67 GB、p5.48xlarge での TP=2 の 2x GPU) による指向性テストでは、新しくスケーリングされたノード上の最初の Pod は、既にウォームされたノード上の後続の Pod と同じスタートアップ時間を達成しました。
| シナリオ | 最初の Pod | 2 番目の Pod |
|---|---|---|
|
クロスノードキャッシュなし (新しいノード) |
65 秒 |
16 秒 |
|
クロスノードキャッシュあり (新しいノード) |
16 秒 |
16 秒 |
デプロイの例
次の例では、Run:ai Model Streamer のパフォーマンスチューニングと torch.compile キャッシュを単一の vLLM デプロイマニフェストに結合します。プレースホルダーの値を、独自の設定に置き換えます:
-
serviceAccountName– モデルバケットへの Amazon S3 読み取りアクセス権がある IAM ロールを持つサービスアカウント。 -
nodeSelector(karpenter.sh/nodepool) – GPU ノードプール名 (例:gpu-nodepool-g6e-12xlarge)。 -
--model– モデルの重みへの Amazon S3 パス。 -
--model-loader-extra-config– モデルサイズに基づいてconcurrencyを設定します:ceil(total_model_size_gb / chunk_size_gb)例えば、チャンクサイズが 4 GB の 67 GB モデルでは、ceil(67 / 4) = 17が得られます。 -
--tensor-parallel-size– テンソル並列 (TP) の度数を、モデルをメモリに収めるために必要な GPU の最小数に設定します。 -
hostPathpath– Karpenter を使用したセルフマネージドノードの NVMe インスタンスストアマウントポイント。EKS Auto Mode では、代わりに/mnt/.ephemeral/compile-cacheを使用します。詳細については、「同じノードで torch.compile アーティファクトをキャッシュする」を参照してください。
apiVersion: apps/v1 kind: Deployment metadata: name: vllm-inference namespace: default spec: replicas: 1 selector: matchLabels: app: vllm-inference template: metadata: labels: app: vllm-inference spec: serviceAccountName: <SERVICE_ACCOUNT_NAME> nodeSelector: karpenter.sh/nodepool: <GPU_NODEPOOL> tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule containers: - name: vllm image: vllm/vllm-openai:v0.21.0 command: - python3 - -m - vllm.entrypoints.openai.api_server args: - --model=s3://<BUCKET_NAME>/<MODEL_PATH> - --load-format=runai_streamer - --model-loader-extra-config={"concurrency":17,"distributed":true} - --tensor-parallel-size=2- --max-model-len=8192 - --host=0.0.0.0 - --port=8000 ports: - containerPort: 8000 name: http env: # Run:ai streamer tuning - name: RUNAI_STREAMER_CHUNK_BYTESIZE value:"4294967296"- name: RUNAI_STREAMER_S3_REQUEST_TIMEOUT_MS value:"3000"- name: RUNAI_STREAMER_S3_LOW_SPEED_LIMIT value:"1048576"# torch.compile cache - name: XDG_CACHE_HOME value:"/compile-cache"- name: TORCHINDUCTOR_CACHE_DIR value:"/compile-cache/inductor"- name: TRITON_CACHE_DIR value:"/compile-cache/triton"resources: requests: cpu: "12" memory: 80Gi nvidia.com/gpu: "2" limits: nvidia.com/gpu: "2" volumeMounts: - name: compile-cache mountPath: /compile-cache startupProbe: httpGet: path: /health port: 8000 periodSeconds: 10 failureThreshold: 60 initialDelaySeconds: 30 readinessProbe: httpGet: path: /health port: 8000 periodSeconds: 5 timeoutSeconds: 3 volumes: - name: compile-cache hostPath: path:/mnt/k8s-disks/0/compile-cachetype: DirectoryOrCreate