はじめに
ローカル LLM を民生 GPU で動かす上で、量子化はもう避けて通れません。モデルの重み量子化 (FP8 / AWQ / GPTQ など) は実用化が進み、Hugging Face にあらかじめ量子化済みの重みが並ぶようになりました。今のフロンティアは、その次のレイヤー、つまり KV cache の量子化 です。
ただ KV cache の量子化と聞くと、直感的に身構えるところがあります。
Attention の Key/Value を 8bit に潰したら、文章が崩れるんじゃないか?
このシリーズの前回までで Qwen3.5 系を投機的デコーディング (MTP ① / MTP ② / D-Flash) と組み合わせる実験をしてきましたが、これらは「速度を買う代わりに品質に caveat (Long でループ等) が出る」性質のチューニングでした。FP8 KV cache は 本当にゼロコストで使えるのか、それとも何か代償があるのか。
今回は RTX 4070 12GB + Qwen3.5-4B FP8 という民生環境で、--kv-cache-dtype auto (BF16) と --kv-cache-dtype fp8 を 同じ条件で並べて、
- decode 速度にペナルティがあるか
- TTFT (応答開始) に変化があるか
- 出力テキストの品質が落ちないか
の 3 点を実測しました。先に結論を書くと、3 つとも「実測差なし」 です。
KV cache の量子化と、重み量子化との違い
KV cache の話を始める前に、何を量子化するかを正確に整理します。
Transformer が文章を生成するとき、各 Attention 層は「過去全トークンの Key / Value」を参照します。逐次生成では計算済みの K/V を毎ステップ捨てると無駄になるので、KV cache として VRAM に保持し続けます。
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。理論上 2x のメモリ節約です。
ここで重要なのは、モデルの重み量子化と KV cache 量子化は別の操作だという点です。
- 重み量子化 (FP8 weight): モデルファイルに保存されている weight tensor を FP8 で表現する。推論時の行列積を FP8 精度で実行
- KV cache 量子化 (FP8 KV): Attention の K/V tensor を VRAM に書き込む際に FP8 に変換。読み出し時に BF16 に戻して計算
RedHatAI/Qwen3.5-4B-FP8-dynamic は前者の重み量子化済みモデルです。今回はその上にさらに後者を重ねる「二段量子化」を試すことになります。
vLLM では --kv-cache-dtype fp8 の 1 行で有効になります。
vllm serve RedHatAI/Qwen3.5-4B-FP8-dynamic \
--kv-cache-dtype fp8 \
--attention-backend flashinfer \
--max-model-len 32768 \
--gpu-memory-utilization 0.937 \
--enforce-eager
flash_attn バックエンドは FP8 KV cache に未対応です。FP8 KV を使うには --attention-backend flashinfer が必要になります (これは後述する D-Flash との両立不可問題に直結します)。
FP8 が KV cache 量子化の事実上の標準である背景
「KV cache を量子化する」と言っても、技術的には INT8、INT4、さらには 2bit / 1bit といった選択肢が研究レベルでは存在します。それでも実運用で広く採用されているのは FP8 (E4M3) です。
裏付けとなる事実を 2 つ挙げます。
vLLM 公式は FP8 だけをサポート
vLLM の Quantized KV Cache ドキュメント を見ると、サポートされる KV cache dtype は FP8 (E4M3 / E5M2) のみです。INT8 や INT4 の KV cache はサポート対象外。本番運用を前提とした OSS インフラが選んだ唯一の選択肢が FP8、ということです。
2026 年 4 月の vLLM 公式ブログ
vLLM コアチームは 2026-04-22 の公式ブログ で次のように書いています。
FP8 KV-cache quantization is now ready to be the default starting point for many long-context vLLM deployments.
「もう default にしていい段階に来た」という宣言です。同ブログの定量評価では、
- Qwen3.5-27B で長文 (1M tokens) ベンチマークの AUC 回復率が 100%
- 推論ベンチマーク全般で sub-point の精度差 (BF16 比でほぼ無視できる)
- decode の per-token コストが BF16 の 54% まで低下
- Llama-8B スループットで +14.9%
という数字が出ています。
つまり「FP8 KV cache は壊れるのでは?」という直感に対して、すでに 27B クラスの長文評価で BF16 と区別がつかないレベルまで実証が積み上がっており、vLLM 公式は明示的に default を推奨しています。本記事はこの宣言を、民生 GPU の 4B モデルという別ハードウェア・別モデル規模で再現できるかを確認する位置づけです。
実験設計
ハードウェアと共通条件です。
| 項目 | 内容 |
|---|---|
| GPU | RTX 4070 12 GB (Ada Lovelace, SM 8.9) |
| OS | Ubuntu |
| モデル |
RedHatAI/Qwen3.5-4B-FP8-dynamic (重みは既に FP8 化済み) |
| vLLM | nightly v0.21.1rc1.dev243
|
| 共通フラグ |
--enforce-eager, --max-num-batched-tokens 4096, concurrency = 1 |
| temperature | 0 |
| thinking | enable_thinking=false |
構成
attention backend は flashinfer で揃え、KV cache dtype だけを変えた直接比較を作ります。
| 構成 ID | kv_cache_dtype | max_model_len |
|---|---|---|
| base_fi_2k | auto (BF16) | 2,048 |
| fp8kv_2k | fp8 | 2,048 |
| base_fi_8k | auto (BF16) | 8,192 |
| fp8kv_8k | fp8 | 8,192 |
| base_fi_32k | auto (BF16) | 32,768 |
| fp8kv_32k | fp8 | 32,768 |
base_fa (flash_attn) を比較対象から外しているのは、backend が違うと attention カーネル差が混入して「FP8 KV のコスト」だけを切り出せないからです。
ワークロード
| 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 |
各構成は warmup 1 回 + TTFT 計測 1 回 + 本計測 5 回の中央値で比較。Triton カーネルの JIT コンパイルで初回 TTFT が突出する現象が出るため、warmup を必ず挟む設計にしています。
結果 1: decode tok/s — FP8 KV の速度ペナルティはあるか
理論上、FP8 KV では K/V の書き込み時に BF16 → FP8、読み出し時に FP8 → BF16 の変換が走るためペナルティが乗るはずです。実測でどうか。
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% |
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% |
差は全条件で 0.2% 以内。5 回計測の中央値で 0.2% は warmup 後でも残るマイクロ揺らぎの範囲で、有意な差ではありません。
Qwen3.5-4B クラス + concurrency 1 では、forward 計算 (FP8 GEMM + ハイブリッド attention) が支配的で、KV の dtype 変換コストは forward 全体に埋もれて見えなくなっている、というのが妥当な解釈です。vLLM 公式ブログが報告した「Llama-8B で BF16 比 54%」のような decode コスト削減は、より大きなモデル・長いコンテキスト・並列バッチでこそ顕在化する性質と読んでよいでしょう。今回の 4B + シングルリクエストでは「遅くもならないが速くもならない」が結論です。
結果 2: TTFT — 応答開始の体感は変わるか
| workload | base_fi_2k (BF16) | fp8kv_2k (FP8) | base_fi_8k (BF16) | fp8kv_8k (FP8) |
|---|---|---|---|---|
| medium (~50 tokens in) | 74 ms | 81 ms | 73 ms | 80 ms |
| long (~50 tokens in) | 61 ms | 61 ms | 60 ms | 60 ms |
| ctx_long (~1000 tokens in) | 142 ms | 143 ms | 141 ms | 142 ms |
ショート prefill (medium / long) では 60〜85 ms、中程度 prefill (ctx_long) でも 141〜143 ms と、ほぼ同条件で差なし。FP8 変換のオーバーヘッドは TTFT レベルでも観測できませんでした。
長文 prefill (ctx_32k) では FP8 側が +130ms 程度遅い傾向が出ましたが、これは「同じ flashinfer の FP8 KV パスを通るカーネルが、長い prefill 時に変換コストを見せ始める」ものと考えられます。decode スループットには波及しないため、長文レイテンシ感度が極端に高いユースケースでなければ無視できる差です。
結果 3: 出力品質 — 文章は崩れないか
ここが本記事のいちばん見たかったところです。
「速度差ゼロでも、出力が崩れたら話にならない」のが量子化の常識。同一プロンプト・同一 seed (temperature=0) で BF16 KV と FP8 KV の出力を並べました。代表として ctx_32k (7,937 tokens のテクニカル文書を 3 文要約させるタスク) の応答を示します。
BF16 KV (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 (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.
完全一致でした。3 文構造、専門用語の使い分け (PagedAttention、O(T²) / O(T) の表記)、要点抽出 — すべてが BF16 と区別がつきません。
機械スキャン: 125 応答で繰り返し検出ゼロ
「たまたま1つのプロンプトで一致しただけ」という可能性を潰すため、別途 5 構成 × 5 ワークロード × 5 ラン = 約 125 応答 を機械的にスキャンして、ループ・繰り返し・破綻パターンを検出する review.md を生成しました。
結果は「No obvious repetition loops detected」。BF16 / FP8 のいずれの構成でも、典型的な量子化失敗パターン (「ですですですです...」のような連発、ワード単位の周期、文単位の循環) は検出されませんでした。目視確認でも構造の崩れ・意味の不整合・幻覚増加といった兆候は確認できていません。
重みが既に FP8 で量子化されている Qwen3.5-4B の上に、さらに KV cache の FP8 化を重ねたにもかかわらず、です。
制約と注意点
「ゼロコストで使える」と書く前に、現状の制約をはっきり残しておきます。
1. flash_attn バックエンドでは使えない
FP8 KV を有効にするには --attention-backend flashinfer が必須です。flash_attn は KV cache の FP8 dtype に未対応で、起動時に弾かれます。
ValueError: Selected backend AttentionBackendEnum.FLASH_ATTN is not valid
for this configuration. Reason: ['kv_cache_dtype not supported']
このシリーズで紹介してきた D-Flash speculative decoding は flash_attn 必須です。つまり「D-Flash で 2.6x 速くし、かつ FP8 KV でコンテキストを伸ばす」という両立構成は現状の vLLM では作れません。decode 速度を投機で買うか、KV 効率を取るか、ユースケースで切り分ける必要があります。
| 機能 | flash_attn | flashinfer |
|---|---|---|
| FP8 KV cache | ✗ | ○ |
| D-Flash speculative decoding | ○ | ✗ |
2. シングルリクエスト計測である
今回は concurrency=1 です。vLLM 公式ブログが報告するスループット改善 (Llama-8B で +14.9%) は、KV プール容量が並列実行のボトルネックになる場面で利く性質です。シングル decode で見える「速度ゼロコスト」が、並列バッチでは「スループット改善」に転じる可能性が高い。これは別記事のテーマです。
3. 二段量子化の品質マージン
モデル重み FP8 + KV FP8 という二段量子化です。今回スキャンしたワークロードでは劣化が出ませんでしたが、より長文や多言語・コード生成タスクでは追加検証の価値があります。vLLM 公式ブログの 27B 評価でも 1〜2 ポイントの精度減退があるベンチ (推論系) が報告されています。
まとめ
RTX 4070 12GB + Qwen3.5-4B FP8 における FP8 KV cache の「テキスト性能」評価を整理します。
| 観点 | 結果 |
|---|---|
| decode tok/s | BF16 比 0.2% 以内。実測差なし |
| TTFT | 短〜中 prefill で実測差なし |
| 出力品質 (ctx_32k 要約) | BF16 と完全一致 |
| 機械スキャン (125 応答) | ループ・破綻ゼロ |
| 制約 | flash_attn 非対応 → D-Flash と併用不可 |
冒頭の問い「KV cache を FP8 にすると壊れるのか?」への答えは、少なくとも RTX 4070 + Qwen3.5-4B FP8 + flashinfer + シングルリクエストの範囲では、壊れないです。
そして vLLM コアチームが 2026-04 に "default starting point" と宣言した通り、運用判断としても「使わない理由のない」設定になりつつあります。INT4 / INT8 / 2bit のような実験的 KV 量子化と違って、FP8 KV は OSS インフラが公式にサポートする唯一の選択肢であり、品質・速度の両面で実用ラインに乗っています。
ただし、本記事は「壊れないこと」を確認しただけで、「速くなる/長くなる」という積極的な恩恵にはまだ触れていません。FP8 KV の本領は KV プールが約 2 倍に拡張されるところで、これによって何が変わるか — 同じ VRAM で扱える最大コンテキスト長、同じコンテキスト長での並列収容、起動できる構成の境界線 — は次回ご紹介します。
計測環境: 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 + 投機的デコーディング)
- Qwen3.5-2B で Multi-Token Prediction を試す ① 投機的デコーディングと MTP の基礎
- Qwen3.5-2B で Multi-Token Prediction を試す ② 量子化と MTP の組み合わせ比較
- Qwen3.5-4B + D-Flash を 12GB GPU で動かし更に高速化を試す(日本語環境での実測)
- 日本語だと D-Flash が伸びない?Qwen3.5 + vLLM で日英ベンチを比較した