推理优化与服务化
训练只发生一次,推理每天发生上亿次;服务的两难在于首 token 要快、吞吐要高,而两者常常互相拉扯
定义
推理优化与服务化是让训练好的模型在生产环境中快速、稳定且低成本地回应用户请求的工程实践。它要在两组相互冲突的指标之间取得平衡:单次请求的延迟(尤其是首个 token 的等待时间)与系统整体的吞吐量,手段包括 KV Cache 与分页注意力、连续批处理、投机解码以及请求路由与降级。
直观理解
把推理服务想成一家高峰期的餐厅。首 token 延迟是“客人坐下多久能端上第一道菜”,吞吐是“整晚一共能服务多少桌”。先给每桌上一道小菜能安抚客人,却占用后厨;一次给许多桌做同样的菜(批处理)能提高效率,却让每一桌都等得更久。真正的调度艺术,是在不饿跑客人的前提下把厨房塞满。
一次推理请求的服务流水线:预填充与逐 token 解码是两段不同的负载,调度器在其中不断重排批次
批大小如何同时影响延迟与吞吐(示意量级):吞吐随批大小上升后趋于饱和,而首 token 延迟近似线性增长——两者在同一张图上朝相反方向走
- 首 token 延迟 TTFT(ms)
- 吞吐(tokens/s)
工作原理
- 01
预填充与解码:两种截然不同的负载
处理整段输入(预填充)是一次性的大矩阵乘法,受算力限制;逐 token 生成(解码)每次只算一个 token,受显存带宽限制。两者的瓶颈不同,因此优化手段也不同——这也是首 token 延迟和后续 token 速度需要分开度量的原因。
- 02
KV Cache 与分页:别重复计算历史
自回归生成时,每一层的注意力都需要之前所有 token 的 Key 和 Value。把它们缓存下来,就能把每步的计算从“重算整段历史”降到“只算新 token”。但 KV Cache 随上下文与并发线性膨胀,于是 PagedAttention 借鉴操作系统的分页思想,按块管理这些缓存,减少显存碎片、提高并发上限。
- 03
连续批处理:让批次流动起来
静态批处理要等一批里最长的序列全部生成完才释放资源。连续批处理在每一步都重新组批——某条序列一结束就立刻填入新请求,GPU 几乎不再空转,这也是现代服务框架吞吐数倍于朴素实现的主要原因。
- 04
投机解码与降级:速度与稳妥的取舍
投机解码让一个小模型先草拟若干 token,再由大模型一次并行校验,命中时能显著提速。而当流量超过容量时,路由与降级策略——把请求分给更小的模型、限制上下文长度或排队——决定系统是优雅减速还是直接崩溃。
同一模型在相同硬件下各服务框架的相对吞吐(以朴素 HuggingFace Transformers 为 1×,示意量级):分页与连续批处理是数量级差距的来源
应用场景
- 在线对话服务:把首 token 延迟控制在数百毫秒内以维持流畅体验
- 高并发 API 网关:用连续批处理在同一硬件上服务更多用户
- 多模型路由与降级:按请求难度与预算选择不同规模的模型
- 容量与成本规划:估算给定延迟目标下每千 token 的硬件成本
常见误区
- 提高批大小能提升吞吐,却会拉高每个请求的延迟。吞吐与延迟不是同一件事,服务端必须明确以哪个为目标来调参。
- KV Cache 是显存的大头。上下文越长、并发越高,缓存越大;很多“上下文放不下”的问题其实不是权重太大,而是缓存把显存吃光了。
- 首 token 和后续 token 的瓶颈不同。用提高解码吞吐的办法往往改善不了首 token 延迟,反过来也一样,必须分别观测。
关键术语
- 首 token 延迟 TTFT
- 从请求发出到收到第一个 token 的时间
- KV Cache
- 缓存历史 token 的键值以减少重复计算
- 连续批处理
- 每一步动态重组批次,减少 GPU 空转
- 投机解码
- 小模型草拟、大模型并行校验以加速生成