大模型推理与部署入门
训练只是万里长征第一步,如何让模型快速、低成本地服务用户才是工业界最关心的问题。本文从 LLM 推理的基本原理讲起,系统覆盖 KV Cache 管理、推理引擎(vLLM / SGLang / TensorRT-LLM)、量化技术、Speculative Decoding、Prefill/Decode 解耦等核心技术,并提供可量化的性能分析方法。
📑 目录
- 1. LLM 推理基础
- 2. KV Cache:推理的显存刺客
- 3. 推理引擎核心技术
- 4. 主流推理框架
- 5. 量化技术
- 6. Speculative Decoding
- 7. 系统架构:Prefill/Decode 解耦
- 8. 性能分析与 Benchmark
- 9. 选型决策树
- 10. 自我检验清单
- 参考资料
1. LLM 推理基础
1.1 自回归生成
LLM 的文本生成是逐 token 进行的。每一步根据已有的所有 token 预测下一个 token,直到生成结束符或达到最大长度:
1 | 输入: "今天天气" |
每一步的预测都需要对所有已有 token 做一次完整的 Attention 计算。如果每次都从头算,计算量随序列长度二次增长。KV Cache 就是为了解决这个问题而生的——模型每生成一个字都要”回忆”之前所有字的信息,KV Cache 就是把这些”回忆”存起来复用,避免每次都从头想。就像考试时把公式抄在草稿纸上,后面直接查,不用每次重新推导。
1.2 Prefill 与 Decode 两阶段
LLM 推理分为两个特性截然不同的阶段:
通俗地说,Prefill 像一次性读完一整页书(并行处理),Decode 像一个字一个字地写回信(串行生成)。
Prefill(预填充)阶段
处理用户输入的 prompt。所有输入 token 可以并行计算,一次性生成所有 token 的 KV Cache。
1 | 输入: "请解释量子计算的基本原理"(假设 10 个 token) |
特性:
- Compute Bound:输入 token 多,矩阵乘法的 batch 维度大,计算量大
- 决定 TTFT(Time To First Token,首 token 延迟)
Decode(解码)阶段
逐个生成输出 token。每步只处理 1 个新 token,但需要读取所有历史 KV Cache。
1 | Step 1: 新 token "量" + 读取 10 组历史 KV → 预测 "子" |
特性:
- Memory Bound:每步只有 1 个 token 的计算量,但要从 HBM 读取大量 KV Cache
- 决定 TPOT(Time Per Output Token,每 token 延迟)
- Decode 阶段通常占总推理时间的 90%+
1.3 关键性能指标
| 指标 | 含义 | 影响因素 |
|---|---|---|
| TTFT | Time To First Token,首 token 延迟 | Prompt 长度、Prefill 速度 |
| TPOT | Time Per Output Token,每 token 生成延迟 | KV Cache 读取速度、模型大小 |
| 吞吐量 | token/s 或 request/s | Batch 大小、GPU 利用率 |
| P50/P95/P99 | 延迟分位数 | 尾延迟敏感场景 |
| Goodput | 满足 SLO 的有效吞吐 | TTFT SLO + TPOT SLO |
推理链路全景
1 | 用户请求 |
1.4 Prefill 和 Decode 的计算特性对比
以 LLaMA-7B(d_model=4096, n_heads=32, n_layers=32)为例:
| 阶段 | 计算量 (FLOP) | 数据搬运量 | 计算强度 | 瓶颈类型 |
|---|---|---|---|---|
| Prefill (seq=2048) | ~28 TFLOP | ~14 GB (权重) | ~2000 FLOP/B | Compute Bound |
| Decode (1 token) | ~14 GFLOP | ~14 GB (权重+KV) | ~1 FLOP/B | Memory Bound |
Decode 的计算强度比 Prefill 低约 2000 倍——GPU 大部分时间在等数据从 HBM 搬过来,而不是在算。这就是为什么推理优化的核心是少搬数据。
2. KV Cache:推理的显存刺客
2.1 什么是 KV Cache
在自回归生成中,每一步 Attention 都需要所有历史 token 的 K 和 V 向量。如果每步都重新计算,计算量为 O(N^2)。KV Cache 的思路:把已计算过的 K 和 V 缓存起来,每步只计算新 token 的 K、V,追加到缓存中。
1 | Step 1: Q_1, K_1, V_1 → cache: K=[K_1], V=[V_1] |
2.2 KV Cache 显存公式
1 | 每层每 token 的 KV Cache = 2 × n_kv_heads × d_head × sizeof(dtype) |
各模型的 KV Cache 开销(FP16, batch=1, seq=4096):
| 模型 | n_layers | n_kv_heads | d_head | 每 token KV | seq=4096 总 KV Cache |
|---|---|---|---|---|---|
| LLaMA-7B (MHA) | 32 | 32 | 128 | 512 KB | 2 GB |
| LLaMA-70B (GQA, 8组) | 80 | 8 | 128 | 320 KB | 1.25 GB |
| Mistral-7B (GQA, 8组) | 32 | 8 | 128 | 128 KB | 0.5 GB |
当 batch_size 增大时,KV Cache 线性增长:
1 | LLaMA-7B, seq=4096: |
KV Cache 是限制推理 batch size(进而限制吞吐量)的主要因素。这就是为什么 GQA/MQA/MLA 这些减少 KV Head 数量的 Attention 变种对推理如此重要。
2.3 KV Cache 的碎片问题
不同请求的序列长度不同,且在推理过程中动态增长。如果为每个请求预分配最大长度的连续显存,会造成严重的内部碎片(浪费)和外部碎片:
1 | 显存空间: |
这个问题由 PagedAttention 解决(详见下文)。
3. 推理引擎核心技术
3.1 PagedAttention
vLLM 提出的 PagedAttention 将操作系统虚拟内存分页的思想引入 KV Cache 管理。传统方式是给每个请求预订一整间包房,不管坐几个人都占着;PagedAttention 改成了自助餐厅的拼桌制,按需分配座位,吃完立刻清桌。具体来说:
- KV Cache 不要求连续内存,而是分成固定大小的页(Page/Block)(如每页 16 个 token)
- 页按需动态分配和回收,消除碎片
- 使用页表映射逻辑位置到物理位置
1 | 传统方式: |
收益:
- 显存利用率接近 **100%**(几乎零碎片)
- 内存浪费从 60-80% 降至 <4%
- 支持 Copy-on-Write:相同前缀的请求可以共享 KV Cache 页,进一步省显存
3.2 Continuous Batching
传统 Static Batching 中,一个 batch 内所有请求必须同时开始、同时结束。短请求生成完毕后必须等最长的请求,造成 GPU 空转。打个比方,传统方式是旅行团模式——必须等所有人到齐才出发;Continuous Batching 是公交车模式——到站就上下客,车一直在跑。
1 | Static Batching: |
核心思想:以 iteration(一次 decode step) 为调度粒度,而非以 request 为粒度。每次 decode step 都可以动态地加入新请求或释放已完成的请求。
收益:吞吐量提升 2-10 倍(取决于请求长度差异)。
3.3 Prefix Cache / RadixAttention
当多个请求共享相同的前缀(如 system prompt、few-shot examples)时,可以复用已计算的 KV Cache,避免重复 prefill:
1 | Request A: [系统提示词(2K tokens)] + "问题A" → prefill 系统提示词 → 缓存 |
vLLM 的 Automatic Prefix Caching:自动检测请求间的公共前缀并缓存。
SGLang 的 RadixAttention:使用 Radix Tree(基数树)管理所有缓存的 KV,支持任意前缀匹配(不只是固定的 system prompt),更灵活。
3.4 Chunked Prefill
长 prompt 的 Prefill 计算量大,如果和 Decode 请求放在同一个 batch 中,会严重拖慢 Decode 的延迟(互扰问题)。
Chunked Prefill 将长 prompt 分成多个小块(chunk),每块和 Decode 请求混合处理:
1 | 不分块: |
4. 主流推理框架
4.1 vLLM
vLLM 是当前最流行的 LLM 推理引擎,由 UC Berkeley 的研究团队开发。
核心特性:
- PagedAttention:近乎零碎片的 KV Cache 管理
- Continuous Batching:动态请求调度
- Automatic Prefix Caching:自动前缀复用
- 多种量化支持:GPTQ、AWQ、FP8、INT8
- Speculative Decoding 支持
- 多 GPU 张量并行
快速上手:
1 | # 安装 |
1 | # Python API |
OpenAI 兼容 API:
1 | # 启动服务 |
4.2 SGLang
SGLang 由 UC Berkeley 的 LMSYS 团队开发,核心优势在于复杂推理场景的优化。
核心特性:
- RadixAttention:基于 Radix Tree 的 KV Cache 复用,支持任意前缀匹配
- 结构化输出加速(cFSM):JSON、正则表达式约束的生成加速
- 前端编程语言:用 Python 表达复杂的多轮/多步生成逻辑
- 高效 Runtime:在长上下文和多轮对话场景下吞吐领先
快速上手:
1 | # 安装 |
1 | # SGLang 前端编程(复杂推理场景) |
4.3 TensorRT-LLM
NVIDIA 官方推出的 LLM 推理引擎,做了最深度的硬件优化。
核心特性:
- 深度 CUDA 优化:手写高效 kernel,充分利用 Tensor Core / TMA
- Paged KV Cache + Inflight Batching(即 Continuous Batching)
- FP8 / INT4 / INT8 量化全面支持
- Speculative Decoding 支持
- 与 Triton Inference Server 集成
1 | # 转换模型 |
4.4 框架选型
| 场景 | 推荐框架 | 原因 |
|---|---|---|
| 通用在线服务 | vLLM | 社区活跃、功能全面、API 兼容 OpenAI |
| 复杂 Agent / 多轮对话 | SGLang | RadixAttention 复用效率高、前端编程灵活 |
| 结构化输出(JSON) | SGLang | cFSM 约束生成加速 |
| 极致性能 / NVIDIA 全家桶 | TensorRT-LLM | 最深度的硬件优化 |
| 长上下文(>32K) | SGLang / vLLM | 都有良好支持 |
| 快速原型验证 | vLLM | 最简单的 API、pip install 即用 |
5. 量化技术
量化是推理优化的核心手段之一。通过降低数据精度,可以减少显存占用、降低 HBM 带宽需求、提高吞吐量。形象地说,量化就像把高清照片压缩成缩略图——虽然细节少了一点,但文件小了好几倍,传输和存储都更快。
5.1 量化基础
量化的本质是将高精度浮点数(FP16/BF16)映射为低精度整数或浮点数:
1 | FP16 (16 bit): 高精度,每个参数 2 字节 |
量化公式(线性量化):
1 | 量化: x_int = round(x_fp / scale) + zero_point |
5.2 Weight-Only 量化:GPTQ 与 AWQ
只量化模型权重,激活值保持 FP16。适合 Decode 阶段(memory bound,权重读取是瓶颈)。
GPTQ(GPT Quantization)
- 使用少量校准数据,逐层贪心量化
- 通过 Hessian 矩阵信息补偿量化误差(OBQ/OBS 方法)
- 支持 3-bit / 4-bit weight-only 量化
1 | # 使用 AutoGPTQ 量化 |
AWQ(Activation-aware Weight Quantization)
- 核心观察:不是所有权重通道同等重要,activation 幅度大的通道更重要
- 对重要通道进行缩放保护,然后再量化
- 比 GPTQ 更快(不需要逐层反向传播),精度相当或更优
1 | # 在 vLLM 中使用 AWQ 量化模型 |
5.3 W8A8 量化:SmoothQuant
同时量化权重和激活值到 INT8。关键挑战:激活值中存在异常值(outlier),直接量化会导致大量精度损失。
SmoothQuant 的核心思想:通过一个可学习的缩放因子,将激活值中的 outlier “转移”到权重上(权重更均匀,更好量化):
1 | 原始: Y = X @ W (X 有 outlier,难量化) |
效果:INT8 矩阵乘法,在保持精度的同时吞吐提升 ~1.5-2 倍。
5.4 FP8 量化
Hopper 架构(H100)原生支持 FP8(E4M3 和 E5M2 两种格式),Tensor Core 可以直接执行 FP8 矩阵乘法。
1 | FP16: 1 符号 + 5 指数 + 10 尾数 → 高精度 |
FP8 是目前精度与性能的最佳平衡点:
- 精度损失极小(通常 <1% perplexity 退化)
- H100 上 FP8 算力是 FP16 的 2 倍
- 工程实现简单(无需复杂的量化校准)
5.5 KV Cache 量化:KIVI
当瓶颈不在权重而在 KV Cache 时(长上下文、大并发),对 KV Cache 进行量化:
- Key 按 channel 维度量化(不同 channel 分布差异大)
- Value 按 token 维度量化(不同 token 分布差异大)
- 可以量化到 2-bit,KV Cache 显存减少 8 倍
1 | FP16 KV Cache: batch=64, seq=4096, LLaMA-7B → 128 GB |
5.6 量化选择决策树
1 | 你的瓶颈是什么? |
5.7 “更低 bit 反而更慢”的原因
量化不总是带来加速,以下情况 INT4 可能比 FP16 更慢:
| 原因 | 说明 |
|---|---|
| Kernel 开销 | INT4 需要 dequantize 操作(解包 + 反量化),引入额外计算 |
| Packing/Unpacking | 4-bit 数据需要位操作来打包/解包,有固定开销 |
| Batch 太小 | batch=1 时,计算量太小,kernel 启动开销占比大,量化收益被抵消 |
| 缺乏优化 Kernel | 不是所有量化方案都有高效的 CUDA kernel 实现(如 Marlin 专门优化 INT4 GEMM) |
经验法则:INT4 在 batch=16-32 以上才能稳定获得加速;batch=1 时 FP8 或 FP16 可能更快。
6. Speculative Decoding
6.1 核心思想
Decode 阶段的瓶颈是 memory bound——每步只生成 1 个 token,GPU 算力严重浪费。Speculative Decoding 的思路:用一个小的 Draft 模型快速”猜”多个 token,再用大的 Target 模型一次性并行验证。 就像让一个小助手(Draft 模型)先快速猜一串答案,再让专家(Target 模型)一次性审核——猜对的直接用,猜错的重来,整体比专家自己一个个写要快。
1 | 传统 Decode(逐 token): |
6.2 正确性保证:Speculative Sampling
Speculative Decoding 不是”近似”,而是数学上精确的。通过 Speculative Sampling 算法,保证最终输出的 token 分布与直接用 Target 模型生成完全一致:
1 | 对于 Draft 模型提议的 token x: |
这保证了最终分布严格等于 Target 分布——无偏加速。
6.3 Speculative Decoding 的变体
Medusa
不使用外部 Draft 模型,而是在 Target 模型上加多个额外的解码头(Decoding Head),每个头预测未来不同位置的 token:
1 | Target 模型 |
优势:不需要单独的 Draft 模型,部署简单。
EAGLE-2
使用动态 Draft Tree——根据校准的置信度分数决定 Tree 的形状和深度,更激进地产生可接受 token。
Block Verification
将 token 级别的逐个验证升级为 block 级别的联合验证,进一步减少验证开销,稳定榨出 5-8% 的额外速度。
6.4 什么时候 Speculative 不赚
| 条件 | 为什么不赚 |
|---|---|
| 高温度采样 | 温度越高,Draft 和 Target 分布越不一致,接受率低 |
| Draft 模型太差 | 猜的不准,大部分被拒绝,白白浪费 Draft 计算 |
| 大 batch | batch 大时 Decode 已经接近 compute bound,加 Draft 反而增加计算量 |
| 短生成 | 生成 token 少,Speculative 的启动开销占比大 |
| 特殊任务(代码/数学) | 这类任务 token 分布更尖锐,Draft 模型更难猜准 |
经验法则:Speculative Decoding 在 batch=1-4、温度 0-0.7、中长生成 场景下收益最大,典型加速 1.5-2.5 倍。
7. 系统架构:Prefill/Decode 解耦
7.1 为什么需要解耦
Prefill 和 Decode 的计算特性截然不同:
| 特性 | Prefill | Decode |
|---|---|---|
| 计算类型 | Compute Bound(大矩阵乘) | Memory Bound(读 KV Cache) |
| 每次处理 token 数 | 数百~数千 | 1 |
| 延迟要求 | 不太敏感(用户等第一个字) | 极度敏感(每个字的延迟) |
| 资源需求 | 需要高算力 | 需要高带宽 |
当 Prefill 和 Decode 混合在同一组 GPU 上时:
1 | 混合 batching 的问题: |
长 Prefill 和 Decode 共享 GPU 资源,造成互扰——Decode 的尾延迟(P95/P99)爆炸。
7.2 解耦架构
将 Prefill 和 Decode 分配到不同的 GPU 池:
1 | 用户请求 |
7.3 关键论文
DistServe(OSDI’24)
系统化论证了解耦的收益,核心贡献:
- 提出 goodput 指标:满足 TTFT 和 TPOT SLO 的有效吞吐
- 证明解耦在大多数 SLO 组合下都优于聚合
- 提出基于 goodput 的资源配比优化
Splitwise(ISCA)
从硬件和成本角度分析:
- Prefill 池使用算力型 GPU(如 H100)
- Decode 池可以使用性价比更高的 GPU(高带宽但算力需求低)
- 在吞吐、成本、功耗三个维度优化
TaiChi(2025)
将聚合和解耦统一到一个框架中:
- 不是”解耦一定好”,而是根据工作负载特征动态选择
- 面向不同 SLO 组合做最优 goodput
7.4 解耦的挑战
| 挑战 | 说明 | 缓解方案 |
|---|---|---|
| KV Cache 迁移 | Prefill 完成后需要将 KV Cache 传给 Decode 池 | 高速网络(NVLink/IB)+ 流水线传输 |
| 调度复杂度 | 需要根据负载动态分配 Prefill/Decode 资源 | goodput 驱动的调度算法 |
| 尾延迟 | 迁移本身引入额外延迟 | 预取 + 重叠传输 |
| 队列震荡 | 突发请求导致某个池过载 | 弹性伸缩 + 溢出处理 |
8. 性能分析与 Benchmark
8.1 核心指标体系
推理性能不是一个单一数字,需要多维度衡量:
1 | 改动(量化/内核/调度/解耦) |
一份合格的性能报告应包含:
| 指标 | 含义 | 量化方式 |
|---|---|---|
| TTFT P50/P95 | 首 token 延迟 | ms |
| TPOT P50/P95 | 每 output token 延迟 | ms/token |
| 吞吐量 | 系统每秒处理的 token 总数 | token/s |
| QPS | 系统每秒完成的请求数 | req/s |
| GPU 显存占用 | 权重 + KV Cache + 其他 | GB |
| GPU 利用率 | 计算单元使用率 | % |
8.2 压测工具
GenAI-Perf
NVIDIA 提供的 LLM 推理压测工具,直接输出 TTFT / TPOT / 吞吐等关键指标:
1 | # 安装 |
vLLM 内置 Benchmark
1 | # vLLM 离线吞吐测试 |
8.3 性能分析工具
| 工具 | 层级 | 用途 |
|---|---|---|
nvidia-smi |
系统级 | GPU 利用率、显存、温度 |
torch.profiler |
框架级 | 算子耗时、shape 分析 |
| Nsight Systems | 系统级 | CPU-GPU 交互、时序分析 |
| Nsight Compute | Kernel 级 | 单个 kernel 的 SM/Memory 利用率 |
定位瓶颈的思路:
1 | 1. nvidia-smi 看 GPU 利用率 |
9. 选型决策树
当你遇到推理性能问题时,按以下思路定位方向:
1 | 你最痛的是哪一个? |
10. 自我检验清单
- 能解释 Prefill 和 Decode 的计算特性差异(compute bound vs memory bound)
- 能给出 KV Cache 的显存占用公式,并据此估算给定 batch size 下的显存需求
- 能把推理链路拆成 tokenize → prefill → decode → sampling → postprocess,并标出每段耗时特征
- 能解释 PagedAttention 解决了什么问题,与传统连续 KV Cache 的区别
- 能解释 Continuous Batching 与 Static Batching 的差异与收益
- 能区分并选择 W8A8 vs Weight-only INT4 vs FP8 的适用场景
- 能解释 Speculative Decoding 的正确性保证(分布不变)
- 能给出”什么时候 speculative 不赚”的判断依据
- 能用 vLLM 或 SGLang 部署一个模型并做压测,输出 TTFT / TPOT / 吞吐指标
- 能给出推理引擎选型建议:低延迟、长上下文、多并发、结构化输出各用什么
📚 参考资料
- Efficient Memory Management for Large Language Model Serving with PagedAttention (vLLM)
- vLLM - GitHub
- vLLM Documentation
- SGLang: Efficient Execution of Structured Language Model Programs
- SGLang - GitHub
- TensorRT-LLM - GitHub
- TensorRT-LLM Documentation
- Orca: A Distributed Serving System for Transformer-Based Generative Models
- SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models
- GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers
- AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration
- KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV Cache
- Marlin: Mixed-Precision Auto-Regressive Parallel INference on Large Language Models
- Accelerating Large Language Model Decoding with Speculative Sampling
- Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads
- EAGLE-2: Faster Inference of Language Models with Dynamic Draft Trees
- DistServe: Disaggregating Prefill and Decoding for Goodput-optimized Large Language Model Serving
- Splitwise: Efficient Generative LLM Inference Using Phase Splitting
- Towards Efficient Generative Large Language Model Serving: A Survey from Algorithms to Systems