从 Prefill、Decode、Attention、KV Cache 到 Batching、量化和推测解码,理解 LLM 在线推理为什么慢、为什么贵、为什么尾延迟难控
| 阶段 | 它在做什么 | 主要消耗 | 用户侧表现 |
|---|---|---|---|
| Tokenize | 把用户输入、历史、RAG 内容和工具结果切成 tokens;细节见 Tokenizer / 词表 | CPU 与少量前处理时间 | 通常不显眼,但超长输入会放大后续成本 |
| Prefill | 模型一次性读完整输入上下文,建立初始注意力状态 | 计算量、显存带宽、KV Cache 初始化 | 直接影响 TTFT,也就是首 token 之前的等待 |
| Decode | 每一步生成一个新 token,并把它追加进上下文 | 内存带宽、KV Cache 读取、逐步调度 | 决定流式输出速度和总耗时 |
| Post-process | 停词、结构化解析、安全过滤、日志和计费 | 应用层 CPU、规则和存储 | 影响最终返回与可观测性 |
如果用户等很久才看到第一个字,先查 TTFT、输入 token 数和 prefill 队列;如果第一个字很快出现但答案慢慢挤出来,先查 decode 速度、输出 token 数和当前 batch 状态。
Attention 的核心动作,是让当前位置去“看”前面相关位置。上下文越长,可看的位置越多,模型就要维护更多关系;位置编码和 RoPE 决定模型如何感知这些顺序与距离,细节见 位置编码 / RoPE。不要把它理解成读一篇文章只是线性扫一遍;在 Transformer 里,位置之间的相互关联本身就是计算对象。
长上下文会同时拉高 prefill 计算、KV Cache 显存、批处理调度难度和队列等待。RAG 把十几段资料塞进 Prompt,往往不是免费提升准确率,而是在用延迟和成本换更多背景信息。
| 输入变化 | 推理层变化 | 典型后果 | 治理方向 |
|---|---|---|---|
| Prompt 变长 | Prefill 计算增加,首 token 前等待变长 | TTFT 上升,短交互体验变差 | Prompt 压缩、上下文摘要、模板瘦身 |
| 历史对话变长 | 每轮都带入更多旧内容 | 成本随对话轮次滚雪球 | 记忆分层、窗口裁剪、长期记忆检索 |
| RAG Top-K 过大 | 检索片段占用大量上下文 | 看似信息更多,实际可能稀释重点并拉高延迟 | 重排、引用压缩、只传必要证据 |
| 长文档整篇塞入 | Prefill 与 KV Cache 压力同时增大 | GPU 容量下降,P95 / P99 更容易抖 | 分段处理、Map-Reduce、结构化抽取 |
KV Cache 是用显存换计算:它让 decode 不用重复算历史,但会把长上下文和高并发变成显存管理问题。
| 变量 | 提高后通常发生什么 | 收益 | 风险 |
|---|---|---|---|
| Batch size | 更多请求合在一起跑 | GPU 利用率和总体吞吐提升 | 等待合批会增加排队时间,长短混批会拉高 P95 / P99 |
| Sequence length | 每个请求带更多输入或生成更多输出 | 能处理更复杂上下文和长回答 | Prefill、KV Cache 和 decode 总时间同步上升 |
| 并发数 | 同时服务更多用户请求 | 摊薄硬件成本,提高资源利用 | 队列堆积、显存耗尽、尾延迟抖动 |
| 吞吐优先 | 系统倾向于把 GPU 填满 | 单位 token 成本更低 | 交互式体验可能变差 |
| 延迟优先 | 系统倾向于减少等待和小批快速返回 | TTFT 和 P95 更好 | GPU 利用率可能下降,单位成本上升 |
Batching 不是越大越好。离线批处理、后台总结、批量分类可以更偏吞吐;聊天、代码补全、客服助手更关注 TTFT、流式平滑度和 P95 / P99。
| 技术 | 它主要优化什么 | 工程直觉 | 适合观察的结果 |
|---|---|---|---|
| FlashAttention | Attention 计算的内存访问效率 | 不是改变模型含义,而是更聪明地组织计算和显存读写,减少中间结果搬运。 | Prefill 性能、长上下文吞吐、GPU 利用率 |
| PagedAttention | KV Cache 的显存管理 | 像操作系统分页一样管理 KV Cache,减少碎片,提高可承载并发。 | 并发容量、显存利用率、长短请求混跑稳定性 |
| Speculative Decoding | Decode 阶段的生成速度 | 让小模型先起草多个 token,大模型批量验证;猜得准时,就能减少大模型逐步生成的等待。 | 输出 tokens/s、端到端延迟、质量回归、草稿命中率 |
这些技术主要让同一个模型跑得更稳、更快或更省显存。它们通常不会凭空提升模型知识和推理能力;如果业务答案质量不行,仍然要回到模型、数据、RAG、Prompt、评测和产品流程。
| 场景 | 必须观察 | 最好进一步拆分 | 常见动作 |
|---|---|---|---|
| 自部署推理 | GPU 利用率、显存占用、KV Cache 水位、tokens/s、TTFT、P95 / P99、队列长度、失败率 | prefill 时间、decode 时间、batch size、sequence length、缓存命中率、不同模型版本 | 调 batch、分池、量化、扩容、上下文压缩、引擎升级、长短请求分流 |
| 闭源 API 调用 | 输入 / 输出 token、TTFT、总耗时、错误码、限流、重试、单位请求成本 | 按用户、产品、功能、模型、Prompt 模板、RAG 命中来源拆分 | 模型路由、Prompt 瘦身、输出长度控制、缓存、重试退避、供应商降级 |
| 混合架构 | 不同模型后端的质量、成本、延迟、可用性和命中比例 | 路由规则、Fallback 触发原因、灰度版本、任务类型分布 | 动态路由、SLA 分层、小模型优先、复杂任务升级、统一网关审计 |
| 证据节点 | 必须记录的字段 | 用来定位什么 |
|---|---|---|
| prefill 计算 | input_tokens、sequence_length、prefill_ms、prompt_cache_hit、attention_backend | 首 token 慢是否来自长上下文、缓存未命中或 Attention 实现差异? |
| KV Cache 水位 | kv_cache_bytes、block_usage、eviction_count、reuse_rate、max_context | 显存压力、缓存逐出、长请求挤占和吞吐下降从哪里来? |
| batch 调度 | batch_size、active_sequences、waiting_queue、priority_class、scheduler_policy | P95 / P99 尾延迟是队列、长短请求混跑还是优先级策略造成的? |
| decode 阶段 | output_tokens、tokens_per_second、decode_ms、speculative_accept_rate | 总耗时和单位 token 成本是否被输出长度或解码策略拉高? |
| 资源与成本 | gpu_id / pool、gpu_util、memory_util、billable_units、cost_center | 某个租户、功能或模型版本是否消耗了异常资源? |
| 请求血缘 | request_id、generation_id、route_id、engine_version、trace_id | 从产品坏例能否反查到具体模型池、推理引擎和计算状态? |
如果说 一次请求的一生 解释请求链路,模型服务 / 推理网关 解释生产服务层,那么这一页解释链路中最贵、最容易抖的计算内核:prefill、decode、attention、KV Cache 和调度权衡。