クラウド設計図鑑

KV Cache

応用読了12分KV CacheMHAGQAMQAPagedAttentionPrefix Caching

KV Cacheは生成済みトークンの中間結果を保持する領域で、同時実行数とコンテキスト長に正比例して増えます。大規模構成では重みより大きくなることも珍しくなく、「同時に何人さばけるか」を実質的に決めるのはこの領域です。

30秒で分かる

  • KV Cacheは、生成済みトークンの中間結果(KeyとValue)を保存しておく領域です
  • これがないと、1トークン生成するたびに文章全体を計算し直すことになります
  • サイズは 同時実行数 × コンテキスト長 に正比例して増えます
  • 大規模な構成では、重みよりKV Cacheのほうが大きくなります
  • GQA・PagedAttention・KV量子化が、この消費を抑える3つの手段です

仕組み

なぜ必要か

Transformerは、次のトークンを予測するとき、それまでのすべてのトークンを参照します。素朴に実装すると、1トークン生成するたびに全トークンの計算をやり直すことになります。

キャッシュなし
  1トークン目を生成:1トークン分を計算
  2トークン目を生成:2トークン分を計算  ← 1回目の計算をやり直している
  3トークン目を生成:3トークン分を計算
  ...

キャッシュあり
  1トークン目:1トークン分を計算 → KとVを保存
  2トークン目:新しい1トークン分だけ計算 → 保存済みのKVを再利用
  3トークン目:同上

計算量が劇的に減る代わりに、保存したKVがメモリを占有し続けます。これがKV Cacheです。

計算式

KV Cache / token = 2 × layers × kv_heads × head_dim × dtype_bytes

                   Key と Value の2本
項目 取得元
layers config.json の num_hidden_layers
kv_heads config.json の num_key_value_heads
head_dim hidden_size ÷ num_attention_heads
dtype_bytes 推論エンジンのKVキャッシュ設定(FP16なら2)

これに、リクエストごとのトークン数と同時実行数を掛けます。

合計 = (KV Cache / token) × コンテキスト長 × 同時実行数

KV Cacheは「消えない」

生成中のリクエストが保持するKV Cacheは、そのリクエストが終わるまで解放されません。長い回答を生成している利用者が10人いれば、その10人分が同時に居座り続けます。

平均ではなくピーク時の同時実行数で見積もる必要があるのは、このためです。

GQAでKVヘッドを減らす

Attentionヘッドすべてに個別のKVを持たせると、KV Cacheは膨大になります。そこで、複数のAttentionヘッドでKVを共有する方式が広く使われています。

方式 KVヘッド数 KV Cache量 品質への影響
MHA Attentionヘッドと同数 最大 基準
GQA グループ単位で共有 中程度 ほぼ影響なし
MQA 1つを全ヘッドで共有 最小 わずかに劣化

num_attention_heads が64で num_key_value_heads が8なら、KV CacheはMHAの8分の1です。同じパラメータ数のモデルでも、この値次第で収容できる同時実行数が桁違いになります。

PagedAttention

従来の実装は、リクエストごとに「最大コンテキスト長」分のメモリを連続領域で先に確保していました。実際には最大長まで使わないリクエストがほとんどのため、大量の無駄が生じます。

素朴な確保
  [████░░░░░░░░░░░░]  ← 確保したが使わない領域が無駄

PagedAttention
  [████][████][████]  ← 小さなブロック単位で必要な分だけ割り当て

OSの仮想メモリと同じ考え方で、KV Cacheを固定サイズのブロックに分割して管理します。断片化が減り、同じメモリでより多くのリクエストを収容できます。実装の詳細は推論エンジン側の話であり、Level 3で扱います。

Prefix Caching

複数のリクエストが同じ先頭部分(システムプロンプトなど)を共有する場合、その分のKV Cacheを使い回せます。

RAGでは「同じ文書を参照する質問が続く」ことがあるため、条件次第で効果があります。ただしプロンプトの先頭が完全に一致する必要があるため、リクエストごとに冒頭へ日時やユーザー名を差し込む設計にすると、まったく効かなくなります。

設計に効く数字と落とし穴

計算例

layers 64・kv_heads 8・head_dim 128・FP16の場合:

1トークン   = 2 × 64 × 8 × 128 × 2 = 262,144 bytes ≈ 262 KB
4Kコンテキスト   ≈ 1.07 GB / リクエスト
16並列          ≈ 17.2 GB
50並列          ≈ 53.7 GB     ← 30B FP8の重み(30GB)より大きい
16Kで50並列     ≈ 215 GB      ← 単体GPUでは収まらない

実際に数字を動かして確認するには、Level 2のサイジング計算機を使ってください。

消費を抑える3つの手段

手段 効果 代償
GQA採用モデルを選ぶ 数分の1〜8分の1 モデル選定の制約になる
KV Cacheを量子化する FP16→FP8で半減 長文で品質劣化が出ることがある
コンテキスト長を制限する 線形に削減 RAGで詰められる文書量が減る

重みの量子化ではKV Cacheは減らない

よくある誤解です。重みをINT4に落としても、KV Cacheのサイズは1バイトも変わりません。両者は独立した設定です。

同時実行数で詰まっている場合、重みの量子化はほとんど効きません。KV側(GQA・KV量子化・コンテキスト制限)に手を入れる必要があります。

落とし穴

  • 平均で見積もらない:ピーク同時実行数で計算します
  • 出力長も含める:コンテキスト長は入力+出力です。長い回答を許すほどKV Cacheが伸びます
  • RAG導入で前提が変わる:検索結果をプロンプトへ詰めるため、入力長が数倍になります。RAG導入前のサイジングは、導入後には通用しません
  • 推論エンジンの事前確保設定に注意:起動時にメモリの大半を先に確保する設定が既定になっていることがあり、実測時の「使用量」が実需要を反映しない場合があります

この技術を実際に使って判断しているLevel