推理量化与加速
难度:⭐⭐⭐ | 高频指数:🔥🔥 | 应用岗相关度:★★
面试回答
常见问法
- INT8、INT4、FP16、BF16、AWQ、GPTQ、GGUF 分别是什么,怎么选?
- 量化会损失多少精度?哪些层对量化敏感?
- vLLM、TensorRT-LLM、llama.cpp、SGLang、Ollama 各自定位是什么?
- PagedAttention 和 Continuous Batching 分别解决了什么问题?
- KV Cache 为什么会越来越大?怎么管?
- Speculative Decoding 是怎么工作的?什么时候开了反而更慢?
回答
推理加速要分两层看。模型层是”怎么压”——量化、蒸馏、稀疏化、MoE,目标是同样质量下让模型更小更轻。系统层是”怎么调度”——Batching、KV Cache 管理、并行策略、Speculative Decoding,目标是把 GPU 跑满。两层是乘法关系,单独优化一层都不够。
工程上的核心认知是:显存是 LLM 推理最先卡住的资源,不是算力。一张 80G 的 H100,跑 70B 模型只剩二十几个 G 给 KV Cache,长上下文 + 高并发立刻 OOM。所以量化(压权重)和 PagedAttention(压 KV Cache)是同等重要的两条线。
面试时要表现出的判断力是:知道不同优化手段的适用场景——量化适合显存受限,Continuous Batching 适合高并发短请求,PagedAttention 适合长上下文,Speculative Decoding 适合输出长且 Draft 模型够准的场景。乱开优化经常负优化。
追问
- GPTQ 和 AWQ 哪个精度损失更小?为什么?
- GGUF 是格式还是量化方法?它和 GGML 什么关系?
- 为什么 vLLM 在高并发场景吊打 HuggingFace transformers?
- TensorRT-LLM 比 vLLM 强在哪?为什么大家还没全部切过去?
- Continuous Batching 和传统 Static Batching 的核心区别是什么?
- Speculative Decoding 的接受率怎么算?低于多少就不该开?
原理展开
1. 推理为什么慢,瓶颈到底在哪
LLM 推理可以拆成两个阶段,瓶颈完全不一样:
- Prefill 阶段:处理输入 Prompt,把整段一次性算完,算力瓶颈(compute-bound),GPU 利用率高
- Decode 阶段:一个 token 一个 token 往外吐,每生成一个 token 要把整个模型权重和所有历史 KV Cache 都从显存搬一遍,显存带宽瓶颈(memory-bound),GPU 利用率经常只有 20-30%
绝大多数推理优化都在打 Decode 阶段。因为输出长、占主要耗时、又卡在带宽,提升空间最大。理解这一点之后再看各种优化手段,逻辑就顺了——量化是压权重减少搬运量,KV Cache 量化和 PagedAttention 是治 KV 膨胀,Batching 是让一次搬运服务多个请求,Speculative Decoding 是一次前向算多个 token。
2. 量化的几种类型
按精度从高到低:
| 类型 | 位数 | 典型场景 | 精度影响 |
|---|---|---|---|
| FP32 | 32-bit | 训练 / 科学计算 | 基准 |
| BF16 | 16-bit | 训练 + 推理主流 | 几乎无损 |
| FP16 | 16-bit | 推理主流(老 GPU) | 几乎无损 |
| FP8 | 8-bit | H100/H200 推理 | 极小(< 1%) |
| INT8 | 8-bit | 推理常用 | 小(1-2%) |
| INT4 | 4-bit | 边缘 / 显存紧张 | 中(2-5%) |
| INT2/3 | 2-3-bit | 极限压缩 | 大,需精调 |
BF16 和 FP16 的关键差异:BF16 指数位多,动态范围大,训练稳定;FP16 精度位多,但容易溢出。新模型基本都用 BF16 训练,所以推理也优先 BF16。
量化方法上几个常见名词:
- GPTQ:基于二阶信息(Hessian)的训练后量化,逐层做,精度高,适合 INT4
- AWQ:Activation-aware Weight Quantization,发现并保护激活值大的少数权重,比 GPTQ 更稳,社区主流
- SmoothQuant:把激活的 outlier 挪到权重侧,让两边都好量化,常用于 INT8
- GGUF:这是格式不是量化方法,llama.cpp 的统一权重容器,里面可以装各种量化方案
- NF4 / FP4:QLoRA 论文里的 4-bit 浮点表示,QLoRA 微调常用
3. 量化对精度的真实影响
实战上的经验数:
- INT8 / FP8:几乎无损,大多数下游任务掉点 < 1%
- AWQ INT4:通用任务掉 1-3%,代码 / 数学等 reasoning 重的任务掉得多一些
- GPTQ INT4:和 AWQ 接近,部分场景略差
- 更激进的 INT3 / INT2:必须重新评测,掉点不可忽略
敏感层:Attention 的 Q/K/V 投影、最后一层 lm_head 通常更敏感,部分实现会把这些层保留 FP16。
最容易踩的坑:用通用 benchmark(MMLU / HellaSwag)看不出问题,但实际业务 prompt 一上就掉。所以量化后一定要跑一遍业务自己的评估集,不能只看公开榜单。
4. KV Cache 为什么是大头
Transformer Decode 时,每一层要存所有历史 token 的 K 和 V。KV Cache 大小近似:
KV Cache = 2 (K+V) × num_layers × num_heads × head_dim × seq_len × dtype_bytes
举个数:70B 模型,80 层,64 head,head_dim 128,BF16(2 字节),单 token 大约 2.5MB。一个 8K 上下文的请求,KV Cache 就是 20GB。100 个并发请求轻松把 80GB 的 H100 撑爆。
所以 KV Cache 必须管:
- KV Cache 量化:FP8 / INT8,典型节省 30-50% 显存,精度几乎无损
- PagedAttention:分页管理,消灭碎片
- Prefix Caching:相同前缀的 KV 复用
- 滑动窗口 / Sink Attention:只保留最近 N 个 token,长序列必备
5. PagedAttention:KV Cache 的虚拟内存
vLLM 的核心创新。传统 KV Cache 是连续内存分配——为每个请求预留 max_seq_len 的空间,实际只用了几百 token 也照样占完,碎片严重。
PagedAttention 把 KV Cache 切成固定大小的块(比如 16 token 一块),按需分配,逻辑地址通过页表映射到物理块:
请求 A 的 KV: [block 7] → [block 2] → [block 9]
请求 B 的 KV: [block 4] → [block 11]
请求 C 的 KV: [block 8]
↑
页表把逻辑顺序映射到物理 block
收益是两个:
- 几乎零碎片:显存利用率从 30-50% 提到 90%+
- 共享前缀:多个请求共享同一段 Prompt(System Prompt / Few-shot 例子)的 KV,物理 block 只存一份
这是为什么 vLLM 在高并发下吊打老引擎的根本原因——同样显存能塞下两三倍的并发。
6. Continuous Batching:动态拼车
传统 Static Batching 是攒齐一个 batch 一起算,问题是长短不一——batch 里最长的请求决定整体耗时,短的算完只能干等。
Continuous Batching(也叫 in-flight batching)按 iteration 调度:每生成一个 token,调度器就检查——有请求结束了?立刻让新请求进来填空位;有新请求到达?下一个 iteration 立刻加入。
传统 Static Batching:
[req1 长] ████████████
[req2 中] ██████ ← 空等
[req3 短] ███ ← 空等更多
↑ batch 结束时间 = 最长的那个
Continuous Batching:
[req1 长] ████████████
[req2 中] ██████[req4 新进来]██████
[req3 短] ███[req5 新进来]██████████
↑ GPU 永远是满的
在线 Serving 场景,Continuous Batching 是几倍吞吐提升。所有现代推理引擎都有。
7. Speculative Decoding:小模型抢跑
思路:用一个小 Draft 模型一次生成 N 个 token,大模型只做”验证”——一次前向同时检查这 N 个 token 是否符合自己的分布。接受的就直接用,第一个不接受的位置开始重新生成。
收益的本质:Decode 是 memory-bound,一次前向多算几个 token 几乎不增加耗时。如果接受率 60%,理论加速 ~2 倍。
接受率怎么算:Draft 预测的 token 和大模型自己采样会得到的 token 一致的比例。
什么时候开了反而更慢:
- 接受率低(< 30%),验证后大量重算,纯亏
- Draft 模型本身大,单次前向就慢
- 输出短(几个 token),开销摊不下来
- 长尾任务(创意写作)随机性高,Draft 难命中
工程上一般要 A/B,不是无脑打开。Llama 3 系列、Medusa、EAGLE 是常见的 Draft 方案。
8. 主流推理引擎对比
| 引擎 | 定位 | 强项 | 适合场景 |
|---|---|---|---|
| vLLM | 开源通用 Serving | PagedAttention 原产地、社区活跃、多模型支持广 | 高并发 在线 Serving |
| TensorRT-LLM | NVIDIA 官方 | 极致性能、深度融合 CUDA 算子 | 对延迟 / 吞吐有极致要求的大型在线服务 |
| SGLang | 新兴 Serving 引擎 | RadixAttention 前缀共享、结构化输出快 | Agent / 多轮 / 高前缀复用场景 |
| llama.cpp | C++ 推理 | CPU / Apple Silicon / 边缘端、GGUF 生态 | 本地部署、边缘设备 |
| Ollama | llama.cpp 的友好封装 | 一行命令跑模型 | 个人开发 / Demo |
| TGI | HuggingFace 自家 | 和 HF 生态贴合 | HF 生态内部署 |
| MLX | Apple 自家 | Mac Apple Silicon 原生 | M 系列 Mac 本地推理 |
几个常见误区澄清:
- TensorRT-LLM 性能更强但模型适配慢,新模型经常 vLLM 先支持
- SGLang 不只是快,结构化输出 / Agent 多轮场景下的前缀复用收益巨大
- llama.cpp 不是”低端选择”,Apple Silicon 上跑 70B 体验比想象的好
- Ollama 适合个人不适合生产,Serving 能力远不如 vLLM
9. 选型决策路径
场景一:在线高并发对客 Serving (QPS > 10)
→ vLLM (主流) / TensorRT-LLM (极致) / SGLang (Agent 多轮)
+ AWQ INT4 (省显存) / FP8 (H100)
+ Continuous Batching (默认开)
+ Speculative Decoding (评估后再开)
场景二:单机 / 离线批量推理
→ vLLM offline mode / TensorRT-LLM
+ 大 Batch Size 拉满吞吐
+ 不需要 Continuous Batching
场景三:边缘 / 本地部署
→ llama.cpp + GGUF (Q4_K_M 起步)
+ INT4 / INT5 量化
+ 没有 Batching 需求
场景四:Mac 本地
→ llama.cpp (Metal) 或 MLX
+ GGUF Q4
场景五:Agent / 多轮 / 工具调用
→ SGLang (前缀复用收益最大)
+ 结构化输出能力
判断标准:先看部署形态(在线 Serving / 离线批 / 边缘),再看显存预算(决定量化精度),最后看请求特征(决定是否开 Speculative / 前缀缓存)。
对比总结
| 场景 | 推荐 |
|---|---|
| 高并发在线 Serving | vLLM + Continuous Batching + PagedAttention |
| 极致延迟 / 吞吐(大厂在线) | TensorRT-LLM + FP8 (H100) |
| Agent 多轮 / 高前缀复用 | SGLang + RadixAttention |
| Mac 本地 / 边缘部署 | llama.cpp + GGUF Q4 |
| 显存紧张但要保质量 | AWQ INT4,必测业务评估集 |
| H100/H200 集群 | FP8 + PagedAttention |
| 长上下文场景 | PagedAttention + KV Cache 量化 + Prefix Caching |
| 输出长且 Draft 准 | Speculative Decoding(先 A/B) |
易错点
- 认为 INT4 量化掉点可以忽略——必须跑业务自己的评估集,公开榜单看不出问题
- 把 GGUF 当成量化方法——GGUF 是格式,里面装的才是量化方案(Q4_K_M 等)
- Speculative Decoding 无脑开——接受率低或输出短的场景开了更慢
- 只调 Batch Size 不调 KV Cache 上限——OOM 出在 KV 上不是权重上
- 以为 Continuous Batching 是 vLLM 独有——现在所有现代引擎都有,区别在调度细节
- 用 transformers 直接跑做 Serving——单请求还行,并发立刻被吊打
- 混用 vLLM / TGI / TensorRT-LLM 的术语——它们的 batching 机制和配置项不一样
- 忽略 Prefill 和 Decode 是两个不同瓶颈——很多优化只对其中一个有效
记忆技巧
记三组锚点:
- 两层优化:模型层(量化、蒸馏、稀疏)+ 系统层(Batching、KV 管理、Speculative)
- 三件 KV 工具:PagedAttention(分页)+ KV 量化(压缩)+ Prefix Caching(复用)
- 选型口诀:在线 Serving 看 vLLM,极致性能上 TensorRT-LLM,多轮 Agent 选 SGLang,边缘本地 llama.cpp
一句话总结:显存比算力更先卡住,所以量化和 KV Cache 管理是同等重要的两条主线。
面试速答版
LLM 推理加速分两层。模型层做量化——INT8/FP8 几乎无损,AWQ INT4 是显存紧张时的主流选择;系统层做调度——PagedAttention 解决 KV Cache 碎片,Continuous Batching 解决长短请求空等,Speculative Decoding 用小模型抢跑加速 Decode。
真正的瓶颈在 Decode 阶段的显存带宽,不是算力。所以 vLLM 这类引擎能把 GPU 吃满靠的就是 PagedAttention(消除显存碎片,提升并发)和 Continuous Batching(动态拼车,永不空等)。
选型上:在线 Serving 主流 vLLM,极致性能上 TensorRT-LLM,Agent 多轮选 SGLang,边缘本地用 llama.cpp + GGUF。
面试加分版
如果想多讲一层,可以补:
- Prefill 和 Decode 是两个完全不同的瓶颈——前者算力 bound,后者显存带宽 bound,优化手段不通用
- PagedAttention 的设计灵感来自 OS 虚拟内存——把”分页 + 页表”的思路搬到 KV Cache,这是 vLLM 论文的核心贡献
- Speculative Decoding 的接受率要看 Draft 和 Target 的分布相似度——同系列的 1B + 70B 接受率高,跨系列效果差
- FP8 是 H100/H200 的免费午餐——硬件原生支持,精度损失比 INT8 还小,但老卡(A100 / V100)享受不到