# 第 0 篇:KV cache 计算
第 0 篇:KV cache 计算
日期:2026-10-14
记录把 verl、vLLM 接到强化学习训推上的过程。这篇不写安装步骤,也不写「我跑通了 GRPO」。训练还没开始。我先弄清一次生成里,显存到底被什么占住。
机器上现成的东西:8 张 A800 80GB,本地 verl 在 /home/wujingxin/verl(v0.8.0),verl_env 里的 vLLM 是 0.11.0。
Prefill 和 Decode
Prefill(预填充阶段) 把整段 prompt(提示词)一起算完,顺手写下每个 token 的 K 和 V。Decode(解码阶段) 每次只算 1 个新 token,用这个 token 的 Q 去看已经写下的全部 K。Prompt 越长,第一步越重。生成越长,后面每一步都要多读一点历史 K、V。
同一层里,\(QK^\top\) 那种分数矩阵只在 prefill 时短暂存在,用完就扔。留下来的是 KV cache。
一层 KV 的公式
一层、一条序列、bf16:
用 Qwen2.5-0.5B、序列长度 512 把每个因子代进去:
- 第一个 \(2\):K 矩阵和 V 矩阵,两份。
- \(n_{\text{kv}} = 2\):Qwen2.5-0.5B 只有 2 个 KV 头(GQA,远少于 14 个 Query 头)。
- \(64\):\(d_{\text{head}}\),也就是
head_dim,每个头的向量维度。 - \(512\):序列长度 \(S\),token 数量。
- 最后一个 \(2\):bf16,一个数值占 2 个字节。
这五个数乘出来是这一层、这一条序列的 KV。模型有 24 层,所以一条序列大约 \(256\text{ KB} \times 24 = 6\text{ MB}\)。这个数对不齐 nvidia-smi。权重和 CUDA 上下文是几百 MB 到几 GB,6 MB 被淹没了。0.5B 只用来把公式算熟。
换到 Qwen2.5-7B 时,五个因子里只有中间三个变了:KV 头从 2 变成 4,头维度从 64 变成 128,序列长度从 512 变成 2048。两头的 2 不变。一层是
28 层约 112 MB。batch 为 64 时,光 KV 大约 7 GB。权重可以多条请求共用,KV 不行,每个请求一份。序列一长、并发一高,显存先被 cache 吃掉。
PagedAttention 不改变注意力的算法,它把 KV 切成固定页再按需领取,避免每条请求按最大长度占一块连续显存。这个说法我还没有在源码里对上。
下一步只打开两个文件,找「谁决定算哪些 token」和「KV 放在哪」:
verl_env里的vllm/v1/core/sched/scheduler.pyvllm/v1/worker/gpu_model_runner.py
浙公网安备 33010602011771号