はじめに: GPU 推論のコールドスタート問題

プロダクション環境では、LLM 推論ワーカーはトラフィック変動に応じて弾力的にスケーリングする必要があります。しかし、Kubernetes 上で GPU ワーカーを新規起動するには数分かかります。その間、GPU は割り当てられているもののアイドル状態となり、SLA 違反のリスクが高まります。

NVIDIA の最新技術ブログ(出典: NVIDIA Developer Blog)によると、単一 GPU の vLLM ワークロードにおけるコールドスタートのレイテンシは以下のように分解されます:

フェーズ所要時間
エンジン初期化(重み読み込み、CUDA グラフコンパイルなど)数十秒〜数分
分散ランタイム起動(コントロールプレーン接続、ディスカバリ登録)数秒
合計数分

この問題を解決するため、NVIDIA は Dynamo Snapshot というチェックポイント/リストア手法を提案しています。本記事では、その中核設計と最適化技法を実務観点から分析します。

NVIDIA GPU server rack in data center for AI inference workloads System Abstract Visual

中核アイデア: CRIU + cuda-checkpoint の組み合わせ

Dynamo Snapshot は二つのオープンソースツールを組み合わせます:

  1. cuda-checkpoint: GPU デバイス状態(CUDA コンテキスト、デバイスメモリ、仮想アドレスマッピング)を CPU メモリにダンプ
  2. CRIU (Checkpoint/Restore in Userspace): CPU 側のプロセスツリー状態(メモリ、ファイルディスクリプタ、名前空間)をディスクに直列化

チェックポイントのシーケンスは以下の通りです:

# 1. cuda-checkpoint が GPU 状態を CPU メモリにダンプ
cuda-checkpoint --pid <PID> --dump-dir /tmp/gpu_state

# 2. CRIU がホスト状態をディスクに保存
criu dump --tree <PID> --images-dir /tmp/criu_images --shell-job

リストア時は逆に、CRIU が先にプロセスツリーを復元し、その後 cuda-checkpoint が GPU 状態を新しい GPU に再割り当てします。CRIU は freeze-and-thaw メカニズムで動作するため、復元されたプロセスはチェックポイント時点の正確な命令から実行を再開します。

Kubernetes での実装: snapshot-agent DaemonSet

NVIDIA は snapshot-agent という特権 DaemonSet を Helm チャートで提供します。各ノードでエージェントが実行され、runc ベースのコンテナのチェックポイントとリストアを処理します。

# Helm チャートによるインストール例(概念)
helm install dynamo-snapshot nvidia/dynamo-snapshot \
  --set agent.image.tag=v0.1.0 \
  --set storage.backend=nfs \
  --set storage.path=/shared/checkpoints

エージェントはワーカーの readiness probe を検知した後にチェックポイントを実行し、リストア時は軽量な placeholder ポッドを先に起動してからチェックポイントアーティファクトを復元します。

クィエス/レジューム (Quiesce/Resume) フック

ワーカーが分散ランタイムに接続した状態でチェックポイントを取得すると、TCP 接続が含まれて復元不可能になります。この問題を解決するため、Dynamo Snapshot は シグナルファイルベースのフック を採用しています。

# ワーカーコード内部(擬似コード)
import os
import time

CHECKPOINT_READY = "/tmp/checkpoint_ready"
RESTORE_COMPLETE = "/tmp/restore_complete"

def quiesce_hook():
    # エンジン初期化完了後、分散ランタイム開始前にシグナルファイルを作成
    open(CHECKPOINT_READY, "w").close()
    
    # リストア完了を待機するポーリングループ
    while not os.path.exists(RESTORE_COMPLETE):
        time.sleep(0.1)
    
    # リストア完了後に分散ランタイムを開始
    start_distributed_runtime()

このパターンにより、チェックポイントサイズの最適化と、復元後に再生成が必要なリソース(RDMA 接続など)のクリーンな処理が可能になります。

Kubernetes cluster diagram showing pod cold-start latency optimization Development Concept Image

最適化 1: KV キャッシュのアンマップ/リリースでチェックポイントサイズを 97% 削減

チェックポイントを取得する時点は、まだリクエストを処理していないクィエス状態です。そのため KV キャッシュバッファはまったく不要です。ただし CUDA グラフに仮想アドレスが固定されている必要があるため、NVIDIA は cuMemUnmap + cuMemRelease を使用して物理割り当てのみを解放します。

// CUDA Virtual Memory Management API 使用例
CUmemGenericAllocationHandle handle;
CUdeviceptr ptr;

// 仮想アドレス予約
cuMemAddressReserve(&ptr, kv_cache_size, 0, 0, 0);

// 物理メモリ割り当てとマッピング
cuMemCreate(&handle, kv_cache_size, &mem_pool_props, 0);
cuMemMap(ptr, kv_cache_size, 0, handle, 0);

