跳到主要内容
AI 系统性能工程方法论

第9章:多节点推理优化

vLLM / TensorRT-LLM / SGLang / Dynamo 选型决策树 + speculative decoding + PD 解耦 + KV cache + continuous batching + 长上下文 / 多模型同卡——把 Goodput 方法论扩展到推理侧

vLLM TensorRT-LLM Dynamo speculative decoding PD 解耦 KV-Cache continuous batching

Ch8 把 Goodput 在训练侧讲透——这一章是模块零的最后一章,把同样的方法论扩展到推理侧。推理工程师每天面对的是另一组挑战:TTFT vs TPOT 的取舍、长 prompt prefill 爆炸、多请求 batching 的复杂调度、PD 解耦的取舍、speculative decoding 的工程现实。这一章给一份可直接照着选的推理决策树,并解释每条选择背后的工程逻辑。

📑 目录


1. 推理工作负载的关键指标

1.1 三个核心指标

指标含义用户感受
TTFT (Time To First Token)从请求发出到第一个 token 的延迟”AI 在多久后开始回应我”
TPOT (Time Per Output Token)后续 token 的平均生成间隔”回应得有多流畅”
Throughput系统每秒能产出多少 token服务能承多少并发

1.2 TTFT vs TPOT 的取舍

它们之间存在结构性冲突

  • 想 TPOT 低:batch 越大越好(GPU 利用率高)
  • 想 TTFT 低:batch 越小越好(避免排队 + prefill 不被插入)
  • 想 throughput 高:batch 大 + prefill / decode 并行

不同业务对这三个的权重不同:

业务关键指标容忍度
客服对话TTFT < 1sTPOT 50-100ms 可接受
Code Agent / CopilotTTFT < 200msTPOT 30-60ms
后台批处理Throughput 最大化TTFT / TPOT 不重要
长上下文 RAGTTFT 与 prompt 长度强相关接受 prefill 慢

1.3 推理 Goodput 的定义

把训练 Ch8 的 Goodput 概念翻译到推理侧:

Inference Goodput=满足 SLA 的请求数 × 输出 tokens总 GPU 时间\text{Inference Goodput} = \frac{\text{满足 SLA 的请求数 × 输出 tokens}}{\text{总 GPU 时间}}

不满足 SLA 的请求不计入分母——这点和”原始 throughput”完全不同。

2. 推理引擎选型决策树

2.1 主流引擎能力对照

引擎强项弱项推荐场景
vLLMPagedAttention / 全开源 / 社区活跃极致 latency 不如 TRT-LLM通用 LLM 服务
TensorRT-LLM极致优化(NVIDIA 自己写)/ FP8闭源 / 集成复杂极致 latency / FP8
SGLangRadixAttention prefix cache / 快较新,生态不如 vLLM工具调用密集
NVIDIA DynamoPD 解耦 / 多模型混合较新大规模生产
llama.cpp / MLCCPU / mobile / 边缘不适合大规模并发端侧

2.2 选型决策树

推理 stack 选型

├─ 业务规模?
│  ├─ 小(< 1000 QPS)→ vLLM 默认即可
│  ├─ 中(1k-10k QPS)→ vLLM + 调优 / SGLang
│  └─ 大(10k+ QPS)→ Dynamo / TRT-LLM

├─ 硬件?
│  ├─ H100 + 想用 FP8 → TRT-LLM
│  ├─ A100 → vLLM 或 SGLang
│  ├─ 国产 GPU → 各厂商定制(华为 MindIE / 寒武纪 etc)
│  └─ 边缘 / 端侧 → llama.cpp / MLC / Apple MLX

├─ 关键 SLA?
│  ├─ TTFT 极致 → TRT-LLM(CUDA Graph + FP8)
│  ├─ throughput 极致 → vLLM (continuous batching)
│  ├─ 长上下文 → vLLM + chunked prefill
│  └─ 工具调用密集 → SGLang (prefix cache)

