テンソル並列で長文脈のモデルを8枚のGPUに載せると、あるところから急にリクエストをさばけなくなる。GPUは余っているのに、KVキャッシュが埋まって新しい会話を受け付けない。原因は計算量ではなくメモリの重複で、しかもその重複はモデルの構造上ほぼ避けようがなかった。vLLMが8月7日に公開した-dcp(Decode Context Parallelism)は、この重複を正面から消しにいく機能だ。ベンチマークでは同じGPU構成のまま長文脈エージェント処理のスループットが約3倍に伸びている。
出典はvLLM公式ブログのこの記事で、以下ではこれを主軸に、公式ドキュメントと設計時のRFCを突き合わせて中身を整理する。
なぜテンソル並列だとKVキャッシュが二重に載るのか
前提を一つだけ。推論中、モデルは過去のトークンの内部状態(Key/Value)を「KVキャッシュ」として持ち続ける。会話が長いほどこれが膨らみ、長文脈では重みよりKVキャッシュのほうがGPUメモリを食う。
テンソル並列(TP)は、この重みやKVキャッシュを「アテンションヘッド」という単位で各GPUに切り分ける。ここで問題になるのが、最近のモデルがメモリ節約のためにKVヘッドの数を絞っている点だ。Grouped-Query Attention(GQA)はKVヘッドを数個しか持たず、DeepSeekなどが使うMulti-head Latent Attention(MLA)に至っては実質1個しかない。
TPの分割数がKVヘッドの数を超えると、切り分けきれずに複数のGPUが同じヘッドのKVキャッシュのコピーを抱える。記事の言葉では、MLAの場合「潜在KVキャッシュが全TPランクに丸ごと複製される」。GPUを8枚使っても、KVキャッシュ用のメモリは1枚分に毛が生えた量しか実効的に増えない。だから同時実行数を上げようとするとすぐメモリが天井を打つ。
トークンの区間でGPUに割り振る
DCPの発想は、ヘッドではなく「トークンの位置」でKVキャッシュを分けることだ。200Kトークンのリクエストを4GPUに載せるなら、GPU0がトークン0〜50K、GPU1が50K〜100K、というように各GPUが連続した区間だけを持つ。重複はゼロになり、GPUを足した分だけKVキャッシュの容量が素直に増える。
問題は、アテンションが「全過去トークンへの注目」を要求する点だ。自分のGPUには全体の1/Nしか無い。DCPはこれを3ステップで処理する。まずデコード時のクエリ(1トークン分で軽い)を全GPUに配るAllGatherを行い、各GPUが手元のKVスライスに対して部分的なアテンションを計算し、最後にAllGatherとReduceScatterで部分結果をオンラインsoftmaxの再重み付けで合成する。デコード時のクエリは短いので、この通信は割に合う。
設計段階では、リング状に通信するRing Attention方式のフルシャーディング案(RFC #26133)も出ていた。ただしこのRFCは "not planned" で閉じており、実際に載ったのは上記のAllGatherベースの実装だ。同じ「シーケンス方向に割る」でも通信の組み方が違う点は、追いかける際に混同しないほうがいい。
使い方は並列指定を1個足すだけ
GPUの枚数は増やさない。既存のTP構成に--decode-context-parallel-sizeを足すと、その分だけKVキャッシュの重複が減る。
vllm serve deepseek-ai/DeepSeek-V2-Lite \
--tensor-parallel-size 2 \
--decode-context-parallel-size 2
Pythonから直接叩く場合も対応する引数がある。
from vllm import LLM
llm = LLM(
model="deepseek-ai/DeepSeek-V2-Lite",
tensor_parallel_size=2,
decode_context_parallel_size=2,
)
指定できるdcpの上限はモデル構造で決まる。公式ドキュメントによれば、KVヘッドが実質1個のMLA系(DeepSeek-R1、Kimi-K2)はTPと同じ値まで割れて、-tp 8 -dcp 8や-tp 16 -dcp 16で重複を完全に消せる。一方GQAはKVヘッド数で頭打ちになり、KVヘッドが4個のQwen3-235Bは-tp 8のとき-dcp 2が上限だ。大きくするほど重複は減るが通信は増えるので、そのトレードオフは残る。
どれだけ効くか
ブログが載せた実測は、8×B200・Kimi K2.6(NVFP4)で、入力の中央値が約67Kトークン、出力が約400トークンというエージェント寄りのワークロードでのもの。
| ベースラインTP | DCP | |
|---|---|---|
| スループット | 約1,863 tok/s/GPU | 6,091 tok/s/GPU |
| 頭打ちになる同時実行数 | 64 | 512まで伸びる |
| KVキャッシュ使用率 | 同時実行64で100% | 同時実行512で82% |
ベースラインはメモリが先に飽和して同時実行64で止まる。DCPは重複が消えた分バッチを大きく取れるので、同じハードのまま同時実行512、GPUあたり約3倍のトークンを吐く。200K超の長さのバケットでも「高く安定した性能の壁を保つ」とされ、ここが効きどころだ。
実務でどう見るか
これは新しいモデルでも新しいアルゴリズムでもなく、配信レイヤの設計変更で「これまで買ったGPUの使い残し」を回収する類の改善だ。地味だが、長文脈のエージェントを本番で回している側にとっては素直に嬉しい。67Kトークン入力・短い出力というベンチの形が、まさに長い履歴やツール結果を抱えて一手ずつ進むエージェントの実運用に近いのも見逃せない。
一方で万能ではない。効くのは「長文脈」で「デコードが支配的」なワークロードに絞られる。短いプロンプトを大量にさばく用途では、増える通信が割に合わない可能性が高い。GQAではdcpの上限がKVヘッド数で抑えられるため、MLA系ほどの伸びは望みにくい。将来対応として、投機的デコードやMTPとの併用、prefill側の並列化(PCP)、マルチノード向けの通信カーネル改善が挙がっている段階なので、今すぐ全部盛りにはならない。
とはいえ「KVヘッドを絞ったモデル×多GPU」という今どき最も普通の構成で、フラグ1個で重複を捨てられる意味は大きい。手元で長文脈の推論サーバを運用しているなら、まず--decode-context-parallel-sizeを足して同時実行数の頭打ちがどこまで動くかを見る価値がある。
※本記事は一次情報の調査をAIが行い、事実確認のうえ執筆・公開しています。