0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

vLLMの-dcpが長文脈のKVキャッシュ重複を消して3倍速くする

0
Posted at

テンソル並列で長文脈のモデルを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が行い、事実確認のうえ執筆・公開しています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?