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?

# ローカルゲーミングPCでLLM - 長文運用の限界。FP8 KV Cache でより長い長文を LLM で処理できるようになるか

0
Posted at

はじめに

前回の記事 では、--kv-cache-dtype fp8 を有効にしても、decode 速度・TTFT・出力品質のいずれも BF16 と区別がつかないことを確認しました。「壊れない」ことが分かった。

ではその上で、FP8 KV cache の積極的な恩恵は何か。一言でいえば、

同じ VRAM 予算で、より長い文章を扱えるようになる

です。FP8 にすると K/V が 1 トークンあたり 2 bytes → 1 bytes になり、同じ VRAM 予算で KV プールが約 2 倍に伸びます。これが「コンテキスト長の上限」「同時収容できるリクエスト数」「起動できる設定の境界」を直接押し上げます。

今回は RTX 4070 12GB + Qwen3.5-4B FP8 の上で、max_model_len を 2K → 8K → 32K → 64K → 96K と段階的に伸ばして、

  • BF16 KV と FP8 KV それぞれで どこまで起動できるか
  • 長文プロンプト (8K / 32K tokens) を どの構成が受理するか
  • 出力品質は構成を問わず 遜色ないか (32K BF16 / 32K FP8 / 96K FP8 を並べて確認)

を実測しました。

先に結論を書きます。

max_model_len BF16 KV FP8 KV
2,048 〜 65,536 起動 OK 起動 OK
98,304 (96K) 起動失敗 起動成功 (177K tokens プール)

64K まではどちらも起動でき、出力品質も BF16 / FP8 で違いが見えません。96K の起動可否が FP8 / BF16 の分かれ目で、ここに「同じ GPU でより長い文脈を扱う」FP8 KV の本領が出ます。逆方向に読めば、「同じ文脈長を BF16 比で半分の KV メモリで扱える」ので、VRAM 節約 → 並列収容増 の文脈でも使える設定です。


なぜ FP8 KV で KV プールが伸びるのか

復習を兼ねて、KV cache のサイズ式と vLLM の起動可否ロジックを整理しておきます。

KV cache のメモリは概ね次の式です。

KV cache (bytes) ≈ batch × seq_len × num_layers × num_kv_heads × head_dim × 2 × dtype_bytes
                                                                            ^^^^^^^^^^^^^^^^
                                                                            K と V           dtype に依存

dtype_bytes は BF16 で 2、FP8 で 1。1 トークンあたりの KV メモリが半分になります。

ただし VRAM を消費しているのは KV cache だけではありません。Qwen3.5-4B の場合は内訳に次のような固定コストが入ります。

  • モデル重み (FP8) ~4 GiB
  • Vision Encoder ~3 GiB
  • Mamba SSM state ~0.9 GiB (max_model_len に無関係に固定)
  • activation buffer 他

これらを引いた「残り VRAM」が KV ブロックに割り当てられます。なので

  • max_model_len が小さい (2K) ときの FP8/BF16 比は 1.40x 程度: 固定コストが支配的で、KV 部分の節約効果が薄い
  • max_model_len を伸ばすほど比率は 2x に漸近する: KV ブロックの占有比率が上がり、FP8 化の効果が顕在化する

これが「max_model_len を伸ばすほど FP8 KV のメリットが大きくなる」根拠です。

そしてもう一つ重要なのが、vLLM の起動可否判定です。vLLM はサーバ起動時に「宣言した max_model_len が、確保できた KV プールに収まるか」を検証します。たとえば max_model_len=98304 を指定したのに、実際に確保できた KV プールが 89,600 トークンしかなければ、

ValueError: The model's max seq len (98304) is larger than the maximum number
of tokens that can be stored in KV cache (89,600). Try increasing
`gpu_memory_utilization` or decreasing `max_model_len` when initializing
the engine.

として 起動を拒否されます。「max_model_len 宣言値 ≤ KV プール容量」が起動可能の必要条件です。

