深度解析 vLLM Prefill 阶段:从 TTFT 延迟到 PagedAttention,一文搞懂大模型推理加速核心
2026/8/28 22:39:02 网站建设 项目流程

前言:最近在做 7B/13B 模型的生产化部署,老板一句“为什么用户问个长问题,首字要等 3 秒?”直接把我送进 vLLM 源码里扒 Prefill。今天把 Prefill 的原理、vLLM 的黑魔法(PagedAttention/Chunked Prefill)和实战调参经验整理出来,看完你就能对着监控面板指点江山了。


一、先搞清概念:Prefill 和 Decode 根本不是一回事

很多新手以为 LLM 推理就是“一直 forward”,其实 vLLM(以及所有现代推理框架)都严格分两阶段:

维度

Prefill(预填充)

Decode(解码)

触发时机

收到 Prompt 后,生成第一个字前

生成第 2 个 token 到结束

并行度

Prompt 所有 token并行算

每次只算1 个新 token

计算瓶颈

计算受限(FLOPs 爆炸,GPU 算力吃满)

访存受限(疯狂读 KV Cache,显存带宽吃紧)

用户感知指标

TTFT(首 Token 延迟)

TPOT / ITL(每秒吐字速度)

简单说:Prefill 是一次性的“重活”,Decode 是细水长流的“碎活”


二、vLLM 里 Prefill 的完整工作流程(源码级视角)

当你curl调 vLLM API 发一个 Prompt,Prefill 在引擎里其实跑了这 4 步:

Step 1:Scheduler 分配“虚拟内存页”

vLLM 最骚的设计是PagedAttention(后面细说)。调度器(Scheduler)先按 Prompt 长度算需要多少个 KV Block(默认 16 token / 块),然后在 BlockManager 里找物理显存块,建立一张block_table(逻辑块 → 物理块映射)。

Step 2:ModelRunner 组输入

把 token_ids、position_ids、还有刚才的slot_mapping(KV 要写进显存的哪个槽)打包,准备喂给 GPU。

Step 3:Transformer 层 Forward + KV 落盘

这是 Prefill 的核心循环,每一层干 3 件事:

  1. 算 Q/K/V(Prompt 所有 token 一起算大矩阵乘,Tensor Core 狂喜)
  2. 调用flash_attn_varlen_func做带 Causal Mask 的注意力(每个 token 只看自己左边)
  3. 把算好的 K/V 按 slot_mapping 直接写进物理块,绝不搞中间拷贝

Step 4:采样第一个 Token

最后一层隐藏态过 LM Head 拿 logits,Sampler 吐出第一个字。

✅ 注意:Prefill 结束 = Prompt 的 KV 全存好了 + 第一个字也预测出来了。之后 Sequence 才进 Running 队列开始 Decode。


三、为什么原生 PyTorch 显存炸?PagedAttention 救场

如果你自己写 demo,给 100 个用户每人预分配max_seq_len=8192的 KV Cache,显存利用率不到 20%,因为:

  • 用户可能只问了 100 字(内部碎片)
  • 连续显存申请容易失败(外部碎片)

vLLM 的解法 = 操作系统的分页思想

逻辑视图: [Block0][Block1][Block2]... (连续) 物理显存: [P_12] -> [P_3] -> [P_99]... (散的,但 block_table 指着)
  • KV Cache 切成16 token 一块的固定大小物理块
  • Prefill 时注意力 Kernel直接读散块,通过 block_table 寻址,避免torch.cat整块连续内存
  • 显存利用率直接干到 ~96%,这也是 vLLM 吞吐比 HuggingFace TGI 早期版本高的核心原因。

四、Prefill 的两大进阶黑科技(面试/吹牛必考)

1️⃣ Chunked Prefill:专治“长 Prompt 卡住全场”

假设你有个 32k token 的文档问答,如果一次性 Prefill:

  • GPU 会埋头算 200ms 只处理你这一个请求
  • 其他正在聊天的用户 Decode 直接断流(ITL 毛刺巨大)

vLLM V1 的解法:把 Prefill 切块

一个 Scheduler Step 的预算: │─ Decode 请求 token(优先级最高,保吐字流畅) └─ 剩余 token 预算 → 分给等待中 Prompt 的一个 chunk

效果:长 Prompt 分 5~10 个 step 慢慢算完,牺牲一点点 TTFT,换来了全站 ITL 的平滑

💡 对应参数:--max-num-batched-tokens,想压 TTFT 就调大,想压延迟毛刺就调小。


2️⃣ Automatic Prefix Caching(APC):相同 Prompt 白嫖加速

RAG 场景里,几百个用户问同一篇 PDF,系统提示词(System Prompt)一模一样。

vLLM 给每个“满的 KV Block”算个哈希指纹:

  • 新请求来了,先查“前缀指纹库”
  • 命中了直接把物理块指针借过去(Copy-on-Write)
  • Prefill 只算“用户提问那一句”的新 token

实测共享长上下文场景,TTFT 能降一个数量级,而且几乎零额外开销


五、生产环境调优 Cheat Sheet(直接抄)

如果你现在就在管 vLLM 服务,这几个参数建议马上看一眼:

# 启动 vLLM 的推荐 baseline(A100/H100 通用) python -m vllm.entrypoints.api_server \ --model your-model-path \ --block-size 16 \ # 默认16,长文本别乱改,FlashAttention 适配过的 --enable-prefix-caching \ # 长系统提示/RAG 必开!白送的加速 --max-num-batched-tokens 8192 \ # 核心杠杆!512~16384 自己压测 --gpu-memory-utilization 0.90 # 留 10% 给激活值,别吃太满

调优心法

  • RAG / 固定 Promptenable-prefix-caching=True+ 中等max-num-batched-tokens
  • 代码生成 / 追求首字快max-num-batched-tokens拉高(甚至 32k+)
  • 多轮闲聊 / 高并发max-num-batched-tokens压低,保 ITL 稳定

六、总结一句话

Prefill 是 vLLM 用算力换显存效率(PagedAttention)+ 用调度换体验平滑(Chunked Prefill)+ 用哈希换重复计算(Prefix Cache)的综合博弈。

下次同事问你“为什么 vLLM 这么快”,把这张图甩给他:

Prefill 管 KV 算得快,Decode 管 KV 读得省,PagedAttention 管显存不浪费。


📌 延伸思考

Prefix Tuning / LoRA 的前缀向量,本质上也是 Prefill 阶段参与注意力,你猜 vLLM 的 Prefix Cache 能缓存 P-Tuning 的虚拟 token 吗?(答案:可以,而且很有意思,下篇写)


如果这篇帮你理清了 Prefill,求个 👍 点赞 + ⭐ 收藏,评论区聊聊你们线上 TTFT 压到多少 ms 了?

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询