大模型推理系统入门
从模型优化到调度优化 · 推理系统技术地图
来源:一起推理吧
整理日期:2026-05-08
约 9000 字原文 + 26 篇参考资料精选
vLLM
PagedAttention
Continuous Batching
FlashAttention
MoE
Speculative Decoding
Prefix Caching
一、文章定位与核心目标
目前主流开源推理框架中,vLLM 与 SGLang 都很值得学习。考虑到 vLLM 社区活跃、资料丰富、工程实现公开透明,这篇文章以 vLLM 所代表的技术路线为主线。
本文目标不是讲源码细节,而是帮你建立一张"推理系统技术地图":
- 近几年最核心的推理优化技术是什么
- 每项技术是为了解决什么问题
- 这些技术在系统里是如何落地的
文章会尽量不陷入过深的算子细节,但每个部分都会给出可继续深入的资料。
二、什么是大模型推理
2.1 大模型架构基础
从架构演进看,Transformer(出自 Attention Is All You Need)是现代大模型的基础。常见结构粗分为三类:
| 类型 | 代表模型 | 特点 |
| Encoder-only | BERT | 双向注意力,适合理解任务 |
| Decoder-only | GPT, Llama, Qwen | 自回归生成,每次预测下一个 token |
| Encoder-Decoder | T5, BART | 编码器+解码器分离 |
当前主流生成式大模型主要采用 Decoder-only,因为它更适合自回归生成。
2.2 推理与训练的关键差异
| 维度 | 训练 (Training) | 推理 (Inference) |
| 计算方向 | 前向 + 反向传播,更新参数 | 仅前向计算 |
| 优化目标 | 收敛速度和总吞吐 | 用户体验与服务稳定性 |
| 请求特征 | batch 固定、长度可控 | 长度、到达时间、输出长度高度不确定 |
2.3 推理的两阶段模型
用户输入 Prompt
→
Prefill
处理完整输入
建立 KV Cache
→
Decode
逐 token 生成
自回归循环
→
输出答案
这两个阶段的计算特征完全不同:
- Prefill:计算密集,一次性处理大量输入 token,像"把整段输入一次性处理完"
- Decode:访存密集,每次只处理一个新 token,但要重复很多轮,直接决定用户体感
2.4 核心性能指标
TTFT
Time To First Token
首 token 延迟
TPOT
Time Per Output Token
每个输出 token 的平均延迟
Throughput
单位时间处理的 token 数或请求数
P95 / P99
Tail Latency
最慢那批请求的表现
Memory Efficiency
显存利用率
尤其 KV Cache 管理
一句话总结:推理系统优化,本质是在"延迟-吞吐-显存-公平性"之间做工程权衡。
三、模型侧优化(Model-side)
参数规模极大的 Transformer 中,最关键的计算热点通常集中在三块:
- Attention:随上下文变长而越来越重,访存压力大
- MLP:很多模型里的纯算力热点
- KV Cache:跨 token 持续占用显存的历史状态缓存
3.1 KV Cache 管理:PagedAttention
核心思想:借鉴操作系统分页思想,把 KV Cache 按固定大小的小块来管理,减少显存碎片。
PagedAttention 如何实现
- 不再要求一条请求的整段 KV 缓存连续放在一大片显存里
- 而是拆成很多固定大小的小块(默认 block_size=16 tokens),需要多少就分配多少
- 系统额外维护一张映射表(block table),记录"这一段内容现在放在哪些小块里"
- 请求结束后,这些小块可以单独回收,也更容易复用给别的请求
工程意义:把"很难管理的一整段大缓存"变成了"很多可灵活调度的小块缓存",更省显存,也更适合高并发场景。
3.2 Attention 机制优化:FlashAttention
核心思想:FlashAttention 主要优化的是"数据搬运",而不是改掉 Attention 的数学定义。它把完整的 attention 矩阵拆成小块分批计算,尽量在 GPU 更快的片上缓存(SRAM)里完成,减少中间结果写回显存(HBM)的次数。
FlashAttention 的关键实现
- 不一次性处理完整的 attention 矩阵,而是拆成很多小块分批计算
- 每算完一小块,尽量立刻在 GPU 片上缓存里继续后续步骤
- 不再把很大的中间分数矩阵完整写回显存
- 减少"写到显存再读回来"的动作
工程意义:提升主要来自"少搬中间结果",通常既能提速,也能降低显存压力。
3.3 MLP 优化:以 MoE 为例
MoE (Mixture of Experts) 的核心是"稀疏激活":不是每次都让全部参数参与计算,而是只让少数几个"专家"处理当前 token。
MoE 的实现流程
- 增加一个很小的路由模块,判断当前 token 更适合交给哪些专家处理
- 每个 token 只会被分配给少数几个专家(如 top-2),而不是所有专家
- 系统把去往同一专家的 token 聚在一起计算,再把结果拼回原顺序
- 训练和服务时还要做负载均衡,避免某些专家总是过载
工程挑战:
- token 在专家之间分发和回收结果,会带来额外通信与重排开销
- 如果很多 token 总是挤到少数专家上,就会出现热点和长尾延迟
四、分布式并行加速
当模型或服务规模继续变大时,单卡通常会先遇到三类问题:
- 模型参数太大,一张卡放不下
- KV Cache 太大,并发一上来显存就吃紧
- 即使能放下,单卡吞吐也不够,扛不住线上流量
四种常见并行方式
| 并行方式 | 切分维度 | 解决什么问题 | 适用场景 |
| Tensor Parallelism (TP) | 按张量维度切分算子 | 单层太大,一张卡算不下 | 大模型单层参数量超过单卡显存 |
| Pipeline Parallelism (PP) | 按层切分模型 | 模型层数太多,整模型放不下 | 超深模型跨多卡/多节点 |
| Data Parallelism (DP) | 复制完整模型 | 模型能放下,但流量太大 | 高并发在线服务 |
| Expert Parallelism (EP) | 把不同专家放到不同设备 | MoE 专家太多,单卡放不下 | MoE 模型推理 |
关键洞见:TP / PP / DP 主要解决"算力与容量扩展",但在线体验(TTFT、P99)仍高度依赖上层调度。
五、系统侧优化(System-side)
如果把一个请求放到真实推理系统里看,它大致经历这样的生命周期:
请求进入等待队列
→
调度器决定何时进 batch
→
执行 Prefill
建立 KV Cache
→
Decode 循环
逐 token 生成
→
结束,回收 KV Cache
5.1 Batching 策略演进
| 策略 | 核心思想 | 优缺点 |
| Static Batching | 攒够固定数量请求再一起执行 | 实现简单,但对波动流量不友好 |
| Dynamic Batching | 在时间窗内尽量多收集请求再执行 | 比 static 灵活,但仍需要"等一会" |
| Continuous Batching | 已经在生成中的请求继续跑,同时持续插入新请求 | GPU 不容易空转,最适合 LLM 推理 |
| Selective Batching | 按请求特征分桶,长度相近的放一起 | 减少互相干扰,进一步提效 |
最值得记住的结论:Continuous Batching 之所以重要,不是因为它名字新,而是因为它更符合 LLM 推理"请求到达时间不同、输出长度不同、decode 过程持续很久"这几个现实特点。
5.2 调度核心:Prefill 与 Decode 的矛盾
推理调度最核心的矛盾:
- Prefill:计算密集、单次开销大
- Decode:单步短但次数多,对交互体验高度敏感
这背后是更普遍的系统矛盾:吞吐和延迟经常天然冲突。
Chunked Prefill
把一个很长的输入拆成多个较小片段,分几轮完成。这样 decode 请求就有机会在这些片段之间穿插执行,减少"首字出来了,后面却突然卡住"的情况。
Disaggregated Prefill/Decode
更进一步的系统会把 prefill 和 decode 放到不同资源池,甚至不同实例上。prefill 可以追求更高吞吐,decode 可以单独追求更稳的交互延迟。代价是系统架构更复杂,请求和缓存的流转更难管理。
5.3 Prefix Caching 与 Cache-aware Scheduling
Prefix Caching 核心思路:复用共享前缀的 KV,跳过重复 prefill。很多请求的开头部分其实是一样的(比如系统提示词或公共上下文),系统会识别这些"已经算过的前缀",把对应缓存记下来。
Cache-aware Scheduling / Routing:传统调度只看队列长度、优先级、显存是否够;cache-aware 调度会进一步看"这个请求能不能复用已有缓存"。如果某个请求命中前缀缓存,它的实际成本就更低,系统可能会优先把它放到能命中缓存的实例上。
5.4 Speculative Decoding(投机解码)
核心思路:先由小模型(draft)生成多个候选 token,再由大模型一次性检查这些候选是否合理。如果大模型认可,就一次接收多个 token;如果不认可,再退回到分歧点继续正常生成。
它与 prefix caching 的区别:
- Prefix Caching:"不要重复做已经做过的计算"
- Speculative Decoding:"先用便宜路径猜一部分,再让大模型批量确认"
5.5 Admission Control 与 Fairness
线上服务不只追求平均性能,还要考虑稳定性与多租户体验:
- Admission Control:当队列太长或资源太紧时,限流、排队或降级
- Fairness:避免某些大请求或高频用户长期占住资源
- Tail 控制:对特别长、特别重的请求做隔离或限额
最终结论:推理系统优化最终是一个服务治理问题,而不仅是算子问题。
六、核心参考资料索引
原文共引用 26 篇参考资料,以下按主题精选最核心的 12 篇:
🔬 PagedAttention 原始论文
https://arxiv.org/abs/2309.06180
vLLM 的基石,将操作系统虚拟内存思想引入 KV Cache 管理,实现非连续存储与高效共享。
🔬 FlashAttention / FlashAttention-2
https://arxiv.org/abs/2205.14135 · https://arxiv.org/abs/2307.08691
IO-Aware 的 Attention 实现,通过分块计算和 SRAM 复用减少 HBM 读写,既提速又省显存。
🔬 Orca: Continuous Batching 经典系统
https://www.usenix.org/conference/osdi22/presentation/yu
OSDI'22,最早系统性地提出 iteration-level scheduling 和 selective batching,奠定现代 LLM 推理调度基础。
📘 Inside vLLM: Anatomy of a High-Throughput LLM Inference System
https://vllm.ai/blog/anatomy-of-vllm
vLLM 核心贡献者对 V1 引擎的系统性拆解,从 Engine Core 到分布式 Serving 的完整数据流。
🔬 Sarathi-Serve: Chunked Prefill
https://arxiv.org/abs/2403.02310
提出将长 prefill 拆分为小块,与 decode 交错执行,减少 decode 请求的排队延迟。
🔬 DistServe: Disaggregated Prefill/Decode
https://www.usenix.org/conference/osdi24/presentation/zhong-yinmin
OSDI'24,将 prefill 和 decode 分离到不同资源池,各自优化 TTFT 和 ITL。
🔬 P/D-Serve: Prefill-Decode Disaggregation
https://arxiv.org/abs/2408.08147
系统性分析 P/D 分离的收益与代价,提出动态迁移策略。
🔬 Preble: Cache-aware Scheduling
https://arxiv.org/abs/2407.00023
将 prefix caching 信息纳入调度决策,实现缓存感知的请求路由。
🔬 Speculative Sampling
https://arxiv.org/abs/2302.01318
投机解码的数学基础,证明 accept/reject 机制保证与直接采样大模型统计等价。
🔬 Switch Transformers (MoE)
https://arxiv.org/abs/2101.03961
Google 提出的稀疏专家模型,通过路由模块实现万亿参数模型的简单高效稀疏化。
📘 vLLM Prefix Caching 设计文档
https://docs.vllm.ai/en/stable/design/prefix_caching.html
vLLM 官方对 prefix caching 的工程设计文档,包含 hash 策略、block 复用机制等细节。
🔬 Llumnix: Fairness in Serving LLMs
https://arxiv.org/abs/2406.03243
关注多租户场景下的公平调度与请求迁移,解决大请求拖慢整体延迟的问题。