FP8 KV で KV プールが 2 倍になるということは、この条件を満たせる max_model_len の上限も約 2 倍になる、ということです。


実験設計

ハードウェアと共通条件は 前回 と同じです。

項目 内容
GPU RTX 4070 12 GB
モデル RedHatAI/Qwen3.5-4B-FP8-dynamic (重みは FP8 化済み)
vLLM nightly v0.21.1rc1.dev243
attention backend flashinfer (FP8 KV を使うため必須)
共通フラグ --enforce-eager, --max-num-batched-tokens 4096, concurrency = 1
gpu_memory_utilization 0.937 (RTX 4070 12GB)

構成マトリクス: max_model_len を 5 段階に振る

構成 ID kv_cache_dtype max_model_len
base_fi_2k auto (BF16) 2,048
base_fi_8k auto (BF16) 8,192
base_fi_32k auto (BF16) 32,768
base_fi_64k auto (BF16) 65,536
base_fi_96k auto (BF16) 98,304
fp8kv_2k fp8 2,048
fp8kv_8k fp8 8,192
fp8kv_32k fp8 32,768
fp8kv_64k fp8 65,536
fp8kv_96k fp8 98,304

ワークロード

長文 prefill を段階的に重くします。

Workload 入力 tokens 出力 max_tokens 性格
medium ~50 256 短入力・標準ケース
long ~50 512 decode 速度感度
ctx_long ~1,000 256 中程度の prefill
ctx_8k 1,793 256 長文 prefill 入門
ctx_32k 7,937 256 重い長文 prefill

結果 1: KV プールサイズ — FP8 で実際何倍に増えたか

vLLM 起動ログから GPU KV cache size: N tokens を抽出して比較しました。

max_model_len BF16 KV tokens FP8 KV tokens FP8 / BF16
2,048 51,200 71,680 1.40x
8,192 75,452 130,327 1.73x
32,768 86,884 163,840 1.89x
65,536 89,600 173,769 1.94x
98,304 — (起動失敗) 177,352

予測通り、max_model_len を伸ばすほど FP8/BF16 比は 2x に漸近します。2K では 1.40x にとどまっていたのが、65K で 1.94x。固定コスト (モデル重み・SSM state など) が一定なので、KV ブロックの占有比率が上がるほど dtype 半減の効果が透けて見えるようになります。

base_fi_64k が KV tokens = 89,600 で頭打ちになっているのは「max_model_len=65536 宣言したものの、実際に確保できた KV プールは 89,600 トークンまで」というケース。max_model_len < KV プール なので起動は通っています。


結果 2: 96K の境界線 — BF16 起動失敗、FP8 起動成功

ここが今回もっとも示唆に富んだ結果です。

base_fi_96k (BF16, max_model_len=98304) を起動すると、vLLM は次のエラーで停止します。

ValueError: The model's max seq len (98304) is larger than the maximum number
of tokens that can be stored in KV cache (89,600). Try increasing
`gpu_memory_utilization` or decreasing `max_model_len` when initializing
the engine.

BF16 で 12GB GPU 上に確保できる KV プールは 89,600 トークンが限界で、これは宣言値 98,304 を下回るため起動を拒否される、という挙動です。gpu_memory_utilization はすでに 0.937 (現実的上限) なので、これ以上は上げられません。

一方 fp8kv_96k は同じ max_model_len=98304177,352 トークンの KV プールを確保して起動に成功します。

項目 base_fi_96k (BF16) fp8kv_96k (FP8)
起動結果 ValueError: model_limit 起動成功
確保 KV tokens 89,600 (98,304 を下回るため不可) 177,352
medium decode 59.97 tok/s
long decode 60.13 tok/s
ctx_long decode 58.54 tok/s
ctx_8k decode 43.29 tok/s
ctx_32k decode 23.57 tok/s

つまり「FP8 KV を使うかどうか」は速度ではなく、まず そもそも起動できるかどうか を決める設定です。RTX 4070 12GB クラスのコンシューマー GPU で 96K コンテキストを宣言した Qwen3.5-4B を立てたければ、FP8 KV はオプションではなく前提です。