// チェックポイント直前: 物理メモリのみ解放(仮想アドレスは維持)
cuMemUnmap(ptr, kv_cache_size);
cuMemRelease(handle);

この最適化により、Qwen3-0.6B モデルのチェックポイントサイズが ~190 GiB から ~6 GiB へ 97% 削減されました。vLLM の sleep()/wake_up() や SGLang の torch_memory_saver で既にサポートされている機能です。

最適化 2: CRIU リストア速度を 7.9 倍に向上

大規模モデル(gpt-oss-120b、129 GiB)では、CRIU のリストア時間がコールドスタートよりも長くなる問題がありました。NVIDIA は二つの中核的最適化を適用しました。

2.1 並列 memfd リストア

vLLM/SGLang の wake-up 処理で、重みが CPU ピンメモリ(memfd)に移動されます。従来の CRIU はこれらのバッファを 直列に 復元していましたが、NVIDIA はスレッドプールを導入して 並列処理 に切り替えました。

2.2 Linux Native AIO による匿名メモリ読み込み

従来の CRIU は preadv 同期ループを使用し、一度に一つの読み込みだけを実行していました。NVIDIA はこれを Linux Native AIO に置き換え、最大 128 の読み込みを同時発行して NVMe 帯域幅を完全に活用しました。

// AIO ベース読み込み例(擬似コード)
struct iocb iocbs[128];
struct io_event events[128];
aio_context_t ctx = 0;

// 読み込みジョブ準備
for (int i = 0; i < num_jobs; i++) {
    io_prep_pread(&iocbs[i], fd, buf, size, offset);
}

// バッチ送信(128 同時)
io_submit(ctx, num_jobs, iocbs);

// 完了待機
io_getevents(ctx, num_jobs, num_jobs, events, NULL);

結果は印象的でした:

モデルチェックポイントサイズCRIU(従来)CRIU(AIO+並列)速度向上SOL 比
Qwen3-0.6B6.2 GiB6.8秒2.4秒2.8倍0.95秒
Qwen3-8B26 GiB24秒4.7秒5.1倍1.8秒
gpt-oss-120b129 GiB119秒15秒7.9倍11秒

最適化 3: GPU Memory Service (GMS) - 並列重み復元

最大のボトルネックは、重みデータが CRIU イメージに含まれて 直列に CPU メモリを経由して GPU に移動する点でした。GMS は CUDA VMM API を使用して重みを別のアーティファクトに分離します。

モデル従来 CRIU サイズGMS 適用後 CRIU サイズGMS 重みアーティファクト
Qwen3-0.6B6.2 GiB4.3 GiB1.2 GiB
Qwen3-8B26 GiB4.8 GiB15 GiB
gpt-oss-120b129 GiB6.7 GiB74 GiB

GMS を使用すると プロセス状態復元と重み復元を並列に 実行でき、GPUDirect Storage (GDS) や NVLink などの高速パスを活用できます。8台のローカル NVMe SSD にストライピングした PoC では、gpt-oss-120b で 21倍 の起動時間短縮を達成しました。

Cloud infrastructure with GPU memory service and CRIU checkpoint restore IT Technology Image

実務適用時の注意点と制限

Dynamo Snapshot は現在 実験段階 であり、以下の制約があります:

  • 単一 GPU のみサポート: vLLM、SGLang ワークロードに限定。マルチ GPU/マルチノードは将来のアップデート予定
  • CUDA ドライバパッチが必要: GMS 復元パスはまだ pending 状態
  • TensorRT-LLM 未サポート: 現時点では vLLM と SGLang のみ
  • ストレージバックエンド依存: NFS/SMB などの共有ストレージが必要で、O_DIRECT が利用できない環境では AIO の利点が限定的

日本開発コミュニティでの適用コンテキスト

国内のクラウド環境(AWS Japan、GCP、さくらのクラウドなど)では、GPU インスタンスのコールドスタートは特にセンシティブな課題です。従来はウォームプールを維持するか、スケールアップ/ダウンを制限的に運用する必要がありました。Dynamo Snapshot が安定化すれば、GPU コスト最適化SLA 準拠 を同時に達成できると期待されます。

次のステップとしての学習方向

  1. CRIU の深掘り: Linux プロセスチェックポイント/リストアの内部動作理解
  2. CUDA Virtual Memory Management: cuMemCreatecuMemMapcuMemUnmap API の習得
  3. Kubernetes Device Plugin: GPU リソース管理とカスタムスケジューラ設計
  4. NVIDIA Dynamo オープンソースプロジェクト: GitHub リポジトリ のウォッチ

合わせて読みたい記事


本記事は NVIDIA Developer Blog の内容を分析し、実務観点から再構成したインサイトです。

本コンテンツは、信頼性の高い情報源をもとにAIツールを活用して作成され、編集者によるレビューを経て公開されています。専門家によるアドバイスの代替となるものではありません。