ステータス: ドラフト(2026-11-14)、実測値。 本記事の数値は 2026-11-13 に筆者が直接測定した値です。環境とリクエストは 第 1 回と同様です。Kubernetes v1.34、24GB 級 GPU 1 基のサーバーと 8GB 級ノート PC 用 GPU サーバー、ノード間 1GbE、RDMA なし、Dynamo v1.6.2 の
vllm-runtime:1.6.2イメージ(vLLM 0.31.0)、モデルは Qwen3-0.6B です。分離サービングが不利となる条件である点を最初にお断りしておきます。 作成ルール: 実測データを基本とし、測定できなかったものは「未測定」、確認できなかったものは「未確認」と表示します。
小さなクラスターで分離サービングを再測定する
目次
1. 分立サービングがやろうとしていること
1つのリクエストを処理する作業は 2 つに分かれます。プロンプト全体を読み込んで KV を作成する作業(prefill)と、その KV を参照しながらトークンを 1 つずつ生成する作業(decode)です。prefill は一度に大きな計算を行い、decode は小さな計算を長く繰り返します。両者が同じ GPU で実行されると、長い prefill 1 つが他のリクエストのトークン生成を停止させてしまいます。
分離サービングでは、これら 2 つを異なるワーカーに割り当てます。prefill ワーカーが KV を作成し、それを decode ワーカーに転送した後、decode ワーカーがトークンを生成します。コストは KV を転送する時間です。Dynamo はこの転送に NIXL というライブラリを使用しており、ドキュメントでは RDMA を前提として説明されています。TCP でも動作しますが、はるかに遅いと記載されています。
本編が答えようとするのは次の 3 点です。
- Kubernetes で Grove なしに分離サービングのグラフが起動するか。
- RDMA のない環境で同じサーバー内の分離にかかるコストはどれほどか。
- 1GbE を跨ぐ分離にかかるコストはどれほどか。
2. 構成
DGD の構成要素に type: prefill と type: decode を配置し、ワーカーの引数に役割を記述します。
# prefill 워커
- --disaggregation-mode
- prefill
- --kv-transfer-config
- '{"kv_connector":"NixlConnector","kv_role":"kv_producer","kv_buffer_device":"cpu"}'
# decode 워커
- --disaggregation-mode
- decode
- --kv-transfer-config
- '{"kv_connector":"NixlConnector","kv_role":"kv_consumer","kv_buffer_device":"cpu"}'
# 둘 다
env:
- {name: UCX_TLS, value: "tcp,self"}
- {name: UCX_RNDV_THRESH, value: "inf"}
RDMA がないため、転送を TCP に固定しました。 kv_buffer_device を cpu としたのは、Dynamo のサンプルのコメントに従ったものです。GPU バッファで TCP を経由させると UCX 内部でクラッシュすると記載されています。この設定では KV が ホストメモリを経由します。ワーカーのログにも「UCX CUDA support was not found」という警告が残りました。
デプロイは 2 つです。
| 名前 | prefill ワーカー | decode ワーカー | KV が通過する経路 |
|---|---|---|---|
| 同じサーバー | 24GB 級 GPU サーバーに 2 台 | 同じサーバーに 2 台 | 1つのサーバー内の TCP |
| サーバー間 | 8GB 級 GPU サーバーに 1 台 | 24GB 級 GPU サーバーに 2 台 | 1GbE |
フロントエンドは第 3 回と同様に KV ルーティングモードです。エンジンの引数とリクエストも同じです。1 つのリクエストは平均 2,371 トークンであり、1 トークンの KV が 114,688 バイトであるため、 1 つのリクエストの KV は約 272MB です。
Grove なしで起動しました。 DGD を適用すると、オペレーターがフロントエンド、prefill、decode をそれぞれ Deployment として作成し、2 分以内に 5 つの Pod すべてが Ready になりました。フロントエンドはリクエストを prefill ワーカーにまず送信し、続いて decode ワーカーに転送しました。チャートのデフォルトでは Grove はオフになっており、このサイズのグラフには不要でした。
3. 同じサーバー内の分離
プレフィックス 2・7・12・24 種類、同時実行 4・8、各ポイント 3 回の中央値です。24 回の測定すべてでエラーは 0 件でした。比較対象の値は第 3 回の KV ルーティング(ワーカー 6 台)です。
| プレフィックスの種類 | 同時実行 | TTFT p50: KV ルーティング / 分離 | TTFT p99: KV ルーティング / 分離 | リクエスト/秒: KV ルーティング / 分離 |
|---|---|---|---|---|
| 2 | 4 | 34 / 286ms | 73 / 592ms | 10.9 / 6.5 |
| 7 | 4 | 40 / 375ms | 129 / 1,007ms | 8.9 / 5.6 |
| 12 | 4 | 43 / 465ms | 138 / 887ms | 8.7 / 5.0 |
| 24 | 4 | 64 / 518ms | 149 / 958ms | 8.1 / 4.8 |
| 2 | 8 | 49 / 462ms | 145 / 981ms | 14.7 / 9.0 |
| 7 | 8 | 60 / 650ms | 163 / 1,477ms | 12.2 / 7.2 |
| 12 | 8 | 69 / 794ms | 172 / 1,744ms | 12.1 / 6.4 |
| 24 | 8 | 106 / 896ms | 184 / 1,776ms | 11.5 / 6.0 |
2つの構成でリソースは同一ではありません。KV ルーティング側は同じ 24GB 級 GPU にワーカー 4 台を配置し、別サーバーのワーカー 2 台を追加で使用しました。分離側はその GPU 1 枚にワーカー 4 台(prefill 2、decode 2)のみを使用しました。そのため、スループットの差にはワーカー 2 台分の要素が混ざっています。一方、 初トークン時間の差はそれだけでは説明がつきません。
最も有利なセルを見るとそれがわかります。プレフィックス 2 種類・同時実行 4 においてキャッシュトークンの比率は 0.857 であり、prefill ワーカーはプレフィックスをほとんど再計算しませんでした。それにもかかわらず、初トークンまでに 286ms かかりました。KV ルーティングにおける同じセルは 34ms でした。 prefill ワーカーのキャッシュにヒットしても、decode ワーカーはその KV を持っていないため、272MB を丸ごと受信する必要があります。 増加した約 250ms がそのコストです。272MB を 0.25 秒で転送したとすると秒速 1GB 強となり、同じサーバー内でホストメモリを経由して TCP でコピーした速度としては妥当なサイズです。転送のみを個別に測定したわけではないため、この内訳は推測です。
同時実行数が上がると転送が重複し、さらに増加します。同時実行 8 において中央値は 0.46〜0.9 秒、p99 は 1〜1.8 秒でした。
ヒット率も低下しました。プレフィックス 12 種類でキャッシュトークン比率が 0.40〜0.42、24 種類で 0.20 前後となり、KV ルーティング(0.57〜0.66、0.36〜0.43)に及びません。プレフィックスキャッシュを保持しているのが prefill ワーカー 2 つだけであり、保持できるプレフィックスの総量がワーカー 6 台時の 3 分の 1 であるためです。
まとめると、この条件において同じサーバー内の分離は、 初トークンが 8〜12 倍遅くなり、スループットが 37〜48% 低下しました。 GPU が 1 枚であるため計算リソースは増加せず、prefill が 100ms 以内に終わる小さなモデルであるため、「長い prefill が decode をブロックする」という問題が最初から小さかったのです。得るものがないところに転送コストだけを上乗せした構成です。
4. サーバー間の分離: 遅かったし、その前にメモリが一杯になった
1GbE は実際には秒速 110〜117MB を転送します。272MB を転送すると 2.3〜2.5 秒かかるという計算になります。
このデプロイは最後まで測定できませんでした。 prefill ワーカーがわずか数件のリクエストで OOMKilled となったためです。
| 試行 | 成功したリクエスト | それらのリクエストの TTFT | その後 |
|---|---|---|---|
| 同時実行 4 | 2 件 | 2,039ms、2,040ms | 負荷をかけてから 9 秒後に prefill ワーカーが OOMKilled(終了コード 137)、残りのリクエストはすべて失敗 |
| 同時実行 1 | 4 件 | 中央値 1,637ms、最大 2,653ms | 同じワーカーが再び OOMKilled |
成功した 6 件の初トークンは 1.6〜2.7 秒であり、リンク速度から計算した値と同等の大きさです。サンプルが 6 件のみであるため、分布を語ることはできません。KV ルーティングの 34ms と比較すると 50 倍前後です。
メモリ面の方がより有用な発見です。prefill ワーカーのメモリ制限は 3,584Mi でした。同じイメージと同じエンジン引数で第 3 回のすべての測定をエラーなく実行した制限です。 kv_buffer_device: cpu では転送すべき KV がホストメモリに載るため、1 つのリクエストあたり 272MB が追加で必要になります。同時実行 4 なら 1GB を超えます。同時実行 1 でも 4 件だけで制限を超過したことを考えると、転送が完了したバッファが即座に解放されていないようです。ワーカーのメモリ使用量を経時的に測定したわけではないため、原因は未確認です。
同じサーバーのデプロイではこの問題は発生しませんでした。あちらのワーカーの制限は 10Gi でした。サーバー間デプロイの制限を引き上げて再測定はしていません。そのサーバーには他のワークロードが同居しており、制限を十分に大きく与えることができなかったためです。
ここで得られる教訓は 1 つだけです。 分離サービングに移行する場合、ワーカーのメモリ制限を再計算する必要があります。 GPU バッファで RDMA を経由する構成では事情が異なるでしょうが(未測定)、TCP とホストバッファを使用する構成では、「同時リクエスト数 × 1 リクエストあたりの KV サイズ」に余裕分を追加した値が、従来の制限に上乗せして必要になります。
5. では、いつメリットがあるのか
本記事では分離サービングが勝る条件を測定することはできませんでした。測定できたのは負ける条件の 2 つです。ただし、損益を分ける要因が何であるかは測定から明らかになりました。
분리 서빙이 치르는 값 ≈ 요청 하나의 KV 바이트 ÷ 전송 처리량
분리 서빙이 얻는 값 ≈ 긴 prefill 이 같은 GPU 의 decode 를 막던 시간
- 支払うコストは事前に計算できます。トークンあたりの KV バイト数に入力長を掛け、リンクのスループットで割ります。本記事のモデルとリクエストでは、同じサーバー内で約 0.25 秒、1GbE で約 2 秒でした。KV が大きなモデルほど、入力が長いほど増加します。
- 得られるメリットは、モデルが大きく入力が長いため prefill に秒単位の時間がかかり、かつ prefill と decode を別々の GPU に配置できる場合に生じます。0.6B モデルの prefill は 100ms 以内であるため、得られるものがありませんでした。
ドキュメントが RDMA を前提としている理由は、この式にあります。転送スループットが数十 GB/s になって初めて、支払うコストが prefill 時間より小さくなります。RDMA や NVLink が存在する環境での損益は未測定です。
6. まとめ
- 分離サービングのグラフは Grove なしで DGD のみで起動しました。構成要素の
typeとワーカーの引数の 2 つがあれば十分です。 - 同じサーバー内で TCP によって分離すると、初トークンが 34〜106ms から 286〜896ms に増加し、スループットは 37〜48% 減少しました。キャッシュにヒットしたリクエストであっても KV 全体を転送するためです。
- 1GbE を跨ぐと初トークンは 1.6〜2.7 秒でした(成功した 6 件)。メモリ制限 3,584Mi の prefill ワーカーは、同時実行 4 では 9 秒後に、同時実行 1 では 4 件の処理で OOMKilled となりました。
- TCP とホストバッファを使用する分離サービングでは、ワーカーのメモリ制限に「同時リクエスト数 × 1 リクエストあたりの KV サイズ」を加算する必要があります。
- 小さなモデル、1枚のGPU、RDMAのないネットワークでは、分離サービングを有効にする理由はありません。同じハードウェアであれば、 第3部のKVルーティングだけを使う方が、あらゆる面で優れていました。
シリーズ全体を1行ずつ要約すると次のようになります。Serviceの背後にあるvLLMは、プレフィックスが1台の容量を超えるとキャッシュを破棄します(第1部)。Dynamoを導入するには、オペレーターのWebhookのスコープと権限から確認する必要があります(第2部)。KVルーティングはその区間でのヒット率を2〜3倍に高めます(第3部)。分離サービングは、高速なネットワークと大きなモデルがある場合の話です(本稿)。
参考
- NVIDIA Dynamo v1.6.2ドキュメントおよびサンプルの分離サービング(disaggregated serving)の項目。
- vLLMドキュメント、Disaggregated PrefillingおよびNixlConnector。
- マニフェスト(
d3-disagg.yaml)、グリッドログ、ワーカーログは、リポジトリのdocs/experiments/2026-11-13-dynamo-adoption/にあります。 - Kubernetesなしで2台のサーバー間でKV転送量とスループットを個別に計測した記録は、 NVIDIA Dynamoを実際に使ってみるにあります。