
问题:naive 推理浪费了什么
用 transformers 库直接起一个推理服务(naive 方式),吞吐低下的原因有两个结构性浪费。显存浪费:为每个请求预分配「最大上下文长度」的 KV Cache 空间——实际请求的生成长度参差不齐(有的 50 token 就完,有的要 2000),按最大值预留的显存大量闲置;显存浪费直接限制了并发数(能同时服务的请求数)。批处理浪费:静态批处理(static batching)攒一批请求一起推理,要等整批全部完成才能进下一批——同一批里短请求早完成了却在陪跑(GPU 算力花在「为已完成的请求填充」上)。这两项浪费叠加,GPU 的有效利用率常常不到 20%。
PagedAttention:把操作系统思想搬进显存
vLLM(2023)的第一项创新 PagedAttention 直接借用了操作系统虚拟内存的思想:把 KV Cache 切成固定大小的块(block,通常 16 个 token 一块),按需分配——请求的 KV 不再占一块连续大内存,而是「按需一块块申请、逻辑上用块表串联」。收益有三:消灭预留浪费(请求用多少分配多少,显存利用率从 20–40% 提到 90%+)、相同的显存装下更多并发(吞吐提升的第一个来源)、前缀共享顺带解决(相同 system prompt 的请求共享前缀块——公共前缀只存一份,多请求引用同一块,本库 KV Cache 篇讲过的前缀缓存在这里有了系统级实现)。
块管理的额外红利是写时复制(Copy-on-Write):多个请求共享前缀块时,一旦某个请求的生成开始「分叉」(前缀相同、后文不同),分叉的那一块才复制一份——采样 beam search 与并行采样的显存开销因此大幅下降。
连续批处理:请求的动态编排
第二项创新 continuous batching(连续批处理/迭代级批处理) 解决静态批的陪跑问题:每一步迭代都动态调整批的组成——完成的请求立即退出、新请求立即插入空位,GPU 的每一步都在处理「活跃请求的当前 token」。效果:GPU 不再有「陪跑」周期,吞吐提升数倍;同时新请求的加入不需要等批满(首 token 延迟大幅降低)。
flowchart LR
subgraph S["静态批处理"]
A1["批:请求A B C"] --> A2["A 完成,仍在批内陪跑"] --> A3["全完成→下一批"]
end
subgraph C["连续批处理"]
C1["每步迭代:完成的退出"] --> C2["新请求随时插入"] --> C3["GPU 永远满载活跃请求"]
end
两项创新叠加的结果:同样的 GPU,vLLM 的吞吐是 naive 方式的 8–24 倍(官方口径的实测区间),服务成本因此降一个数量级——这是「推理引擎」这个品类存在的根本理由。
vLLM 生态的位置与同类
vLLM 已成为开源推理引擎的事实标准,但它不是唯一选择,按场景分野。vLLM:高吞吐服务端的标准答案——OpenAI 兼容 API、支持主流模型架构、社区最活跃;它的优化重心在「批量服务的吞吐」。SGLang:后起之秀,在结构化输出(约束解码)与缓存复用(RadixAttention 的前缀树缓存)上有独到优化,部分场景吞吐超过 vLLM。TensorRT-LLM:NVIDIA 官方的极致性能路线——针对特定卡型深度编译优化,性能上限最高,代价是绑定 NVIDIA 与编译部署的复杂度。llama.cpp:本地与边缘设备的可达性路线(本库专篇),与服务端引擎的战场不同。选型的粗规则:高并发服务 → vLLM/SGLang;极致性能且绑定 NVIDIA → TensorRT-LLM;本地/边缘 → llama.cpp。
什么时候不需要 vLLM:杀鸡不用牛刀
推理引擎不是万金油,三类场景用了反而添乱。单用户本地使用:没有并发就没有「批处理」可言,llama.cpp 更简单高效。极低频调用:一天几十次请求的服务,naive 方式完全够用——引擎的收益在规模化后才兑现。原型与实验:研究提示词与采样参数的阶段,transformers 直接调用最透明——引擎的调度层会「藏住」细节。判断式一句话:有并发、要省钱、上生产——用引擎;单用户、低频、做实验——用最简单的。以及一个部署提示:vLLM 的显存占用要显式配置(gpu_memory_utilization 参数,默认预留 90% 显存)——同一张卡上跑别的服务时要调低,否则会 OOM 互杀。
推理引擎的进阶特性:前缀缓存、投机解码与并行调度
vLLM 类引擎的进阶特性决定了生产部署的最终性能,三个关键特性展开。前缀缓存(automatic prefix caching):相同前缀的请求共享 KV 块(PagedAttention 的块结构天然支持)——开启后,共享长 system prompt 的服务(智能体、RAG)吞吐提升显著(前缀部分零计算);代价是块管理复杂度上升(缓存淘汰策略),需要在引擎参数里显式开启并调「缓存占显存的比例」。投机解码(speculative decoding,本库专篇):用小模型先猜、大模型验证的加速——vLLM 已集成(speculative config),对延迟敏感(低并发、重体验)的场景收益明显(1.5–3 倍提速),对吞吐敏感(高并发)的场景收益有限(批处理已经填满 GPU)。调度策略:vLLM 的调度器参数决定「公平与效率」的取舍——长请求与短请求混跑时的抢占策略、用户级的公平调度、优先级队列——生产部署的调优重点常不在模型侧而在调度侧(不同业务的 SLA 不同)。部署形态的选择:裸引擎(自建 API 层)、推理框架封装(如 Ray Serve、BentoML 的集成)、以及一站式平台——自建最灵活、封装省心、平台最贵。选型的元原则:先明确自己的 SLA(TTFT 要求?吞吐要求?成本上限?),再反推引擎与特性配置——推理引擎的调优没有通用最优解,只有「SLA 约束下的最优解」;把 SLA 写成数字(TTFT < 2s、吞吐 > X tok/s、月成本 < $Y),所有调参决策就有了裁判。
推理引擎的监控与容量规划:从单卡到集群的视角
推理引擎上生产后的第二阶段工作是监控与容量规划——引擎跑起来只是开始,持续运营需要三套仪表。仪表一:性能仪表。TTFT 与 TPOT 的分位数分布(P50/P95/P99——均值骗人,长尾才是体验杀手)、GPU 利用率(compute 与 memory 两条曲线——瓶颈在算力还是显存一目了然)、批处理的排队深度(请求等待编排的时间——排队涨 = 容量不足的前兆)。仪表二:成本仪表。每请求的平均 token 数(输出失控的检测——某些 prompt 模式导致啰嗦输出)、缓存命中率(prompt 结构改坏的检测)、单请求成本的分位数(P95 的成本尖峰——超长输出的请求在拉高账单)。容量规划的方法:以「目标并发 × 目标 TTFT」反推所需的显存与卡数(本库显存账单的服务器版),留 30% 的余量应对峰值;扩容的触发线设在 P95 排队深度或 TTFT 的告警阈值。一个生产环境的常见教训:推理服务的「容量不足」往往不是均匀变慢,而是长 prompt 请求的突发(一个 100K 上下文的请求占住显存与 prefill 算力,把全服务的延迟打爆)——对策是请求级的长度限制与优先级隔离(长短请求分流到不同实例)。监控的三套仪表 + 容量的余量纪律,是推理服务从「能跑」到「能扛」的分界线。
推理引擎的对比实测方法论:自己动手测吞吐
推理引擎的选型(正文)最可靠的方式是自己实测,一份标准的实测方法论值得给出。测试环境的固定:同一张卡、同一模型与量化档位、同样的输入输出分布(固定一批真实 prompt)——变量只有引擎。测什么指标:吞吐(tokens/s,并发 1 与并发 N 各测——单流体验与批量吞吐分开看)、TTFT 分布(P50/P95——长 prompt 的首响)、显存占用(峰值——决定同卡并发上限)。测的流程:每个引擎用官方推荐的部署配置(别用「故意配错」的方式拉踩)、固定请求集压测 N 分钟、记录指标取稳定段。结果的解读纪律:差异在 10% 以内的引擎视为打平(换引擎不值得),显著差异先查配置(引擎的默认参数差异常常是主因)再下结论;以及用你的真实负载测——公开 benchmark 的负载分布(长文本?短问答?)与你的业务可能天差地别。实测两小时的成本,换来的是「适合你」而不是「据说最强」的引擎选型。
从 vLLM 到生产:一次最小上线的步骤清单
推理引擎的最小上线清单,七步从零到生产。第一步:环境——安装引擎、确认 CUDA 与驱动匹配。第二步:起服务——单卡起一个 vLLM 的 OpenAI 兼容服务(模型路径、量化档、显存比例参数)。第三步:冒烟测试——curl 发三个请求(短、中、长 prompt),确认正常返回与流式可用。第四步:压测——用基准脚本按目标并发压五分钟,记录吞吐与 TTFT。第五步:配网关——限流、鉴权、日志(不要把裸引擎暴露公网)。第六步:监控——引擎指标接入监控(吞吐、延迟、显存、队列),设告警线。第七步:容量备案——记录本次配置的性能基线与容量上限(下次流量增长时的扩容依据)。七步走完,一个「能扛事」的推理服务就绪——清单的价值在于顺序:先冒烟再压测、先网关再监控,跳步的代价都在生产环境偿还。
小结
naive 推理的浪费在显存(按最大长度预留 KV)与批处理(静态批的陪跑);PagedAttention 用分页管理把显存利用率提到 90%+(顺带解决前缀共享与 CoW),连续批处理用迭代级编排消灭陪跑——两项叠加让吞吐提升 8–24 倍,这是推理引擎品类的存在理由。生态分野:vLLM 是高吞吐服务的标准、SGLang 在结构化输出与缓存有独到优化、TensorRT-LLM 绑定 NVIDIA 求极致、llama.cpp 管本地边缘。判断式:有并发上生产用引擎,单用户低频实验用最简单的。下一篇转向训练侧的另一笔账:分布式训练的并行策略——模型大到单卡装不下时,怎么把训练摊到一堆卡上。
读者留言
COMMENTS 暂无还没有留言,来说第一句?