大模型推理优化实战:PD分离、Radix Attention 与 Continuous Batching,算力成本能省多少
400 万美元一台的 GB200 NVL72 机柜,72 张芯片,软件调度没做好,实际 GPU 利用率可能只有 50%。这意味着另外 200 万美元,正以电费、折旧和机会成本的形式,在机房里白白烧掉。
这不是段子。2026 年 8 月硅谷最热的话题已经不是"哪个模型更强",而是"怎么把 GPU 的物理极限榨出来"——AI Infra 这场效率革命,正在催生一个千亿美元级别的新市场。大模型推理的四个核心问题,正好对应四种主流优化手段:哪些计算不用重做(Radix Attention)、哪些等待可以消除(PD 分离)、哪些算力没吃满(低精度与投机采样)、哪些请求不该排队(Continuous Batching)。
这篇文章写给正在部署大模型推理服务的人:你在用 vLLM 或者 SGLang,想搞清楚 TTFT 为什么慢、吞吐为什么上不去、KV Cache 为什么把显存吃光了。我会把 Prefill/Decode 分离、Radix Attention、Continuous Batching 这几件事讲透,配上可跑的配置和实测数据。读完你能回答一个问题:我该不该上 PD 分离,上了到底能省多少。
1. 背景与痛点:推理税、显存爆炸和相互拖累
先说三个真实存在的坑。
坑一:推理税。 Agent 场景下,同一个 system prompt 和工具描述可能被调用几十上百次,但模型每次都从头算一遍。这是纯浪费——同样的输入,算一遍和算一百遍,结果一模一样,凭什么算一百遍?业界把这种白白烧掉的算力叫"推理税"(inference tax)。多轮对话里尤其明显,每一轮都带着前几轮的全部上下文重算。
坑二:KV Cache 显存爆炸。 推理过程中,模型每生成一个 token 都要把之前的 key-value 缓存下来。以 70B 模型、8K 上下文为例,单个请求的 KV Cache 就要吃掉大约 4GB 显存(A100 总共 80GB)。并发一上来,显存直接告急,而传统的显存分配方式碎片化严重,实际利用率更低。
坑三:Prefill 和 Decode 互相拖累。 大模型推理分两个阶段:Prefill(读入 prompt,高计算密集,要快)和 Decode(逐字生成,高内存带宽密集,一个 token 一个 token 往外蹦)。早期推理引擎像排队打饭,一个算完才轮到下一个;后来有了 Continuous Batching(连续批处理),GPU 变成了一辆随时上下客的公车,新请求随时上车、算完随时下车,吞吐量提升了 2 到 4 倍。
但问题来了:把 Prefill 和 Decode 塞在同一张卡上,它们还是互相干扰。Prefill 抢算力,Decode 的延迟(TPOT)被拉长;Decode 占着带宽,Prefill 的首 token 延迟(TTFT)也变差。两头不讨好。
为什么是现在爆发? 2026 年的现实是:模型能力卷到头了,至少短期是这样。斯坦福《2026 人工智能指数报告》的说法是中美顶尖模型综合性能差距只剩 2.7%,各家旗舰打平,再往上卷的边际收益越来越低。这时候谁能在同样的硬件上跑出更低的单价,谁就能拿下企业客户。百倍价差就是这么来的——不是单纯的补贴,是推理效率的结构性差异。所以 8 月的硅谷,AI Infra 取代了模型发布,成了最热的关键词。
这就是 PD 分离要解决的问题。
2. 技术原理:四个优化手段各解决什么问题
先把概念摆清楚,后面实战才有依据。
PagedAttention(vLLM 提出):借鉴操作系统分页思想,把 KV Cache 切成固定大小的 block,按需动态分配,解决显存碎片和浪费。vLLM 论文里的数据:相比 HuggingFace Transformers,吞吐提升最高 24 倍;相比 TGI,提升 3.5 倍。
RadixAttention(SGLang 提出):在 PagedAttention 基础上更进一步,用基数树(radix tree)组织所有请求的 KV Cache。相同前缀的请求直接共享一段计算结果,只有内容分叉时才长出新枝叶。配合缓存调度(Cache Scheduling)——把相似请求集中连续处理——长上下文场景的算力成本可以暴降 90%。
Continuous Batching(连续批处理):不再等一个 batch 全部算完,而是新请求随时插入、完成的请求随时离开,GPU 永远在干活。这是现在所有主流推理引擎的标配。
PD 分离(Prefill-Decode Disaggregation):把 Prefill 和 Decode 分配到不同的 GPU 实例上。Prefill 卡专门吃大段输入,算完立刻通过高速网络把 KV Cache 传给 Decode 卡集群,Decode 卡专心逐字生成,互不干扰。vLLM、SGLang、Mooncake、Dynamo 都已支持。
flowchart LR
A[请求进入] --> B[Router 路由]
B --> C[Prefill 实例<br/>计算密集型<br/>快速处理大段输入]
| C -->|KV Cache 高速传输| D[Decode 实例<br/>内存带宽密集型<br/>逐字生成] |
D --> E[输出返回]
| D -->|共享前缀复用| F[Radix Tree<br/>KV Cache 缓存] |
F --> D
对比一下各个手段的适用场景,别盲目上:
| 优化手段 | 核心机制 | 典型收益 | 适用场景 |
|---|---|---|---|
| PagedAttention | KV Cache 分页管理 | 吞吐比 HF 提升 24 倍 | 所有 vLLM 部署(默认开启) |
| RadixAttention | 前缀树共享 KV Cache | 比 vLLM 0.5.0 快 30%,吞吐 1.5 倍 | 多轮对话、Agent、共享 system prompt |
| Continuous Batching | 动态拼批 | 吞吐提升 2-4 倍 | 高并发在线服务(默认开启) |
| PD 分离 | Prefill/Decode 分卡 | 聊天场景吞吐比 vLLM 高 2.0-3.41 倍 | 严格 TTFT/TPOT 双 SLO 要求 |
数据来源:PagedAttention 论文、SGLang RadixAttention 论文、得物技术《从大模型性能优化到 DeepSeek 部署》、智源社区 DistServe 技术报告。上面四个手段不是替代关系,是叠加关系——Radix Attention 和 PD 分离可以同时开。
3. 环境准备
我的测试环境(2026-08 实测):
| 组件 | 版本 |
|---|---|
| 操作系统 | Ubuntu 22.04 |
| GPU | 8 × H200 80GB(PD 分离对比用 4 节点) |
| Python | 3.10.12 |
| vLLM | 0.6.3(API 兼容 0.8+) |
| SGLang | 0.4.1 |
| CUDA | 12.4 |
安装命令(用 pip,别用 root 跑):
# 创建独立环境,避免和训练环境打架
python3 -m venv ~/venvs/sglang
source ~/venvs/sglang/bin/activate
pip install "sglang[all]==0.4.1" --find-links https://flashinfer.ai/whl/cu124/torch2.4/flashinfer-0.1.6+cu124torch2.4-cp310-cp310-linux_x86_64.whl
pip install vllm==0.6.3
装完验证一下:
python3 -c "import sglang; print(sglang.__version__)"
python3 -c "import vllm; print(vllm.__version__)"
4. 实战实现:SGLang 启动 + Radix Attention 实测
4.1 单实例启动(Radix Attention 默认开启)
SGLang 的 Radix Attention 是默认开启的,不需要额外配置。启动命令:
python3 -m sglang.launch_server \
--model-path Qwen/Qwen3-32B \
--port 30000 \
--tp 8 \
--mem-fraction-static 0.85 \
--enable-mixed-chunk
注意 --enable-mixed-chunk 这个参数:它允许 prefill 请求分块执行,避免单个大请求把 GPU 占死,高并发下平均响应时间能提升约 2 倍。vLLM 新版本里 chunked prefill 已经默认开启,SGLang 需要手动加。
4.2 压测:验证共享前缀的收益
Radix Attention 的核心价值在于共享 system prompt。先用 SGLang 自带的 bench_serving 跑个基线:
python3 -m sglang.bench_serving \
--backend sglang \
--model Qwen/Qwen3-32B \
--num-prompts 200 \
--request-rate 10 \
--output-token 256
对比实验:一组用相同的 system prompt(触发前缀复用),一组每请求随机 system prompt(无法复用)。同样的 200 个请求,相同前缀场景下的吞吐明显更高——这就是"推理税"被省下来的部分。
但要测出 Radix Attention 的真实收益,光靠 bench_serving 不够,它默认是随机 prompt。我写了个小脚本,专门控制前缀共享率:
import json
import random
import time
from openai import OpenAI
client = OpenAI(base_url="http://localhost:30000/v1", api_key="EMPTY")
SYSTEM_PROMPT = "你是一个严谨的代码审查助手,回答要给出理由和反例。" # 共享前缀
POOLS = [
"解释一下什么是闭包。",
"这段代码有什么问题?",
"如何优化这个查询?",
"写一个 Python 装饰器。",
"对比 list 和 tuple 的差异。",
]
def run_batch(share_prefix: bool, n: int = 100):
latencies = []
for i in range(n):
if share_prefix:
messages = [{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": random.choice(POOLS)}]
else:
messages = [{"role": "system", "content": f"你是助手{i},专注{i}号领域"},
{"role": "user", "content": random.choice(POOLS)}]
start = time.time()
client.chat.completions.create(
model="Qwen/Qwen3-32B",
messages=messages,
max_tokens=128,
temperature=0.7,
)
latencies.append(time.time() - start)
return sum(latencies) / len(latencies)
print("共享前缀 avg latency:", run_batch(True))
print("随机前缀 avg latency:", run_batch(False))
同样 100 个请求,共享前缀那组的平均时延通常能低 20%-40%(前缀越长、复用率越高,差距越大)。如果两个数字几乎一样,先别急着怀疑脚本——去查 radix cache 的命中日志,大概率是缓存被 eviction 挤掉了(见踩坑 3)。
4.3 vLLM 对照
有对比才有说服力。同样模型同样负载,vLLM 侧启动:
python3 -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen3-32B \
--port 30001 \
--tensor-parallel-size 8 \
--gpu-memory-utilization 0.9 \
--enable-prefix-caching
vLLM 的 --enable-prefix-caching 是 0.6 之后才默认开的,老版本要显式加上,否则共享前缀场景完全不缓存,吞吐差距会非常大。这个参数我一开始漏了,对比出来的数据直接没法看——不是 SGLang 快,是我 vLLM 根本没开缓存。
4.4 PD 分离:两卡分离部署
PD 分离需要至少 2 组实例:一组 Prefill worker,一组 Decode worker,中间用 Router 连接。SGLang 的部署方式:
# 节点 A:2 个 Prefill worker
python3 -m sglang.launch_server \
--model-path Qwen/Qwen3-32B --tp 2 \
--node-prefix prefill --node-rank 0 --nnodes 1 \
--port 30010 --pd-mode prefill
# 节点 B:2 个 Decode worker
python3 -m sglang.launch_server \
--model-path Qwen/Qwen3-32B --tp 2 \
--node-prefix decode --node-rank 0 --nnodes 1 \
--port 30020 --pd-mode decode
# Router:分发请求
python3 -m sglang.launch_server \
--model-path Qwen/Qwen3-32B \
--port 30000 --pd-mode router \
--prefill-node-url http://node-a:30010 \
--decode-node-url http://node-b:30020
生产环境还要考虑 Router 本身的扩容问题。请求量大了以后 Router 会成为新的瓶颈,需要多 Router 加负载均衡,这是很多人上了 PD 分离之后才踩到的坑。
5. 效果验证:数据说话
我自己压了一轮,结合公开实测数据,先说结论:PD 分离不是无脑上,收益和场景强相关。
AWS 博客公开的一组 100K 长上下文实测(MiMo-V2-Flash-FP8 on H200):
| 方案 | 输入长度 | Output token/s | Mean TTFT (s) | Mean TPOT (ms) | 结果 |
|---|---|---|---|---|---|
| 2P2D + NIXL | 100K | 47.36 | 666.5 | 51.2 | ✅ 满足 SLO |
| 2P2D + Mooncake TE | 100K | 9.69 | - | 244 | ❌ 不满足 |
| Non-PD 4 节点 | 128K | 97.47 | 73.3 | 240 | ✅ 满足 SLO |
注意看:单论 output token/s,Non-PD 单节点方案反而更高(97.47 vs 47.36)。但 Non-PD 的 TPOT 是 240ms,PD 分离只有 51.2ms——如果业务对 TPOT 有硬性要求(比如实时对话、流式输出体验),PD 分离就是必须的;如果只是离线批量任务,Non-PD 反而更划算。
另一个真实对比(得物技术实测):Radix Attention 充分利用请求间共享前缀,耗时比 vLLM 0.5.0 快 30%,吞吐是它的 1.5 倍;SGLang 官方给的对比数据是吞吐提升 5 倍以上(共享前缀场景)。
我自己的压测感受:前缀共享率是 Radix Attention 收益的决定性因素。 Agent 场景下 system prompt 占比高,收益明显;纯随机短对话场景,缓存命中率低,收益就打折了。
6. 踩坑记录
这一路踩的坑,比学到的知识多。挑四个最典型的:
坑 1:TTFT 和吞吐是矛盾的。 高并发能提升吞吐,但会恶化 TTFT。如果业务对 TTFT 有硬性要求(比如 3 秒内必须出第一个字),再高的吞吐也没意义。压测的时候别只盯 throughput,把 TTFT 的 P90、TPOT 的 P99 都打出来看。
坑 2:PD 分离的 KV Cache 传输开销。 Prefill 算完要把 KV Cache 通过网络传给 Decode,这一步在 100K 长上下文场景下是实打实的开销。AWS 那组数据里,Mooncake TE 方案直接不满足 SLO——传输策略没调好,收益全被传输吃掉了。需要高速互联网络(NIXL/RDMA)才能把开销藏住。
坑 3:Radix Attention 的缓存上限。 默认的 radix cache 有容量限制,超过以后会触发 eviction(淘汰)。如果共享前缀的请求到达时间间隔太长,缓存早就被挤出去了,命中率感人。SGLang 的 GitHub issue #3501 讨论过彻底关闭 KV 缓存复用的场景——默认开着,但极端场景(比如缓存一致性敏感)可能需要手动调。
坑 4:Router 成为新瓶颈。 上了 PD 分离之后,所有请求先过 Router。单 Router 在高 QPS 下会变成单点,延迟直接爆表。多 Router + 负载均衡是必须的,这个在官方文档里写得很隐晦,我是在生产环境被教育之后才明白的。
7. 调优清单:照着抄就行
把上面所有内容压缩成一张清单,按优先级排。这是我在三套环境(2 卡、8 卡、多节点)验证过的顺序:
| 优先级 | 动作 | 预期收益 | 成本 |
|---|---|---|---|
| P0 | 确认 chunked prefill 已开启 | 高并发 RT 提升约 2 倍 | 改一个参数 |
| P0 | 检查前缀缓存开关(vLLM 老版本需手动开) | 共享前缀场景吞吐翻倍 | 改一个参数 |
| P1 | max-token 设成业务真实需要,别留余量 | 显存占用和 TPOT 同步下降 | 改代码 |
| P1 | FP8/INT4 量化 | 显存减半,吞吐提升 | 换权重文件 |
| P2 | Radix Attention 命中率监控 | 定位缓存被挤掉的问题 | 加监控 |
| P3 | PD 分离 | 严格 SLO 场景收益巨大 | 多机部署 + 网络改造 |
一个容易被忽略的点:max-token 的余量是显存杀手。 很多人习惯给 max_tokens 留 2-3 倍余量,结果 KV Cache 按最大可能值预留,显存白白空转。把 max-token 收紧到业务真实值,有时候比折腾 PD 分离省得还多。
8. 总结与展望
给个不绕弯子的结论:
- 已经在用 vLLM/SGLang 的:PagedAttention、Continuous Batching 默认就是开着的,不用折腾。先把 Radix Attention 的收益吃满——检查你的业务有没有高共享率的前缀(system prompt、多轮对话),有的话收益立竿见影。
- 要上 PD 分离的:先想清楚业务是否同时要求严格 TTFT 和 TPOT。两者都要,PD 分离是正解;只要一个,先别折腾,Non-PD 可能更划算。别信任何"PD 分离一定更快"的说法,case by case 压测。
- 预算有限的:先用 chunked prefill + 优化输出长度(max-token 设小)+ 量化(FP8/INT4),这三样性价比最高。
下一步值得关注的方向:投机采样(speculative decoding)和更激进的低精度推理,这是"哪些算力没吃满"这个问题的答案,2026 下半年会有更多工程化落地。
最后留个问题:你现在的推理服务,GPU 利用率是多少?有没有遇到过"看起来吞吐很高、但用户体验很差"的情况?评论区聊聊你的压测数据,我后面整理成一篇完整的调优案例。
文中数据来源:PagedAttention 论文(vLLM 吞吐 24x/3.5x)、SGLang RadixAttention 论文与官方文档、得物技术《从大模型性能优化到 DeepSeek 部署》(Radix vs vLLM 快 30%/1.5x)、AWS 博客《基于 SGLang 的大模型推理实践》(PD 分离实测表)、智源社区 DistServe 技术报告(2.0-3.41x)。版本号以 2026-08 实测环境为准。
更多推荐
所有评论(0)