└─ 团队工程能力?
   ├─ 强 → 自定义 + 各家组合
   ├─ 中 → vLLM + 上层胶水
   └─ 弱 → 直接调商业 API

2.3 选型的实战经验

经验 1:先用 vLLM 跑起来

99% 团队的第一选择应该是 vLLM——开源、文档好、性能基线已经很高、坑相对少。等到性能跑满 vLLM 极限再考虑其他。

经验 2:TRT-LLM 的优势在 H100

H100 + FP8 + CUDA Graph 这一套在 TRT-LLM 上才能拿全——A100 上 vLLM 已经接近 TRT-LLM。

经验 3:SGLang 在工具调用场景显著更快

Agent / RAG 这种多轮工具调用 + 大量 prefix 复用的场景,SGLang 的 RadixAttention 能拿到 vLLM 之上的 30-50% 加速。

3. Continuous Batching 与 PagedAttention

3.1 静态 batch vs continuous batch

静态 batch(早期实现):

  • 等够 N 个请求 → 凑成一个 batch → 一起跑
  • 问题:第 1 个请求要等到第 N 个到达
  • 长短请求差距大时尾部请求被快请求拖慢

Continuous batching(vLLM 等现代引擎):

  • 每个 step 动态加入新请求 / 移除完成的请求
  • 没有”凑齐”的等待
  • 长短请求互相不阻塞

3.2 PagedAttention:解决 KV-Cache 碎片

KV cache 的传统实现:每个请求预分配最大长度的 contiguous 显存——严重碎片化

  • 短请求只用了 100 token,剩下 31900 token 浪费
  • 长请求时已经分配的小块凑不出 32K

vLLM 的 PagedAttention:

  • 把 KV cache 切成固定大小的”页”(block,typically 16 token)
  • 请求按需分配页(不连续也行)
  • 类似 OS 的虚拟内存机制

效果:

  • KV 内存利用率从 ~50% 提到 ~90%+
  • 同样显存能服务更多并发请求

3.3 Chunked Prefill

长 prompt 的 prefill 是 O(n2)O(n^2)——一个 32K prompt 的 prefill 可能要 1-2 秒。如果在这期间 decode 阶段被阻塞,TPOT 会暴增。

Chunked prefill:

  • 把 32K prompt 拆成 4-8 个 chunk
  • 每个 chunk 的 prefill 和正在 decode 的请求交错进行
  • TPOT 不再被 long prompt 卡死

vLLM 0.6+、SGLang 都已默认开启。

4. KV-Cache 优化:Prefix / Cross-node / Quantization

4.1 Prefix Cache:复用相同前缀

很多场景下不同请求共享相同 prefix:

  • 同一 system prompt 的多个用户请求
  • Agent 多轮对话的历史部分
  • RAG 的同一文档检索

把 prefix 的 KV cache 缓存下来,新请求复用:

请求 1: [system_prompt] [user_msg_1] → KV: cache_sys + cache_msg1
请求 2: [system_prompt] [user_msg_2] → KV: cache_sys (reuse!) + new

vLLM Prefix Caching、SGLang RadixAttention 都做了这件事——典型场景能拿到 5-30% 的总 prefill 时间节省。

4.2 Cross-node KV-Cache 共享

PD 解耦推理(§5 详细)的核心:Prefill 节点和 Decode 节点不同——KV cache 必须跨节点传输。

  • 普通 TCP:太慢(KV cache 几十 MB / token)
  • RDMA / NVLink:µs 级
  • 鲲鹏 UB:见模块十六 Ch4

代表实现:Mooncake、NIXL、LMCache。

4.3 KV-Cache Quantization

把 KV cache 从 FP16 / BF16 降到 INT8 / FP8:

  • 显存节省:50%
  • 精度损失:通常 <1% perplexity 上升
  • 实现:vLLM 0.6+ 支持 fp8 KV cache

工程经验:长上下文场景特别值得开——KV cache 是长上下文显存压力的主要来源。

5. PD 解耦推理

5.1 为什么要解耦