結果 3: 長文プロンプト受理マトリクス

max_model_len ごとに、長文プロンプトをどこから受け付けるかも変わります。ctx_8k (1,793 tokens) と ctx_32k (7,937 tokens) を投げて API の振る舞いを並べました。

構成 max_model_len ctx_8k 受理 ctx_32k 受理
base_fi_2k 2,048
base_fi_8k 8,192 ○ (TTFT 773 ms)
base_fi_32k 32,768 ○ (TTFT 773 ms) ○ (TTFT 2,762 ms)
base_fi_64k 65,536
base_fi_96k 98,304 — (起動失敗) — (起動失敗)
fp8kv_2k 2,048
fp8kv_8k 8,192 ○ (TTFT 782 ms)
fp8kv_32k 32,768 ○ (TTFT 2,896 ms)
fp8kv_64k 65,536
fp8kv_96k 98,304

凡例: ○ = 正常応答 / ✗ = 400 BadRequest "input tokens exceed max_model_len" / — = サーバ起動失敗

ここから読み取れることは 2 つあります。

  1. 8K プロンプトを受け付けるには max_model_len >= 8192、32K プロンプトには max_model_len >= 32768 が必要。これは API レベルの厳格チェックで、KV プールが余っていても max_model_len を超える入力は拒否されます。
  2. 「96K 宣言サーバで長文を捌く」シナリオは FP8 KV でしか成立しない。BF16 では base_fi_96k が起動しないので、宣言可能な context window は 64K 止まりです。

結果 4: スループットと TTFT — FP8 KV のオーバーヘッドは見えない

理論上、FP8 KV は K/V 書き込み時に BF16 → FP8、読み出し時に FP8 → BF16 の変換が走るためペナルティが乗るはずです。実測ではどうか。

decode tok/s (medium, 256 tokens 出力)

max_model_len BF16 FP8
2,048 60.26 60.16 -0.17%
8,192 60.16 60.09 -0.12%
32,768 60.05 59.98 -0.12%
65,536 60.04 59.98 -0.10%
98,304 59.97

decode tok/s (long, 512 tokens 出力)

max_model_len BF16 FP8
2,048 60.39 60.33 -0.10%
8,192 60.28 60.26 -0.03%
32,768 60.20 60.13 -0.12%
65,536 60.15 60.14 -0.02%
98,304 60.13

差は 0.2% 以内に収まり、誤差範囲。Qwen3.5-4B クラスでは forward 計算自体が支配的で、KV の dtype 変換は隠れています。

長文 prefill の TTFT

構成 ctx_8k TTFT ctx_32k TTFT
base_fi_32k 773 ms 2,762 ms
fp8kv_32k 786 ms 2,896 ms
fp8kv_96k 781 ms 2,894 ms

ctx_32k で FP8 側が +130 ms ほど遅い傾向はありますが、これは prefill の attention カーネルが flashinfer の FP8 KV パスを通ることのオーバーヘッドと考えられます。スループットは下がっていないので、長文 prefill のレイテンシ感度が高いユースケースでない限り無視できる差です。

注目すべきは、fp8kv_96k の TTFT が fp8kv_32k とほぼ同じであることです。max_model_len を 32K → 96K に 3 倍宣言しても、実プロンプトが同じ 7.9K tokens であれば prefill のコストは変わりません。「context window を大きく宣言しても、短いプロンプトは短いまま処理される」という当然の性質ですが、起動できないと使えない側面でもあります。


結果 5: 出力品質 — 32K / 96K で文章は崩れないか

「FP8 KV にしたら品質が落ちる」「max_model_len を大きく取ると品質が落ちる」はどちらもよく聞く懸念です。今回は同一プロンプト・同一 seed (temperature=0) で BF16 KV と FP8 KV、さらに 96K 宣言を含む 3 構成の出力を並べて確認しました。

