LLM推理优化2026:批处理、KV缓存与投机解码详解
自托管模型每秒两个token,GPU却有一半闲着。推理加速的收益集中在那几个成熟技巧上,先搞清瓶颈在哪再动手。
📜 目录
LLM推理优化2026:批处理、KV缓存与投机解码详解
LLM 推理的瓶颈是内存不是算力。生成每个 token,都要把整个模型权重从显存搬到计算单元一遍。这一个事实解释了下面所有优化:它们全部在减少内存流量,或者复用已经加载过的数据。
先记住两个指标:TTFT(首个 token 的等待时间)和生成速度(每秒 token 数)。用户体感主要由这两个决定,而不是总吞吐。优化之前一定要先量,不然很容易调错方向。
一、先分清两种负载
单用户交互:瓶颈是延迟,一次只有一个请求,GPU 利用率低是正常的。优化方向是减少单步耗时:量化、KV 缓存、投机解码。
多用户服务:瓶颈是吞吐,GPU 闲着就是亏钱。优化方向是把请求批量塞进同一次前向传播。
很多"优化没效果"的案例,本质是拿批处理手段去优化单用户延迟,或者反过来。先判断自己属于哪一类。
二、技巧1:连续批处理(最大单项收益)
朴素服务一次只处理一个请求,等这个请求生成完再接下一个,GPU 大部分时间在等内存。连续批处理把多个请求的 token 计算交错进同一个批次,每当有请求结束就立刻补进新请求,不用等整批结束。
vLLM 的 PagedAttention 是把 KV 缓存按页管理,避免为每个请求预留最大长度的连续显存,这也是它能塞下更多并发的原因。SGLang 在调度和前缀复用上做了进一步优化。这两个项目在服务端场景相对朴素实现的吞吐提升常常是数倍到十几倍,具体数字随模型和并发数变化很大。
代价:单请求延迟会略微上升,因为要等批次凑齐。并发越高收益越大,单用户使用几乎没有收益。
三、技巧2:KV缓存与它的量化
生成每个 token 时,注意力需要看过前面所有 token。如果每次都重算,成本随序列长度线性增长。KV 缓存把这些中间结果存下来,代价是显存占用。
估算公式大致是:KV 缓存 ≈ 2 × 层数 × KV头数 × 头维度 × 序列长度 × 字节数。以 8B 级模型、8K 上下文为例,FP16 下占几个 GB 很常见,长上下文场景下 KV 缓存能超过权重本身。
所以现代引擎都支持 KV 缓存量化:
# llama.cpp:KV缓存量化到8位
./build/bin/llama-cli -m model.gguf -c 8192 -ctk q8_0 -ctv q8_0
# vLLM:fp8 KV缓存
vllm serve model --kv-cache-dtype fp8
KV 缓存压到 8 位通常质量影响很小,显存占用接近减半,能换来更长的上下文或更多并发。这是性价比很高的一步。
四、技巧3:投机解码
思路是让一个小的草稿模型先猜后面几个 token,大模型一次性并行验证这些猜测。猜对的部分直接接受,一次前向就产出多个 token;猜错就从第一个错的位置重来。
实际加速通常在一倍多到三倍之间,取决于草稿模型和主模型的一致性。文本越可预测(代码、格式化输出、模板化回答),加速越明显;开放式创作几乎没收益。
实现上有两种:配一个同系列的小模型做草稿,或者用 n-gram 查表做无模型草稿(llama.cpp 支持 lookup decoding)。后者不需要额外显存,适合模板化输出场景。注意投机解码不降低显存占用,反而要加载两个模型。
五、技巧4:前缀缓存
系统提示词、工具定义、few-shot 示例在每个请求里都一样。前缀缓存让这些 token 的 KV 只算一次,后续请求直接复用。
对 Agent 类应用收益极大——工具定义往往几千 token,每个请求都重算一遍是纯浪费。vLLM 有 prefix caching 开关,多数推理框架也支持。启用后 TTFT 常有可观下降,同时省下重复计算。
六、技巧5:权重量化
4 位权重的内存流量约为 16 位的一半,理论上生成速度接近翻倍。实际收益受反量化开销和硬件支持影响,通常能拿到明显提速但不是线性。GPU 上有专门的低比特计算支持时效果更好。
七、按场景选组合
| 场景 | 推荐组合 | 预期收益 |
|---|---|---|
| 个人本地单用户 | Q4量化 + KV缓存q8 + 前缀缓存 | 显存省一半,长上下文可用 |
| 小团队API服务 | vLLM/SGLang + 连续批处理 + 前缀缓存 | 吞吐数倍提升 |
| 模板化输出多(JSON、代码补全) | 上面 + 投机解码/lookup | 额外一到两倍 |
| 长文档问答 | KV缓存量化 + 分块处理 | 能跑更长上下文 |
八、参数速查
| 工具 | 关键开关 | 作用 |
|---|---|---|
| vLLM | --max-num-seqs |
同时处理的序列上限 |
| vLLM | --gpu-memory-utilization |
显存占用比例,留余量防OOM |
| vLLM | --enable-prefix-caching |
开启前缀复用 |
| llama.cpp | -ngl |
GPU卸载层数,没设满就是没用上显卡 |
| llama.cpp | -b/-ub |
批大小与微批大小,影响预填充速度 |
| llama.cpp | --flash-attn |
开启Flash Attention,长上下文提速 |
一个常见错误:-ngl 留了默认值,模型大半还在 CPU 上跑,然后抱怨显卡没用上。
九、优化纪律
每次只改一个变量,改完测三项:TTFT、生成速度、p95 延迟。跑至少几十个请求取分位数,单次测量噪声很大。别信博客里的绝对数字,包括本文的区间——它们只能告诉你量级。
常见问题
Q:并发上去了单个请求变慢正常吗? A:正常。批处理是用延迟换吞吐,只要 p95 延迟在可接受范围、总吞吐上升,就是赚的。
Q:KV缓存量化会掉质量吗? A:8 位影响通常很小,4 位在长上下文和精确回忆任务上可能出问题。先用 8 位。
Q:投机解码值得上吗? A:只在输出高度可预测时值得,而且要能接受多占一份显存。开放式生成不要上。
Q:为什么GPU利用率显示很高但速度还是慢? A:利用率高不代表有效。可能是批次太小、可能是显存带宽打满、也可能是 CPU 侧的数据准备成了瓶颈。看每 token 耗时而不是利用率。
注:各框架的参数名与默认值随版本变化,具体以官方文档为准;加速倍数须以你的硬件和负载实测。
相关文章
本站文章由编辑人工撰写,收录的工具均经过实测或公开资料核验。文中链接指向工具官网或 GitHub 仓库,仅作信息参考,不构成付费推广。