模型推理优化

推理延迟与成本是 AI 应用的两大痛点。Batching、KV Cache、量化、Speculative Decoding 等优化技术详解,以及 vLLM、TGI、TensorRT-LLM 等推理框架的工程取舍

22 min read Part of AI Engineering · Ch. 2

模型推理优化

flowchart LR
  A["模型推理优化"]
  A --> B["分类:工程与生产"]
  A --> C["关键词:AI"]
  A --> D["关键词:推理"]
  A --> E["关键词:vLLM"]
  A --> F["关键词:量化"]

推理优化不是“把模型跑快一点”的小修小补,而是在延迟、吞吐、显存、成本、质量、复杂度之间持续做权衡。很多 AI 应用最后不是死在模型能力不够,而是死在推理太慢、太贵,或者一上量就扛不住。


延伸阅读


这篇文章会讲什么

在调用托管 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 batchingPagedAttention 列为其核心性能特性之一。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 的思路是:

  1. 先用更便宜的 draft 机制提出一小段候选 token
  2. 再由目标模型批量验证
  3. 能接受的部分直接跳过逐 token 生成
  4. 不接受的部分再回退到目标模型继续生成

它本质上是在做一件事:

尽可能把昂贵模型的“逐 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、显存效率在线生产服务运行时理解门槛更高
TGIHugging Face 生态内快速部署已有 HF 体系用户已进入维护模式
TensorRT-LLMNVIDIA 上的极致性能平台团队、性能导向团队部署和优化复杂
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 太多、上下文太长、任务路由太粗糙,所以产品约束和架构策略同样属于推理优化的一部分

如果要把这篇文章浓缩成一句话,大概就是:

推理优化不是单点技术清单,而是一套围绕延迟、吞吐、显存、成本和复杂度的系统工程。

也因此,真正成熟的优化顺序通常不是“先上最酷的技术”,而是:

  1. 先搞清楚瓶颈
  2. 先减少无意义 token
  3. 再把运行时调度和显存管理做好
  4. 最后再考虑量化、speculative decoding、多卡并行这些更高阶的手段

这比一上来就追求“benchmark 漂亮”更接近真实生产。