代表として ctx_32k (7,937 tokens のテクニカル文書を 3 文要約させるタスク) を取り上げます。

BF16 KV / max_model_len=32K (base_fi_32k)

The document explains that Key-Value (KV) caching is essential for optimizing Large Language Model inference by reusing attention states to reduce computational complexity from O(T²) to O(T). It highlights that FP8 quantization of the KV cache significantly reduces memory footprint, enabling longer context lengths on consumer hardware like the RTX 4070 compared to BF16. Furthermore, the text details how vLLM's PagedAttention mechanism manages this memory efficiently while noting the importance of distinguishing between model weight quantization and KV cache quantization as complementary optimization strategies.

FP8 KV / max_model_len=32K (fp8kv_32k)

The document explains that Key-Value (KV) caching is essential for optimizing Large Language Model inference by reusing attention states to reduce computational complexity from O(T²) to O(T). It highlights that FP8 quantization of the KV cache significantly reduces memory footprint, enabling longer context lengths on consumer hardware like the RTX 4070 compared to BF16. Furthermore, the text details how vLLM's PagedAttention mechanism manages this memory efficiently while noting the importance of distinguishing between model weight quantization and KV cache quantization as complementary optimization strategies.

FP8 KV / max_model_len=96K (fp8kv_96k)

The document explains that Key-Value (KV) caching is essential for optimizing Large Language Model inference by reusing attention states to reduce computational complexity from O(T²) to O(T). It highlights that FP8 quantization of the KV cache significantly reduces memory footprint, enabling longer context lengths on consumer hardware like the RTX 4070 compared to BF16. Furthermore, the text details how vLLM's PagedAttention mechanism manages this memory efficiently while noting the importance of distinguishing between model weight quantization and KV cache quantization as complementary optimization strategies.

3 つとも完全一致でした。

確認できたこと:

  1. KV cache dtype を BF16 → FP8 に変えても出力は変わらない (32K 比較)
  2. max_model_len を 32K → 96K に大きく宣言しても出力は変わらない (FP8 同士比較)
  3. つまり「大きな context window を確保しても品質は損なわれない」「FP8 KV cache も品質を損なわない」

ctx_8k (1,793 tokens 入力) でも同様の一致が得られており、5 構成 × 5 ワークロード × 5 ラン (約 125 応答) を機械的にスキャンしてループ・繰り返し・破綻パターンを検出する review.md では「No obvious repetition loops detected」となりました。

検証範囲の caveat

ただし、今回の ctx_32k プロンプトは 7,937 tokens であり、96K 構成の context window 全体に対しては余裕がある状態での出力です。「真に 96K いっぱいまで詰めた長文プロンプト」での品質は今回のデータには含まれません。

vLLM 公式ブログ (2026-04-22) の Qwen3.5-27B 評価では、1M tokens 級の長文ベンチマーク (mrcr) で AUC 回復率 100% が報告されており、超長文ベンチでも FP8 KV は破綻しないことが大規模モデルでは確認されています。本記事の 4B 規模では、その範囲までは未確認 — 32K プロンプトまでで「壊れていない」ことを確認した状態です。


実務でこの結果をどう活かすか

1. 「速くする」ではなく「収容を増やす」設定として使う

FP8 KV cache は decode 速度を上げません (前回記事の通り)。代わりに、

  • 同じ VRAM 予算で max_model_len を約 2 倍まで宣言できる (RTX 4070 12GB / 4B FP8 で 64K → 96K)
  • 同じ max_model_len 宣言で KV プールが約 2 倍になり、より多くの同時リクエストを収容できる (本記事は concurrency=1 で未検証)

という効果が出ます。スループット (req/s) のスケーリングは「KV プール容量 × 並列度」で決まるため、長文サービングや RAG のような KV 重いワークロードでは特に有効です。

2. 「VRAM 節約」の方向にも使える

