模型推理优化
flowchart LR
A["模型推理优化"]
A --> B["分类:工程与生产"]
A --> C["关键词:AI"]
A --> D["关键词:推理"]
A --> E["关键词:vLLM"]
A --> F["关键词:量化"]
推理优化不是“把模型跑快一点”的小修小补,而是在延迟、吞吐、显存、成本、质量、复杂度之间持续做权衡。很多 AI 应用最后不是死在模型能力不够,而是死在推理太慢、太贵,或者一上量就扛不住。
延伸阅读:
- vLLM 文档 —— 高吞吐推理与 serving
- Hugging Face TGI 文档 —— Hugging Face 的推理服务框架
- TensorRT-LLM 文档 —— NVIDIA 推理优化栈
这篇文章会讲什么
在调用托管 API 时,很多人最开始只关心“能不能跑起来”。 但只要你开始面对这些问题,推理优化就会迅速从“进阶话题”变成“生死线”:
- 首字延迟太高,用户觉得系统像卡死
- 总生成时间太长,聊天体验断裂
- 一上量 GPU 就满,吞吐上不去
- 长上下文一来就爆显存
- 成本随着流量线性上升,毛利空间被吃光
- 明明模型效果不错,但服务侧就是跑不出商业上可接受的形态
本文会把推理优化拆成几个真正影响工程结果的维度来看:
- 延迟:用户要等多久,尤其是首 token 要等多久
- 吞吐:单机、单卡、单集群能扛多少并发
- 显存:模型权重、KV Cache、batch 竞争谁占了大头
- 成本:同样质量下,每个请求花多少钱
- 复杂度:这个优化值不值得引入额外系统负担
你会看到,很多流行术语背后其实都在解决一个共同问题:
如何让 GPU 少做无用功,让显存少浪费在低价值状态上,同时不把系统复杂度推得失控。
1. 推理优化真正优化的是什么
很多人第一次接触推理优化,会把它理解成“让模型更快”。这太笼统了。 更准确地说,推理优化通常在同时优化四件事:
| 目标 | 真正关心的问题 | 常见指标 |
|---|---|---|
| 首字延迟 | 用户多久看到第一段输出 | TTFT(Time To First Token) |
| 总体完成时间 | 一次请求多久结束 | End-to-End Latency |
| 吞吐 | 单位时间能处理多少请求 | QPS / TPS / tokens per second |
| 资源效率 | 同样硬件上能跑多大负载 | GPU 利用率、显存占用、单位请求成本 |
这里最容易混淆的是:低延迟和高吞吐并不总是同一个目标。
例如:
- 为了吞吐做更激进的 batching,可能会让单个请求等更久
- 为了压低首字延迟减少 batch,又可能牺牲整体 GPU 利用率
- 为了上更小的量化模型降低成本,可能又会损失一点输出质量
所以推理优化第一步不是“上什么技术”,而是先问:
你现在的主要瓶颈,到底是首字延迟、总延迟、吞吐、显存,还是成本?
如果这个问题没答清,后面的优化很容易方向错。
2. 先理解推理的两段:Prefill 和 Decode
要理解为什么不同优化技术作用不一样,最有帮助的切分是把一次生成拆成两个阶段:
2.1 Prefill:先把已有上下文“吃进去”
也就是把输入 prompt、系统提示、历史对话、RAG 结果等上下文做一次前向计算,生成后续解码所需的状态。
这个阶段的特点通常是:
- 输入 token 多
- 并行度高
- 更偏“算力密集”
- 对长上下文尤其敏感
2.2 Decode:再一个 token 一个 token 地往后写
这是真正生成输出的阶段。由于是自回归生成,每次只新增一个 token,再继续下一个。
这个阶段的特点通常是:
- 天然带有串行性
- 每一步计算量相对小,但次数很多
- 更容易受内存访问、KV Cache 管理和调度策略影响
- 用户最直接感受到的延迟,往往发生在这里
这两个阶段的优化重点并不一样:
| 阶段 | 常见瓶颈 | 典型优化 |
|---|---|---|
| Prefill | 长上下文计算重、显存压力高 | Chunked prefill、FlashAttention、输入裁剪 |
| Decode | 自回归串行、KV Cache 占用大、调度效率低 | Continuous batching、Paged KV、Speculative decoding |
很多文章一上来就讲 batching、量化、KV Cache,但如果没有先区分 prefill 和 decode,读者会很难理解为什么某些优化对“长输入”更有效,另一些则主要改善“长输出”。
3. 先别急着上花活:最常见、也最被低估的优化是“少生成”
很多团队一谈推理优化,第一反应是:
- 上 vLLM
- 上量化
- 上 speculative decoding
- 上多卡并行
这些当然重要,但对很多业务来说,最先该做的优化不是系统级技巧,而是减少无意义 token。
OpenAI 的官方延迟优化文档就明确提到:生成 token 往往是延迟中的最大头,经验上减少 50% 输出 token,往往就能显著减少生成延迟。1
这件事看起来朴素,却非常工程化。因为很多请求之所以慢,不是因为模型不够快,而是因为系统让它说了太多废话。
典型方式包括:
- 让模型更短、更直接
- 用结构化输出代替长段自然语言
- 限制 max tokens
- 对不同场景设置不同输出预算
- 能模板化的部分不要让模型自由发挥
- 对中间步骤和最终展示结果分层,别让所有内容都流向用户
这类优化的好处是:
- 同时降低延迟和成本
- 不依赖特定框架
- 不引入额外推理复杂度
- 对托管 API 和自托管都成立
所以本文后面会讲很多“硬核优化”,但先给一个更实用的经验:
如果你的系统还在让模型每次输出一大段并不必要的文本,那么先做输出约束,收益往往比上复杂推理技巧更直接。
4. Batching:不是“把请求凑一凑”这么简单
先看定义:Batching 的核心不是批处理本身,而是让 GPU 在更多时间里保持高利用率。
GPU 擅长并行。如果你一次只喂一个很小的请求,很多算力其实是闲着的。 Batching 的思路就是:把多个请求一起算,让同一轮计算服务更多请求。
4.1 三种常见 batching 模式
| 模式 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| Static Batching | 凑齐固定数量后再执行 | 实现简单 | 等待明显,长短请求互相拖累 |
| Dynamic Batching | 在时间窗口内聚合请求 | 比 static 更灵活 | 仍有等待窗口 |
| Continuous Batching | 请求可动态进出批次 | 吞吐高、调度更细 | 实现复杂,对运行时要求高 |
其中真正改变 open-source LLM serving 体验的,是 continuous batching。
它的关键不在于“批次更大”,而在于:
- 新请求不必非等整个 batch 结束
- 已完成请求可以及时退出
- 不同长度请求能更细粒度地共存
- GPU 更少出现“为了等最慢那个请求而空转”的情况
vLLM 官方文档把 continuous batching 和 PagedAttention 列为其核心性能特性之一。2 Hugging Face TGI 文档也明确把 continuous batching 作为提高总体吞吐的重要机制。3
4.2 为什么 static batching 在 LLM 场景里不够好
传统批处理常常默认同构任务:输入长度差不多,执行时间差不多。 但 LLM 请求通常不是这样:
- 有的输入 20 token,有的输入 20,000 token
- 有的只生成 50 token,有的要生成 2,000 token
- 有的请求很快结束,有的会占着 KV Cache 很久
如果你还沿用静态 batch 思维,就会出现:
- 短请求被长请求拖慢
- 已完成请求的资源不能及时释放
- batch 内部“木桶最短板”问题很明显
这也是为什么 LLM serving 的 batching,本质上更像一个调度问题,而不只是一个“队列合并”问题。
4.3 Batching 带来的 trade-off
Batching 很强,但它不是白送的。
好处
- 显著提高 GPU 利用率
- 提高整体吞吐
- 降低单位请求边际成本
- 在多用户场景下更容易打满硬件
代价
- 可能牺牲单请求延迟
- 调度复杂度上升
- 对 KV Cache 管理要求更高
- 更容易出现“批次被长上下文绑架”的问题
所以如果你的场景是:
- 高并发、多用户、在线服务 那么 continuous batching 往往非常关键。
但如果你的场景是:
- 极低并发
- 单用户本地推理
- 对单次首字延迟极度敏感 那 batching 的收益可能没有想象中大,甚至要谨慎配置。
4.4 工程上该怎么判断 batching 是否真的有效
很多团队会看平均延迟,这其实不够。
更值得盯住的是:
- TTFT 是否变差
- P95 / P99 延迟是否失控
- tokens/s 是否显著提升
- 单卡吞吐是否明显变高
- GPU 利用率是否更平稳
- 长请求是否把短请求“拖死”
因为一个常见误区是: 平均吞吐上去了,但用户体验其实变差了。
这在对话系统里尤其常见。
5. KV Cache:推理速度和显存压力的交汇点
先看定义:KV Cache 通过复用历史 token 的 Key / Value,避免每生成一个新 token 都把旧上下文全部重算;它是解码加速的基础,也是显存管理的核心战场。
5.1 为什么 KV Cache 必不可少
在自回归生成里,每个新 token 都要“看到”前面的上下文。 如果不缓存历史 token 的中间状态,那么每生成一步都得把前文再算一遍,这会浪费大量计算。
KV Cache 的作用就是把历史 token 的注意力相关状态存下来,后续解码直接复用。
这件事带来的收益非常大,但代价也很直接:
你省下了重复计算,却把大量状态留在了显存里。
所以在 LLM serving 里,很多时候真正限制并发的,不是参数权重,而是 KV Cache。
5.2 为什么长上下文和长输出都会把 KV Cache 撑大
KV Cache 的体积会随着序列长度增长。 而这里的“序列长度”既包括输入上下文,也包括已经生成出来的输出。
这意味着:
- 长 prompt 会更贵
- 长历史对话会更贵
- 长文档 RAG 会更贵
- 长输出也会持续把缓存顶高
这意味着,KV Cache 不是只在“超长上下文模型”里重要。 只要你的系统有以下任一特征,它就会很快成为核心问题:
- 多轮对话历史不清理
- 企业知识问答喜欢塞很多召回片段
- 输出很长
- 并发高
- 多用户共享同一张卡
5.3 PagedAttention / Paged KV:为什么它会成为 vLLM 的代表特性
vLLM 官方文档把 PagedAttention 作为核心能力,核心思想是更细粒度地管理 KV memory,而不是把每个请求的 KV Cache 粗暴地当成一整块连续内存。2
它解决的问题并不神秘,主要是这几个:
- 不同请求长度不同,连续分配容易碎片化
- 请求会动态进入和退出,内存需要灵活回收
- Continuous batching 需要更好的请求级状态管理
所以 PagedAttention 的重要性不只是“一个更快的 attention kernel”,而是:
它让 KV Cache 从“容易碎片化、难调度的包袱”,变成更适合 serving 场景的内存对象。
这也是为什么在高并发、长上下文、多请求混跑时,vLLM 经常比朴素 Transformers serving 更能打。
5.4 KV Cache 的工程误区
误区一:只盯模型参数,不盯运行时状态
很多人会问“7B 模型能不能放进这张卡”,但真正上线后发现问题在于:
- 权重放得下
- KV Cache 放不下
- 一并发就 OOM
所以“模型能跑”和“模型能稳定 serving”是两回事。
误区二:上下文窗口开得越大越好
支持 128K 上下文不代表你就应该默认塞到这么长。 更长窗口意味着:
- prefill 更慢
- KV Cache 更大
- 并发更低
- 成本更高
大窗口是能力,不是默认策略。
误区三:多轮对话历史永远不裁剪
很多聊天系统会不断累计历史上下文,看起来更“懂上下文”,但代价是:
- 延迟越来越高
- 显存越来越紧
- 历史噪声干扰当前回答
- 并发能力持续下降
所以对话系统要有明确的历史管理策略:摘要、裁剪、分层记忆、外部记忆,而不是无限堆 token。
6. Streaming:它不让模型更快,但能显著改善“感觉更快”
先看定义:Streaming 不是缩短实际推理时间,而是更早把结果交到用户手里,从而改善感知延迟。
OpenAI 官方文档明确支持基于 Server-Sent Events (SSE) 的流式响应;默认情况下,模型会先生成完整输出再一次性返回,而开启流式后,可以边生成边返回内容。4
6.1 为什么 Streaming 几乎是对话产品的默认项
在聊天类产品里,用户对“什么时候看到第一个字”往往比对“总共多久结束”更敏感。
这也是为什么 TTFT(Time To First Token) 通常比平均总耗时更影响体验:
- 2 秒看到第一个字、8 秒结束,体感可能还可以
- 8 秒空白、然后一次性吐完,体感通常很差
所以 streaming 的价值不在于 benchmark 上更快,而在于:
- 降低空白等待感
- 提前暴露系统正在工作的信号
- 允许用户更早判断答案是否跑偏
- 让取消变得更自然
6.2 SSE 还是 WebSocket
| 方式 | 适用 | 特点 |
|---|---|---|
| SSE | 单向流式输出 | 简单、浏览器友好、很适合文本流 |
| WebSocket | 双向实时交互 | 更灵活,适合多事件、多状态协同 |
大多数纯聊天输出场景,SSE 已经够用。 只有当你需要:
- 双向事件
- 多阶段状态推送
- 工具调用过程可视化
- 协作式实时交互
WebSocket 才更值得引入。
6.3 Streaming 的边界
Streaming 当然重要,但也不要把它神化。
它解决不了这些问题:
- 总推理真的太慢
- 首字延迟本身就高
- 模型一直在输出废话
- 工具调用前半天没结果
从工程上看,streaming 是体验优化,不是底层性能优化。 如果系统本身过慢,streaming 只能缓解,不能根治。
7. 量化(Quantization):最现实、也最容易被误用的优化手段
先看定义:量化通过使用更低精度表示权重,降低显存占用、提高推理效率,但它不是“免费午餐”,收益与损失都要结合模型、硬件和任务来评估。
vLLM 官方文档明确列出 GPTQ、AWQ、INT4、INT8、FP8 等量化支持。2 TGI 文档也列出了 bitsandbytes 与 GPT-Q 量化支持。3
7.1 量化到底在换什么
量化的核心不是神秘算法,而是很朴素的工程交换:
- 用更少的 bit 表示权重
- 减少显存占用和带宽压力
- 部分情况下提高吞吐、降低成本
- 用一定的数值误差换取资源效率
但这里有两个常见误解。
误解一:量化一定会让推理更快
不一定。
在真实系统里,量化的收益取决于:
- 硬件是否对该精度有良好支持
- 内核实现是否成熟
- 当前瓶颈是算力还是内存带宽
- 模型架构是否适合该量化方案
有时量化最大的收益不是“单 token 更快”,而是:
- 同样显存能放更大模型
- 同样硬件能跑更高并发
- 原本跑不起来的模型终于能稳定跑
误解二:量化影响都很小
也不一定。
不同任务对量化误差的敏感度不同:
- 开放式聊天可能比较能容忍轻微退化
- 结构化抽取、代码生成、复杂 reasoning 更可能暴露细小质量问题
- 工具调用或 JSON 输出场景,对格式稳定性可能更敏感
所以量化从来不是“看 benchmark 差不多就上”。 更稳妥的做法是:在你的真实任务集上评估,而不是只看通用榜单。
7.2 常见量化格式应该怎么理解
| 格式 / 精度 | 典型特点 | 适合关注的点 |
|---|---|---|
| FP16 / BF16 | 质量稳定、常见基线 | 最适合作为对照组 |
| INT8 | 比较稳妥,收益通常不错 | 适合作为生产初始尝试 |
| INT4 | 显存收益更大 | 更要看质量回归 |
| GPTQ | 权重量化常见方案 | 社区使用广 |
| AWQ | 强调激活感知 | 在很多模型上质量表现较稳 |
| GGUF | 本地生态常见 | 更适合边缘 / 本地运行 |
与其背格式名,不如记住一个更工程化的原则:
量化选型不是学术选择题,而是“你的模型、你的硬件、你的任务”三者共同决定的结果。
7.3 什么时候量化最值得优先尝试
通常是这些场景:
- 显存明显不够
- 希望把更大模型塞进现有硬件
- 并发受显存约束而不是受 QPS 约束
- 单位请求成本太高
- 希望在消费级 GPU 或边缘设备上部署
很多团队第一次真正感受到量化价值,不是因为单次请求快了 30%,而是因为:
- 从“只能跑 7B”变成“终于能跑 14B / 32B”
- 从“单卡只能扛 2 并发”变成“能扛 6 并发”
- 从“必须上更贵 GPU”变成“现有卡还能再撑一阵”
7.4 量化上线前最该检查什么
不要只看“主观感觉回答还行”。
更应该看:
- 结构化输出成功率
- 工具调用正确率
- 长上下文稳定性
- P95 / P99 延迟
- OOM 频率
- 实际 tokens/s
- 单位请求成本
- 关键任务的质量回归
因为量化的收益,往往不是平均值决定上线,而是尾部失败模式决定能不能上线。
8. Speculative Decoding:不是魔法加速,而是“用便宜计算猜、用贵计算验”
先看定义:Speculative decoding 的核心思想是先让一个更便宜的机制草拟若干 token,再由目标模型验证并尽量批量接受,从而减少高成本模型逐 token 推进的压力。
vLLM 官方文档把 speculative decoding 列为重要优化特性之一。2 TensorRT-LLM 官方文档也提供了 speculative decoding 的支持与示例,包括 MTP、Eagle、N-Gram 等配置方式。5
8.1 为什么它会有效
LLM 解码慢,很大原因在于“一个 token 一个 token 地认真生成”。
Speculative decoding 的思路是:
- 先用更便宜的 draft 机制提出一小段候选 token
- 再由目标模型批量验证
- 能接受的部分直接跳过逐 token 生成
- 不接受的部分再回退到目标模型继续生成
它本质上是在做一件事:
尽可能把昂贵模型的“逐 token 串行劳动”压缩掉一部分。
8.2 常见实现不止“小模型 + 大模型”这一种
很多入门文章把 speculative decoding 讲成固定套路:
- 一个小模型生成 draft
- 一个大模型验证
这没错,但现在工程实现已经不止这一种。 TensorRT-LLM 文档里就可以看到不同 speculative 配置方式,包括 MTP、Eagle3、N-Gram 等。5
这提醒我们一件事: speculative decoding 的核心不是某个具体算法名,而是“先猜后验、批量接受”这一类思想。
8.3 它为什么没有成为所有系统的默认配置
因为它确实有代价。
好处
- 在合适条件下明显降低解码延迟
- 对大模型尤其有吸引力
- 能改善 tokens/s
代价
- 系统复杂度更高
- 需要额外 draft 机制或额外模型
- 验证接受率不高时收益会下降
- 不同模型组合效果差异大
- 调参和运维成本更高
所以 speculative decoding 很像一种“高阶优化”:
- 当你已经把基础优化做得差不多
- 你的瓶颈明确在 decode
- 模型规模足够大
- 低延迟收益足够值钱
它才更值得认真投入。
8.4 一个常见误区:把 speculative decoding 当成“无损 2-3x 加速按钮”
现实里没有这么简单。
它的收益会受很多因素影响:
- draft 质量如何
- 接受率高不高
- 目标模型和 draft 模型是否匹配
- 任务分布怎样
- 输出风格是否容易预测
- 框架实现是否成熟
所以它更像“值得实验的高价值优化方向”,而不是人人都该先上的默认项。
9. 框架对比:别只看名气,要看你要解决什么问题
很多文章喜欢简单下结论:“生产就 vLLM,本地就 Ollama。”这不算错,但太薄。
更实用的比较方式是:这些框架分别在优化什么,代价是什么,适合什么阶段。
9.1 vLLM:高吞吐 serving 的主流选择
vLLM 官方文档强调的核心能力包括:
- PagedAttention
- Continuous batching
- Quantization
- FlashAttention / FlashInfer 集成
- Speculative decoding
- Chunked prefill2
这意味着它最强的地方不是“能跑模型”,而是:
把 open-source LLM serving 里最关键的运行时问题做成了比较系统的解决方案。
它尤其适合:
- 高并发在线服务
- 多请求混跑
- 长短请求并存
- 对吞吐、资源效率很敏感的生产场景
它的代价则是:
- 你要接受一套更偏 serving runtime 的心智模型
- 某些模型支持、特性兼容、升级节奏需要跟社区演进
- 对调度、显存和部署理解要求更高
9.2 TGI:仍然实用,但定位要看清
Hugging Face TGI 文档明确提到:TGI 已进入 maintenance mode,未来主要接受轻量维护、bug 修复和文档改进,并推荐用户关注 vLLM、SGLang、llama.cpp、MLX 等引擎方向。3
这点很重要,因为很多旧文章还会把 TGI 当作“和 vLLM 并列的长期主战选择”,这在今天已经不完全准确。
TGI 仍然有价值,尤其是:
- 你已经深在 Hugging Face 生态
- 想快速部署已有模型
- 规模不是极端大
- 需要相对成熟的生产组件与指标体系
但如果是新系统、新部署,且你非常看重持续性能演进,那么需要认真考虑它的维护状态。
9.3 TensorRT-LLM:不是通用默认项,而是 NVIDIA 栈上的高性能路线
TensorRT-LLM 官方文档的定位非常清楚:为 NVIDIA GPU 上的 LLM inference 构建高性能 TensorRT engine,并提供大量优化能力。6
它通常更适合:
- 你明确运行在 NVIDIA GPU 上
- 对性能上限非常敏感
- 有工程能力吃下更重的部署复杂度
- 愿意为极致性能去适配硬件栈
它的优势是:
- 性能上限高
- 对 NVIDIA 硬件优化深
- speculative、KV、并行等高级能力很完整
它的代价也很明显:
- 学习和运维门槛更高
- 更偏平台工程,不一定适合所有应用团队
- 对硬件、驱动、构建链路要求更强
9.4 Ollama / llama.cpp 一类:不是“玩具”,而是另一个优化方向
这类框架的重点通常不是“云端极限吞吐”,而是:
- 本地部署
- 较低门槛
- 开发调试方便
- 边缘和个人设备友好
它们适合:
- 开发环境
- Demo
- 个人工具
- 边缘设备
- 对部署简洁性比吞吐极限更在乎的场景
所以别用数据中心 serving 的标准去否定这类框架,它们解决的是另一类问题。
10. 一个更工程化的选型表
| 框架 | 更擅长解决什么 | 更适合谁 | 主要代价 |
|---|---|---|---|
| vLLM | 高吞吐、多请求 serving、显存效率 | 在线生产服务 | 运行时理解门槛更高 |
| TGI | Hugging Face 生态内快速部署 | 已有 HF 体系用户 | 已进入维护模式 |
| TensorRT-LLM | NVIDIA 上的极致性能 | 平台团队、性能导向团队 | 部署和优化复杂 |
| Ollama / llama.cpp | 本地部署、简单可用 | 开发者、本地工具、边缘场景 | 吞吐和云端大规模 serving 不是主目标 |
如果一定要给一个不那么武断、但更接近现实的建议,大概是:
- 多租户在线生产、高并发服务:优先考虑 vLLM
- NVIDIA 栈、极致性能、团队工程能力强:认真评估 TensorRT-LLM
- 已深度依赖 Hugging Face 生态,且短期内想快速上线:TGI 仍可用,但要意识到维护模式
- 本地开发、单机实验、个人产品或边缘部署:Ollama / llama.cpp 往往更顺手
11. 不同优化技术分别解决什么问题
| 技术 | 主要改善 | 常见收益 | 常见代价 | 更适合的场景 |
|---|---|---|---|---|
| Streaming | 感知延迟 | 更早看到输出 | 不减少总算时 | 聊天、交互式应用 |
| Batching | 吞吐、GPU 利用率 | 单卡吞吐上升 | 可能拉高尾延迟 | 多用户在线服务 |
| Continuous Batching | 吞吐 + 调度效率 | 比静态 batch 更灵活 | 实现复杂 | 高并发 serving |
| KV Cache | 解码效率 | 避免重复计算 | 显存压力大 | 所有自回归生成 |
| Paged KV / PagedAttention | 显存利用率、调度 | 更少碎片、更易混跑 | 依赖运行时实现 | 多请求 + 长上下文 |
| 量化 | 显存、成本、吞吐 | 更高资源效率 | 可能有质量损失 | 显存受限、成本敏感 |
| Speculative Decoding | 解码延迟 | 加快生成 | 系统复杂度提升 | 大模型、低延迟需求 |
| 输出裁剪 / 约束 | 延迟 + 成本 | 立竿见影 | 需改产品和提示设计 | 几乎所有场景 |
这张表里最想强调的是最后一行。 很多团队会优先做系统层优化,却忽略了最便宜、最稳的输出约束。
12. 优化顺序:不是技术越高级越该先做
如果你的系统已经上线,推理优化更建议按下面顺序来想。
第一步:先确认瓶颈到底在哪
先看这些指标:
- TTFT
- 平均 / P95 / P99 延迟
- tokens/s
- GPU 利用率
- 显存占用
- OOM 频率
- 单请求成本
- 输出 token 分布
很多时候一测就会发现,你以为是推理框架不够强,实际是:
- 输出太长
- 历史上下文太长
- RAG 塞得太多
- tool loop 太长
- 某些请求本来就不该走大模型
第二步:先做低风险、高收益优化
通常包括:
- Streaming
- 输出裁剪
- max tokens 控制
- 历史对话压缩
- Prompt 去冗余
- 小任务换小模型
- 基本缓存
这一步常常已经能解决一大半“看起来是推理问题”的问题。
第三步:再做运行时优化
包括:
- 更成熟的 serving 引擎
- continuous batching
- KV cache 优化
- chunked prefill
- 更合理的并发参数
- 显存与 batch 配置调优
这一步开始真正进入“推理系统工程”。
第四步:再考虑高阶优化
例如:
- 量化到更低精度
- speculative decoding
- 多卡并行
- 更复杂的调度策略
- 特定硬件栈深度优化
因为这些优化虽然收益可能很大,但也更容易带来额外复杂度和运维负担。
13. 一些常见误区
误区一:看到别人快 10 倍,就以为自己也能快 10 倍
推理 benchmark 很依赖前提:
- 模型多大
- 输入多长
- 输出多长
- 单请求还是多请求
- 单卡还是多卡
- 首字延迟还是总吞吐
- 是否做量化
- 是否 batch 打满
脱离这些条件谈“比谁快几倍”,参考价值很有限。
误区二:只看平均值,不看尾延迟
在线系统真正伤用户体验的,经常不是平均延迟,而是:
- 某些请求特别慢
- 长请求拖住短请求
- 高峰时 P99 爆炸
- GPU 看起来很忙,但用户感知很差
误区三:为了追求 benchmark,把系统复杂度堆过头
有些优化在单机压测里非常漂亮,但一上生产会带来:
- 更复杂的部署链路
- 更难定位的问题
- 更高的回滚成本
- 更脆弱的兼容性
推理优化不是谁堆的技巧多谁赢,而是谁在性能、稳定性、复杂度之间找到可持续平衡。
误区四:把“能跑长上下文”当成“应该默认给长上下文”
支持长上下文是能力,不是默认策略。 真正成熟的系统,反而会更谨慎地决定:
- 什么场景值得给这么长上下文
- 哪些信息可以外部记忆化
- 哪些历史应该摘要而不是原样保留
- 哪些请求根本不值得进大窗口模型
小结
推理优化的本质,不是“把模型 API 包得更花”,而是围绕几个核心矛盾不断做工程权衡:
- GPU 很强,但不能闲着,所以要做 batching 和更好的调度
- 重复计算很浪费,所以要做 KV Cache
- 显存很贵,所以要做更细粒度的缓存管理和量化
- 解码天然串行,所以才会有 speculative decoding 这类高阶优化
- 用户不只关心总时间,更关心什么时候看到第一个字,所以 streaming 几乎是交互式产品的标配
- 很多系统真正的瓶颈不是框架,而是 token 太多、上下文太长、任务路由太粗糙,所以产品约束和架构策略同样属于推理优化的一部分
如果要把这篇文章浓缩成一句话,大概就是:
推理优化不是单点技术清单,而是一套围绕延迟、吞吐、显存、成本和复杂度的系统工程。
也因此,真正成熟的优化顺序通常不是“先上最酷的技术”,而是:
- 先搞清楚瓶颈
- 先减少无意义 token
- 再把运行时调度和显存管理做好
- 最后再考虑量化、speculative decoding、多卡并行这些更高阶的手段
这比一上来就追求“benchmark 漂亮”更接近真实生产。