LLM 推理两阶段差距巨大:

阶段计算特征GPU 利用率优化重点
PrefillO(n2)O(n^2) 大 batch matmul高(compute bound)TFLOPS
Decode每步 1 token,small batch低(memory bound)HBM bandwidth

把它们放在同一张卡上跑:

  • 长 prompt 的 prefill 让卡进入 compute bound
  • 这期间在跑的 decode 请求 TPOT 暴增
  • 一个长请求拖累所有短请求

5.2 PD 解耦的设计

                          ┌─────────────────┐
请求 → 调度器 → ─────────► │  Prefill Pool   │
                          │  (大 batch /     │
                          │   compute bound) │
                          └────────┬────────┘
                                   │ KV cache 跨节点传输

                          ┌─────────────────┐
                          │   Decode Pool   │
                          │  (持续 batch /   │
                          │   memory bound)  │
                          └────────┬────────┘


                              用户响应

5.3 何时值得做 PD 解耦

需要做的信号:

  • 长 prompt(>4K)占比高
  • TPOT SLA 紧(< 30ms)
  • 集群规模大(>16 GPU)

不需要做的信号:

  • 大部分请求 < 1K prompt
  • 集群小(< 8 GPU)—— 解耦本身的协调成本超过收益

5.4 实现选项

  • NVIDIA Dynamo:原生支持 PD 解耦
  • Mooncake:开源,专注 PD 解耦 + KV cache pool
  • vLLM 自定义:vLLM 有 disagg-prefill 实验性支持

6. Speculative Decoding

6.1 原理

Speculative decoding 的核心想法:

  • 用一个**小模型(draft model)**快速生成 K 个 token
  • 用大模型一次 forward 验证这 K 个 token
  • 接受能匹配的部分 → 实际 throughput 上升

关键工程理由:大模型的 forward 是 memory-bound 的——一次 forward 算 1 个 token 和算 K 个 token 的 wall time 差不多。所以”一次验证 K 个”几乎免费。

6.2 几种 draft 策略

策略draft 来源接受率实际加速
Small model draft小一两个量级的模型0.6-0.81.5-2.5x
Medusa同模型加 K 个 head0.5-0.71.3-2x
EAGLE学习一个轻量 next-K predictor0.7-0.852-3x
Lookahead缓存最近见过的 n-gram0.4-0.61.2-1.5x

6.3 工程现实

  • Speculative decoding 在 TPOT 紧 的场景才值得开——throughput 极致场景没收益
  • 接受率高度依赖 draft 模型质量——一个差 draft 反而拖慢
  • 对工具调用 / 结构化输出特别有用(next K token 高度可预测)

6.4 在 vLLM / TRT-LLM 上启用

# vLLM 0.6+
from vllm import LLM

llm = LLM(
    model="meta-llama/Llama-3-70B",
    speculative_model="meta-llama/Llama-3-8B",
    num_speculative_tokens=5,
)

TRT-LLM 通过 --speculative_decoding_mode flag。

7. 长上下文(>1M tokens)的特殊挑战

7.1 三个工程瓶颈

瓶颈 1:Prefill 时间

1M token prefill ≈ 101210^{12} ops 量级——单 H100 即使 FP8 也要几分钟。

对策

  • Sequence Parallelism + Ring Attention(多卡分担)
  • 工程上把长上下文请求路由到 prefill 集群

瓶颈 2:KV cache 显存

1M token × 2 (K+V) × 8 (heads) × 128 (head dim) × 2 byte (fp16) ≈ 4 GB / layer × 80 layers ≈ 320 GB——单卡装不下。

对策

  • KV cache 量化(INT8 / FP8)
  • KV cache 跨节点池(鲲鹏 UB / NIXL / Mooncake)
  • Sliding window attention(牺牲历史 tokens 复用)

瓶颈 3:Attention O(n2)O(n^2)

每 token 要 attend 全 1M token —— 101210^{12} ops 仅 attention。