同じ max_model_len で考えれば、FP8 にすると KV プールに必要な VRAM が半分で済みます。これは:

  • 同じモデルをより小さい GPU に乗せる
  • 余った VRAM を他のモデルやサービスに回す
  • --mem-fraction-static を下げて起動時の VRAM 余裕を作る

といった運用に転用できます。「長くする」と「節約する」は同じ設定の表裏です。

3. max_model_len は「使う最長プロンプト + 出力余白」に合わせて宣言する

vLLM は API レベルで input_tokens > max_model_len を 400 で拒否します。長文を扱う可能性があるなら、実プロンプト最長 + 出力 max_tokens をカバーする max_model_len 宣言が必須です。「とりあえず大きく宣言しておく」のは VRAM 浪費なので、ワークロードから逆算して値を決めましょう。

4. 起動できない構成は FP8 KV で救える可能性が高い

ValueError: max seq len ... is larger than KV cache は典型的な「max_model_len 宣言過大」エラーですが、FP8 KV にするだけで KV プールが約 2 倍になり、宣言を維持したまま起動できるケースがあります。gpu_memory_utilization を上げる前に試す価値があります。

5. attention backend の制約に注意

機能 flash_attn flashinfer
FP8 KV cache
D-Flash speculative decoding

FP8 KV と D-Flash は同時には組めません。「decode 速度を投機的デコーディングで上げる」 vs 「コンテキスト長と収容数を稼ぐ」のどちらを取るかは、ワークロード次第です。チャット系で短いやり取りが主なら D-FlashMTP、長文 RAG や大量同時接続なら FP8 KV、と切り分けるのが現状の運用解です。


制約と注意点

  • シングルリクエスト計測: 今回は concurrency=1 です。FP8 KV の本当の恩恵 (KV プール 2 倍) は同時実行で利く設定で、複数リクエストでのスループットスケーリングは次回検証対象です。
  • 真の長文ベンチは未検証: 96K 構成の context window 全体を埋めるような超長文プロンプトでの品質は今回のデータでは確認していません。32K プロンプトまでで「壊れていない」状態の確認に留まります。大規模モデル (27B) では公式ブログが 1M tokens 級の品質維持を報告しています。
  • gpu_memory_utilization=0.937 上限: RTX 4070 12GB で今回の評価ではこれが現実的な上限です。0.94 以上で OOM になります。
  • flashinfer のバージョン依存: vLLM nightly の flashinfer ビルドに密結合した結果なので、リリース版の挙動とは差が出る可能性があります。

まとめ

  • FP8 KV cache は max_model_len の上限を約 2 倍まで押し上げる設定。RTX 4070 12GB / Qwen3.5-4B FP8 では BF16 上限 64K に対して FP8 で 96K が起動できた
  • BF16 KV では起動できなかった max_model_len=98304 の構成が、FP8 KV では 177,352 トークンの KV プールで起動成功
  • decode tok/s と TTFT は 32K プロンプトまでで実用上同等。FP8 変換オーバーヘッドは隠れている
  • 出力品質は BF16 KV / 32K FP8 / 96K FP8 の 3 者で完全一致。max_model_len を大きく宣言しても品質は損なわれない
  • 同じ設定は「VRAM 節約 → 並列度向上」の方向にも転用できる
  • 長文サービング・高並列 RAG では選択肢ではなく標準装備にすべき設定

民生 GPU で 4B クラス LLM の 96K コンテキストサービングが現実的になっています。これがデータセンター GPU 専有の機能ではなく、5 万円台のコンシューマー GPU の上で成立している、というのが今回の記事でいちばん伝えたい結論です。


計測環境: RTX 4070 12GB / Ubuntu / vLLM nightly v0.21.1rc1.dev243 / RedHatAI/Qwen3.5-4B-FP8-dynamic / --enforce-eager / --max-num-batched-tokens 4096 / concurrency = 1 / warmup 1 + TTFT 1 + 本計測 5 ラン (中央値)

参考

過去シリーズ (Qwen3.5 + 投機的デコーディング / KV cache)

外部資料

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?