对策

  • FlashAttention 系列(kernel 优化)
  • Sparse attention / sliding window
  • StreamingLLM 风格(保留 sink + 最近 window)

7.2 1M token 真实 wall clock

按 H100 + FP8 + Ring Attention 当前最优:

  • Prefill:~1-2 分钟 / 1M token
  • Decode:~30-50ms / token
  • 1M prompt + 100 token output 总耗时 ~2-3 分钟

这是 GPT-4 / Claude / DeepSeek 这一档商业模型的真实数字。

8. 多模型同卡服务

8.1 三种隔离模式

模式隔离粒度性能 isolation实现
MIG (Multi-Instance GPU)SM / 显存物理分割A100 / H100 硬件支持
MPS (Multi-Process Service)时间共享CUDA MPS daemon
Time-slicing软件层调度k8s GPU device plugin

8.2 选型

  • 不同租户严格隔离(云服务、SLA 强)→ MIG
  • 同租户多模型混跑 → MPS / time-slicing
  • 极致密度(小模型 + 共享)→ time-slicing + custom scheduler

8.3 一个工程坑

MIG 的 instance 数量配置在boot 时固定——不能动态改。所以 MIG 适合稳定负载,不适合突发负载。

9. 推理 Goodput 路径总结

把上面所有内容整合成”推理 Goodput 优化的工程化路径”:

9.1 阶段 1:从基线到合理(Goodput → 50%+)

  • ✅ 用 vLLM 起步(不要从头写)
  • ✅ Continuous batching 默认开
  • ✅ PagedAttention 默认开
  • ✅ Chunked prefill(如果有长 prompt)
  • ✅ Prefix caching(如果有共享前缀)

9.2 阶段 2:精细调优(Goodput → 65-75%)

  • ✅ KV cache fp8 量化
  • ✅ 模型本身 fp8(H100+)
  • ✅ Speculative decoding(如果 TPOT 紧)
  • ✅ 选对 batch size(throughput vs latency 平衡)
  • ✅ Continuous batching 调度参数(max_num_batched_tokens / max_num_seqs)

9.3 阶段 3:架构级优化(Goodput → 80%+)

  • ✅ PD 解耦(长 prompt 占比高时)
  • ✅ Cross-node KV cache 共享
  • ✅ 自定义 kernel(特定算子)
  • ✅ 多模型 MIG 提升集群利用率
  • ✅ 故障预测 + 主动迁移

9.4 与训练 Goodput 的对照

维度训练 (Ch8)推理 (Ch9)
主要矛盾通信 + 故障长短请求混跑 + KV cache
核心优化技术3D 并行 / ZeRO / FP8continuous batch / PagedAttention / spec decode
Goodput 顶级水平75%+80%+
工程成熟度Megatron / DeepSpeedvLLM / TRT-LLM / Dynamo
公司核心竞争力训练 stack 是顶级 lab 护城河推理 stack 是大模型公司护城河

🎯 自我检验清单

  • TTFT 和 TPOT 之间为什么存在结构性冲突?不同业务(客服 / Code / 批处理)各自更看重哪个?
  • PagedAttention 解决的是什么具体问题?为什么 KV cache 内存利用率从 50% 能提到 90%+?
  • 解释 PD 解耦的工程动机——为什么”prefill 是 compute bound + decode 是 memory bound”使得它们值得分开?
  • Speculative decoding 的”一次 forward 验证 K 个 token 几乎免费”这件事的硬件原因是什么?
  • 1M 上下文推理的三个工程瓶颈分别是什么?各自的对策是什么?

📚 参考资料


🌟 模块零至此完整。从 Ch1 的 Goodput 方法论,到 Ch5-7 的 CUDA / PyTorch 工具链,到 Ch8 的训练 Goodput,再到 Ch9 的推理 Goodput——一份贯穿单 kernel 到万卡集群、训练到推理的性能工程方法论就此收口。

把这 12 章作为长期参照——当你下次面对”为什么我的训练 / 推理慢""怎么才能让 Goodput 上去”这类问题时,回到这里找答案。