分布式推理(Distributed Inference)
所属:01-fundamentals / 06-agent-infra
面试权重:★★★
来源:《深入理解 AI Infra:量化分析与系统设计》第 9 章(李博杰,github.com/bojieli/ai-infra-book);书中数字均有实验条件与算式,非本仓库测量
相关知识点:vllm-pagedattention.md(分页与前缀复用)、batching-parallelism.md(TP/PP/DP/EP 与 KV 复制倍数)、latency-cost.md(PD 分离的边界)、10-agent-runtime.md(任务与工具执行层的资源、隔离与恢复 —— 本篇 §6 是实例级,那篇是任务级)、../01-llm-core/kv-cache.md(GQA 与 MLA 的字节布局)
一句话#
前六篇讲的都是「一张卡怎么跑得快」(连续批处理、分页、量化、前缀缓存);这一篇讲的是「计算与状态放在哪里」—— 四种组织方式(加完整副本 / PD 分离 / AF 分离 / 共享 KV)各自改变系统的不同部分,也各自带来不同的新增约束。判断该用哪一种,靠的是算清三种量:资源占用时间、状态传输字节、以及排队与就绪谁先到。
为什么重要(面试角度)#
- 这是「单实例优化」的天花板之后的必然一问。 面试官问完 PagedAttention 和连续批处理,往下一定是「一个实例扛不住怎么办」。只会答「加机器」的人,和能答出「先算瓶颈在 P 还是 D、状态要搬多少字节、链路够不够」的人,立刻分层。
- 它有一堆能当场推的公式和能引用的实测数字,是「有没有真算过」的分水岭。KV 交接时间、
T_first = max(Q,R)+C、专家副本回本批数 —— 这些都不是背出来的形容词。 - 它专门治一个高频错误答案:「PD 分离提升吞吐」。 同构硬件上分离反而更低 7%;吞吐大幅提升只出现在异构硬件。这个反转是最容易被打穿的一处,也最能体现判断力。
- 它把「缓存亲和性路由」讲清楚,而这是 Agent / 多轮场景的核心:长前缀反复复用,路由决策必须比较完成时间,而不是看有没有命中标记。
- 它和你的项目接得上:QueWI 那个 AI 网关做的是「模型路由」,而这一篇讲的是更下一层的「实例路由」—— 同一个问题的两个尺度。
⚠️ 边界声明:本篇是《AI Infra》第 9 章的知识梳理,不是你的实战经验。你没有自己搭过多机 PD 分离集群、没做过专家副本调度。面试时用「这套账我会这么算」的口径讲,不要说成"我们线上就是这么部署的"。
核心内容#
1. 先接受一个前提:状态可能比权重还大#
第 1 节先建立量级感。Qwen3-8B 的 BF16 权重约 15 GB;而 batch size 32、上下文 8704 token 时,KV 共占 43.5 GB,约为权重的 2.7 倍(第 9.2.3 节算例)。上下文再长,状态就彻底压过权重。
这解释了为什么「分布式推理」不是「把模型切开跑」那么简单 —— 切权重是一件事,放置和搬运状态是另一件事,而后者的量级可以更大。
四种基本组织方式,各自改变系统的一个不同部分:
| 部署方式 | 希望获得什么 | 新增的主要约束 |
|---|---|---|
| 增加完整副本 | 处理更多独立请求,分散排队 | 模型复制、缓存分散、启动与常驻成本 |
| PD 分离 | 分别配置两个阶段的能力 | KV 交接、两端同时占内存、两个队列 |
| AF 分离 | 让算子用上适合的计算与存储资源 | 逐层激活往返、同步、流水线各段负载均衡 |
| 共享 KV | 跨实例复用上下文,减少重复 prefill | 缓存标识、读取、就绪通知、失效与空间释放 |
选择顺序:先分析当前负载的资源需求。两个阶段需求相近 → 加副本就够了,分离只会增加交接开销;许多请求共享长前缀 → 跨实例缓存可能比继续拆计算更值。先把「资源占用」和「交接时间」分别算出来,再决定加副本、分阶段还是共享状态。
2. PD 分离:三个结论,一个反转#
为什么要分。 Prefill 一次处理许多新 token,矩阵计算能充分复用权重,计算受限;decode 每步只处理一个 token,却要反复读权重和上下文,带宽受限。混跑时,一次 8192 token 的 prefill 会独占一张 A100 约 0.86 秒,相当于让卡上所有在途请求错过约 16 个 55 ms 的 decode 步。分离把两个阶段放进独立队列、独立批处理、独立资源配比。
交接的是什么:上下文 KV。 逻辑状态大小
( 层、 token、 个 KV 头、每头 维、每元素 字节,系数 2 对应 K 和 V)。Qwen3-8B(36 层、8 个 KV 头、每头 128 维)BF16 下每 token 144 KiB,8192 token 的上下文 = 1.125 GiB。沿 200 Gbit/s 网卡(25 GB/s)纯载荷约 48.3 ms。
MLA 让这份交接减半。 DeepSeek-V3 缓存的是上投影之前的潜变量(61 层、、),BF16 下每 token 68.6 KiB,同样 8192 token 只占 549 MiB,25 GB/s 下传输 23.0 ms。原因不是"压缩",而是按结合律把上投影移到了查询一侧执行(代价是 D 侧每 token 多算,见 §4 的紧凑路径代价)。
| 交接状态 | 每 token | 8192 token | 25 GB/s | 50 GB/s |
|---|---|---|---|---|
| Qwen3-8B,BF16 GQA | 144 KiB | 1.125 GiB | 48.3 ms | 24.2 ms |
| DeepSeek-V3,BF16 紧凑 MLA | 68.6 KiB | 549 MiB | 23.0 ms | 11.5 ms |
另一个常被漏掉的约束:交接期间两端同时占内存。 整份双缓冲交接时,传输结束前源端保留完整状态、目的端分配完整接收缓冲 —— GQA 状态两端各 1.125 GiB,合计 2.25 GiB。分块传输能让源端收到一块确认就提前释放一块,缩短重复驻留时间。
三个关键结论(按重要性排序):
① 同构硬件上,PD 分离不提高吞吐 —— 反而更低。 八张 A100 跑同一个模型:共置时每张卡每请求占 GPU 秒,八张合计 3.05 请求/s;分离后只能按整数张卡划分,三张做 P 给 3.50、五张做 D 给 2.83,上限 2.83 请求/s,比共置低 7%。原因是每个请求消耗的 GPU 秒没变,而整数划分让两池能力无法恰好相等,多出的能力只能闲置。这才是同构场景的正确答案:先考虑分块 prefill(chunked prefill),而不是分离。
② 分离真正的价值是延迟稳定 + 配置自由,不是吞吐。 分离后 D 卡每步稳定在 55 ms,P 卡专心处理输入。在途请求的输出间隔不再被长 prefill 打断 —— 这是共置做不到的。同时两个池可以各自选最优的并行方式和 batch size,排队也彻底隔开。
③ 吞吐的大幅提升只出现在异构硬件。 4×A100 做 P + 4×H20 做 D:A100 prefill 比 H20 快一倍多(0.856 s vs 1.81 s),H20 decode 比 A100 快一倍(27.4 ms vs 55.1 ms,因为 H20 显存带宽 4096 GB/s ≈ A100 2049 GB/s 的两倍,而算力不到其一半)。各自避开弱项后:
从共置的 3.02 提到 4.55,1.51×。 反过来配(H20 做 P、A100 做 D)只剩 2.22 请求/s,不到正确分工的一半。异构的收益来自硬件本身的差异,分块替代不了(H20 一步 decode 的空闲算力只够捎带约 85 个 prefill token,理想分块共置也只有 4.17 请求/s < 4.55)。
配比要算,不能拍。 最优配比是让两池能力接近相等:把一张 A100 从 P 调到 D,P 从 4.67 降到 3.50,总吞吐被 P 限制;把一张 H20 从 D 调到 P,D 降到 3.42,同样变慢。一个池有富余能力也无法替另一个池干活。
收益边界随负载翻转(这段最能体现会算账):
| 负载变化 | 最优配比的变化 | 结果 |
|---|---|---|
| 前缀命中 6144 token(P 只需算 2048) | 4 张 A100 做 P 太多 → 改为 2 张 A100 做 P、其余 6 张做 D | 4.55 → 5.69 请求/s |
| 输出缩到 129 token(普通对话,不带推理) | D 不再是瓶颈 → 三张 H20 转去做 P | 分离只比共置高 8.5% |
| 输出增到 4097 token(长推理) | 八张卡无论如何配比都扛不住 3.5 请求/s | 需复制 3–4 组八卡部署 |
两个时间尺度:请求到达时选进哪个队列(决定眼前等待),负载分布持续变化时调整池规模(决定积压能否消退)。
3. 交接的代价:PD 付一次,AF 每步都付#
这是 PD 与 AF 最本质的差别,也是「为什么 AF 通常不划算」的答案。
- PD:每个请求只交接一次完整的上下文 KV,代价随上下文长度增长。
- AF(注意力 / FFN 分离,含专家分离):每层、每步 decode 都要交接激活。Qwen3-8B 一步 decode 是 72 次交接、576 KiB —— 单次很小,但代价随生成步数重复发生。
以 25 GB/s 链路算:一步 AF 交接约 0.384 ms,看着比一次 PD 交接 48.3 ms 快得多。但放到整条请求上,1024 步 AF 交接累计约 393 ms,是 GQA 一次 PD 交接 48.3 ms 的 8.1 倍,是紧凑 MLA 一次交接 23.0 ms 的 17 倍。 输出越长,差距越大。
由此可以求临界启动开销 (让一次 PD 交接与一步 AF 交接耗时相等的每次启动时间):
GQA 状态在 25 GB/s 下 μs,MLA 状态约 324 μs(50 GB/s 下分别 340 / 162 μs)。实际启动只要几微秒,远低于临界值 —— 所以「AF 单步更快」是对的,但整条请求上不成立。而且注意 的走向:状态越小、链路越快,留给每次启动的预算越少 —— AF 的优势窗口在收窄。
AF 的正经用法是专家分离 + micro-batch 流水。 MoE 里把路由专家放到独立池,micro-batch 在注意力节点与专家节点之间交错:一个 micro-batch 做注意力时,另一个做专家计算。忽略交接, 个独立 micro-batch 的双阶段流水为
MegaScale-Infer 论文(Mixtral-8×22B / DBRX / 317B Scaled-MoE,八节点×八张 80GB Ampere,TPOT ≤ 150 ms)报告每 GPU decode 吞吐达 TensorRT-LLM 的 1.90 倍。⚠️ 这个数字是分离放置、并行选择、流水与通信库共同的效果,不能当作「只开启 EP 分离」的通用加速比 —— 论文自己也按专家热度配置冗余副本。
4. 专家放置与大 EP 的 skew#
先看局部性:专家该在 CPU 还是 GPU 上算? 两条路径交付同样的输出,区别是跨链路传什么:传权重(把 36 MiB 的专家权重搬到 GPU),还是传激活(把输入交给 CPU,用本地主存里的权重算)。
以 KTransformers 论文的实验机(双 Xeon 8452Y、1 TB DDR5、A100 40GB PCIe、同插槽内存带宽 220 GB/s)为例,八个专家各处理不同 token 数时:
| CPU kernel | 每个专家约多少 token 时 GPU 开始更快 |
|---|---|
| AVX-512(1.8 TFLOP/s) | 约 71–72 个 |
| AMX(21.3 TFLOP/s) | 约 689 个 |
同一台机器、同一批请求,CPU 用哪套指令集就决定了专家该放哪一侧。 低复用时(每专家 1 个 token),避免搬 36 MiB 权重比提高矩阵算力更有价值(CPU 路径约 1.46 ms vs GPU 路径约 12.5 ms);高复用时反过来。这就是「规模摊薄固定开销」在专家层的样子。
再看大 EP 放大 skew。 直觉上「EP 越大每卡专家越少越均衡」,事实相反。同一批 的分派,若其中一个专家收到平均值的 4 倍:
| EP 规模 | 每卡专家数 | 热点卡相对平均 |
|---|---|---|
| EP8 | 32 | +8%(被同卡其他专家冲淡) |
| EP32 | 8 | +36% |
| EP256 | 1 | +400% |
没有热点时同样成立:均匀随机路由下,EP8 的最忙卡平均只比每卡平均多 4%,EP256 多 52%(固定种子模拟 1000 批)。两个效应同源 —— EP 越大,每卡上能相互抵消的专家越少,而要等待的卡越多。 DeepSeek 公开的 decode 部署用 EP144(每卡 2 个路由专家),位于曲线右端,正是靠冗余专家 + 按负载调整副本来补偿。
注意通信是两次,不是一次。 dispatch(把 token 的隐状态按所选专家分组发出去)与 combine(把结果送回并按路由权重加权求和)各是一次 All-to-All,中间夹着专家计算。同样的 64 MiB 载荷,均衡分布时阶段屏障下界 0.827 ms,热点分布时 2.068 ms(专家 skew 从 1 变成 2.5)。总通信量和总计算量都没变,只是分布变了。
专家副本什么时候回本? 判据是累计节省超过复制耗时:。
- 同一台 HGX H100 内经 NVLink 复制:约 0.622 ms,每批节省 0.240 ms → 第 3 批回本(持续 16 批净省约 3.2 ms)。
- 跨服务器经 ConnectX-7:约 5.32 ms → 要到第 23 批才回本;热点只持续 16 批时反而净损失约 1.5 ms。
结论:跨机复制副本的成本高到要求「热点必须持续得够久」,而同机复制几乎只受显存余量限制。 另外要按层分配副本预算 —— CRAFT(基于 SGLang v0.4.8、八个 p4de 节点的 A100 集群)报告端到端吞吐平均达对照的 1.14 倍、最高 1.2 倍;但这是它自己的实验条件(输入 4096 token 分块、输出固定 256 token),不能直接替代线上的 p99 指标。
5. KV 的分布、共享与路由:缓存不是免费的,也不是越大越好#
① 缓存标识不只是「相同文本」。 模型与 adapter 版本、token 序列、位置索引、状态格式、编码配置都可能参与标识 —— 同样的 token 配上不同 adapter,投影后就是不同的 K/V;同样的后缀接在不同前文后面,注意力看到的东西也不同。滑动窗口或递推状态还要携带 token 位置索引与更新时刻。
② 四级存储,取回都比重新算快。 仍以 1.125 GiB 的 8K 前缀为例(DGX A100 每张卡分到的配置):
| 层级 | 每张 GPU 容量 | 到 GPU 的路径 | 取回 8K 前缀 |
|---|---|---|---|
| HBM | ≤ 63.6 GB | 已在 GPU 上 | 0 |
| 主机内存 | 256 GiB | PCIe 4.0 x16,25 GB/s | 48.3 ms |
| 本地 SSD | 3.84 TB | 顺序读 7.1 GB/s | 170 ms |
| 远端存储池 | 随节点数增 | 网卡 25 GB/s,再经 PCIe | 96.6 ms |
| 对照:在 A100 上重算 | — | 峰值 50% | 856 ms |
四级的取回时间都远小于重算 —— 所以「放哪一级」不是关键问题。真正的限制是另外三个:能不能留到下次用、读取能不能和计算重叠、写下去值不值。
③ 容量必须和复用间隔挂钩。 一张 A100 连续做无命中的 8K prefill,每 0.856 s 产生一份 1.125 GiB 的 KV,产生速率 GB/s。要让间隔 内再次到达的请求都命中:
HBM 只够存约 45 s 内产生的 KV,主机内存约 195 s,SSD 约 45 分钟。而实际复用间隔的分布是:
- 面向个人的对话负载:80% 的复用发生在 10 分钟以内;
- 面向企业的 API 负载:80% 的复用发生在 10 秒以内。
10 秒只需 14.1 GB(HBM 就能装下),10 分钟需要 846 GB(超过每卡 275 GB 的主机内存,得上 SSD)。「该不该上 SSD」的答案完全取决于谁在发起下一轮:人在打字是分钟级,程序调用是秒级。
④ 命中率会饱和 —— 这是「缓存越大越好」的反例。 Mooncake 公开的 Kimi 线上 trace(一小时采样、每块 512 token)在 LRU 淘汰下:
| 缓存块数 | 命中率 |
|---|---|
| 1,000 | 30% |
| 50,000(≈3.77 TB,接近一块 3.84 TB SSD) | 50% |
| 容量不受限 | 51% |
一半以上的块从未被再次使用,另一些被访问上万次(阿里云的 trace 同样集中:10% 的块贡献 77% 的复用)。所以容量规划要按「保留多久、避免几次重算、增加几次搬移」来算,而不是追求命中率。
⑤ 保存 vs 重算的判据。 状态将在未来复用 次时:
对 1.125 GiB 前缀:重算 856 ms,25 GB/s 下写入 + 取回各 48.3 ms,复用一次(96.6 ms)就不到重算的八分之一,所以只要还会再用一次就值得存。但临界带宽只有约 1.41 GB/s —— 恰好等于产生速率 (直觉:链路只要比 GPU 产生这份 KV 更快,取回就比重算快)。低于这个带宽(如 10 GbE 每方向 1.25 GB/s,取回一次约 966 ms)应该重算,或者把取回挪出关键路径。
⑥ 让读取和计算重叠,而不是「先读完再算」。 命中后只需算 256 个新 token(约 30.9 ms),而整份读入要 48.3 ms —— 先读后算共 79.2 ms,读取比计算还长。逐层预加载(算第 层时预读后面的层)降到 49.2 ms,仍由读取决定。要彻底掩盖,需要在请求开始前先读入若干层,缓冲区大小就是读取比计算多出来的那部分字节:
代入 25 GB/s、48.3 ms、30.9 ms 得约 437 MB,略多于 13 层,按整层取 14 层。 就不需要缓冲区 —— 新输入达到约 400 个 token 时,计算本身就能掩盖从主机内存的读取(远端池要约 800 个,本地 SSD 要约 1400 个)。CachedAttention 在 LLaMA-13B 上测得:1K 历史 + 100 新 token 时,逐层预加载让 prefill 缩短 35%,再加 15 层缓冲后缩短 61%。
⑦ SSD 的真正瓶颈是写入寿命,不是带宽。 以 GB/s 写入只占 PCIe 带宽 5.6%、SSD 顺序写带宽 34%,看着很宽裕。但 D7-P5520 按 1 DWPD(五年内每天写满一次)计,3.84 TB 盘平均每秒只能写 44.4 MB —— 把全部新 KV 都写进去是额度的 31.7 倍,五年寿命不到两个月耗尽。长期看只有约 3.2% 的新 KV 能落 SSD。 所以写入前必须做准入判断(例如只写已被复用过的前缀、或尚未结束的多轮会话),而不是「有空间就写」。
⑧ 缓存亲和性路由:比较的是完成时间,不是命中标记。 设 GPU 最早可执行时刻为 、状态就绪时刻为 、状态可用后的剩余计算为 ,则
这个式子的关键是它把「排队」和「取回」放进了同一个 max。 一个典型算例(8192 前缀 + 256 新输入):
| 路径 | 首 token 时间 |
|---|---|
| A:本地命中,但排队 250 ms | 281 ms |
| B:无缓存,直接重算 | 907 ms |
| B:经 50 GbE(6.25 GB/s)远端取回 | 282 ms |
| B:经 200 GbE(25 GB/s)远端取回 | 137 ms |
A 明明命中了却输给 B —— 因为 A 要等 250 ms。两边打平的带宽约 6.30 GB/s(50 GbE 的 6.25 略低于此,200 GbE 远高于此);而取回与重算打平的带宽只有约 1.48 GB/s,所以在 A100 上重算 8K 前缀几乎总是最慢的选择。
再看「命中概率」这把刀。 路由器靠缓存事件预测位置,事件延迟或对象被淘汰会让预测失准。设 A 排队 80 ms、缓存以概率 真正可用,命中 110.9 ms、全失效 967.2 ms,则
只要 ,均值就优于 B 的直接重算(907 ms) —— 看着很容易达标。但 时,10% 的请求仍是 967 ms,这个两点分布的 p99 就是 967 ms,远达不到 220 ms 的目标(要 p99 达标,命中率得 ≥99%)。这是「优化均值 vs 优化尾延迟」最干净的一个例子。
⑨ 先确定优化目标,再决定路由。 缓存优先 vs 空闲优先的实测:缓存优先时目标请求约 1.38 s 完成、两个任务也同时结束;把目标请求移到空闲实例后,它缩短到 0.33 s,但两个任务要到 1.54 s 才全部完成 —— 目标请求快了 1.05 s,全局完成时刻推迟了 0.16 s。做路由决策之前必须问清:优化的是这条请求,还是全部任务。
6. 启动、扩缩容与故障恢复#
启动积压是可以算的。 到达率 、启动时间 ,就绪时积压约 ;就绪后服务率 ,排空时间
沿用异构集群的例子:启动 10 秒、每秒到达 3.5 个 → 积压 35 个;直接 PD 就绪后 4.55 请求/s,每秒净消化约 1.05 个,约 33 秒排空,从开始启动算是第 43 秒。反推最低服务率:50 秒内消除 35 个积压同时还要接新请求, 请求/s。直接 PD 的 4.55 只余 8% 的余量 —— kernel 效率若只有假设值的九成,D 池掉到约 4.10,排空推迟到第 68.5 秒,错过期限。而共置(3.02)的积压会一直增长。
面试里这段的价值:它把「扩容」从一个直觉动作变成了一个带期限的算式。说得出「多出来的算力是用来消化启动积压的,不是用来提高稳态吞吐的」,就已经超过大多数答案。
运行中迁移要处理「源端还在增长」。 迁走一张 H20 上的 D worker:批内 32 条请求平均上下文 8704 token,KV 共约 41.1 GB;源端每秒追加约 0.172 GB,目标以 25 GB/s 复制,约 1.65 s 追上(50 GbE 则要约 6.76 s)。decode 追加 KV 的速率远低于网卡带宽,追赶时间几乎全由初始的 41.1 GB 决定。 更值得注意的是:若切换并行方式后还要建立通信组、准备执行图、恢复接收请求,这些串行软件准备可能占 9 秒 —— 此时网络传输从 2.63 s 压到 0.66 s(快 4 倍),总时间只从 11.6 s 变到 9.7 s,只缩短 17%。瓶颈从网络变成了软件准备,靠更快的链路解决不了。
部分故障的恢复要分两份记录。 输出记录决定「继续哪条序列」,KV checkpoint 决定「还要重做多少计算」 —— 两者末端不重合:输出已经返回,不表示对应的 KV 已经保存。做法是 token 级预写日志(WAL) 保存已确定的输出序列,配额 KV checkpoint;恢复时用已记录的 token 补算重建相同前缀,不必重新采样这些输出(重新采样会改变后缀,对 RL 的 rollout 还会改变样本生成过程)。故障影响范围由同步组决定:张量并行组或专家并行组丢一张卡,可能让整个实例上的请求停住;而其他完整副本可以继续服务 —— 这是「多副本」在容错维度上的价值。
常见面试问法#
Q:单实例优化到头了,接下来怎么办? A:先别急着「加机器」。按改变系统的哪一部分分四类:加完整副本(改变请求分配,换更多并发)、PD 分离(改变两阶段的执行位置,换延迟稳定与配置自由)、AF 分离(改变层内算子位置,换异构利用)、共享 KV(改变状态的保存与读取范围,换跨请求复用)。选择的依据是先算清三件事:各阶段的资源占用时间、状态传输的字节数、以及排队与就绪谁先到。两阶段需求相近就加副本;请求共享长前缀就优先做共享缓存,而不是继续拆计算。
Q:PD 分离能提升吞吐吗? A:要分硬件。 ① 同构硬件上不能,反而更低 —— 八张 A100 共置 3.05 请求/s,整数分池(3 张 P + 5 张 D)只能到 2.83,低约 7%:每个请求消耗的 GPU 秒没变,而整数划分让两池能力无法恰好匹配。② 吞吐大幅提升只出现在异构硬件 —— 4×A100 做 P + 4×H20 做 D(A100 prefill 快一倍多、H20 decode 快一倍,因为 H20 带宽是 A100 两倍而算力不到一半),各自避开弱项后从 3.02 提到 4.55 请求/s,1.51×;反过来配只剩 2.22。③ 分离真正稳赚的是延迟 —— D 卡每步稳定在 55 ms,不被长 prefill 打断;同构下通常应先试 chunked prefill。④ 配比要算:最优是让两池能力接近相等,偏了就有一个池成为瓶颈。
Q:PD 和 AF 有什么本质区别? A:交接的频率和对象不同。 PD 每个请求只交接一次完整的上下文 KV,代价随上下文长度增长;AF 每层、每步 decode 都交接激活,Qwen3-8B 一步 72 次、576 KiB,代价随生成步数重复发生。单看一步,AF 交接(0.384 ms)比 PD(48.3 ms)快得多;但 1024 步累计约 393 ms,是 PD 一次交接的 8.1 倍(对 MLA 状态是 17 倍)。而且临界启动时间 只有几百微秒,状态越小、链路越快,AF 的优势窗口越窄。AF 的正经用法是 MoE 专家分离 + micro-batch 流水(MegaScale-Infer 报 1.90×,但那是分离放置、并行选择、流水与通信库的共同效果,不能当「只开 EP」的加速比)。
Q:专家并行(大 EP)是不是越大越均衡? A:恰好相反 —— 大 EP 会放大 skew。 同一个 4 倍热点专家:EP8 时热点卡只比平均多 8%(被同卡其他 31 个专家冲淡),EP32 多 36%,EP256 时每卡只剩一个专家,多 400%。没有热点时同样成立:均匀随机路由下 EP8 最忙卡多 4%,EP256 多 52%。原因是每卡上能相互抵消的专家变少,而要等待的卡变多。DeepSeek 的 decode 部署用 EP144 并配 32 个冗余专家 + 按负载调整副本,正是补这个。另外通信是两次 All-to-All(dispatch 与 combine),同样 64 MiB 载荷下,均衡分布阶段屏障 0.827 ms、热点分布 2.068 ms —— 总量没变,只是分布变了。
Q:专家副本值得做吗? A:看热点能持续多久和复制走哪条链路。 判据是累计节省超过复制耗时:同一台 HGX 内经 NVLink 复制约 0.622 ms,每批省 0.240 ms,第 3 批回本(热点持续 16 批净省约 3.2 ms);跨服务器经 ConnectX-7 要约 5.32 ms,要到第 23 批才回本 —— 热点只持续 16 批时反而净亏约 1.5 ms。所以跨机复制要求热点足够持久,同机复制几乎只受显存余量限制。还要按层分配副本预算(把空间给最能缩短等待的层),CRAFT 报告平均 1.14×、最高 1.2×,但那是它自己的实验条件。不要改 top-k 或替换逻辑专家来做均衡 —— 那会改变模型行为,不属于透明的资源调整。
Q:多实例之间怎么共享 KV?容量要多大? A:先说缓存标识:相同文本不足以唯一确定可复用状态,还要含模型与 adapter 版本、位置索引、状态格式。容量要按复用间隔定:,其中 GB/s 是单卡连续无命中 8K prefill 的产生速率 —— 10 秒只需 14.1 GB(HBM 够),10 分钟需要 846 GB(得上 SSD)。实测复用分布差异极大:个人对话负载 80% 的复用发生在 10 分钟内,企业 API 负载 80% 在 10 秒内。 但容量越大命中率提升越慢:Mooncake 的线上 trace 上,块数从 1,000 增到 50,000,命中率 30%→50%,不限容量也只有 51%,因为一半以上的块从未被再用。所以按「保留多久、避免几次重算、增加几次搬移」算,而不是追命中率。保存 vs 重算:,临界带宽约 1.41 GB/s(恰好等于 )。SSD 的真正瓶颈是写入寿命(1 DWPD 下只有约 3.2% 的新 KV 能长期落盘),必须先做准入判断。
Q:缓存亲和性与空闲优先,请求该路由到哪台机器? A:比较完成时间,而不是看有没有命中标记。 首 token 时间为 (排队、状态就绪、剩余计算)—— 关键是排队和取回进的是同一个 max。典型算例:A 本地命中但要排队 250 ms → 281 ms;B 无缓存直接重算 → 907 ms;B 从 50 GbE 远端取回 → 282 ms;换成 200 GbE → 137 ms,反超 A。两边打平的带宽约 6.30 GB/s;取回与重算打平的带宽只有约 1.48 GB/s,所以在 A100 上重算 8K 前缀几乎总是最慢的选择。还要看尾延迟:命中概率 只要 >7% 均值就优于重算,但 时那 10% 仍是 967 ms,两点分布的 p99 就是 967 ms —— 要 p99 达标需要 ≥99% 命中。最后先确定优化目标:缓存优先能让目标请求从 1.38 s 缩到 0.33 s,但全局完成时刻反而推迟 0.16 s。
易错点#
| 易错说法 | 问题 | 应该说 |
|---|---|---|
| 「PD 分离提升吞吐」 | 同构硬件上反而更低 | 同构分离低约 7%(3.05 → 2.83),价值在延迟稳定与配置自由;大幅提吞吐只在异构硬件(A100+H20 → 1.51×) |
| 「同构上也该上 PD 分离」 | 跳过了更便宜的那条路 | 同构优先 chunked prefill(把 prefill 切块捎带进 decode 步的访存空闲里) |
| 「分池就能扩容」 | 忽略整数划分与瓶颈池 | 配比要让两池能力接近相等;一个池有富余也替不了另一个池。给 P 加卡不解决 D 的瓶颈 |
| 「AF 分离比 PD 快,因为单次传得少」 | 只看了单步 | AF 每步都传:1024 步累计 393 ms,是 PD 一次交接的 8.1 倍(MLA 下 17 倍)。AF 的价值在专家分离 + micro-batch 流水 |
| 「EP 越大越均衡」 | 方向反了 | 大 EP 放大 skew:EP8 热点卡 +8%,EP256 +400%;无热点时也 +4% → +52% |
| 「专家副本总能省时间」 | 漏了回本条件 | 同机 NVLink 第 3 批回本,跨机 ConnectX-7 要第 23 批;热点不够久时跨机复制净亏 |
| 「均衡就是改 top-k / 换掉冷专家」 | 混淆了资源调整与模型行为 | 改 top-k 或替换逻辑专家会改变模型行为,不能当作透明的负载均衡 |
| 「容量越大命中率越高,所以缓存越大越好」 | 忽略了饱和 | Mooncake trace 上 1,000→50,000 块只把命中率从 30% 提到 50%,不限容量也只有 51%;一半以上的块从未被再用 |
| 「KV 放 SSD 就行」 | 漏了写入寿命 | 1 DWPD 下 3.84 TB 盘每秒只能写 44.4 MB,只有约 3.2% 的新 KV 能长期落 SSD,写入前要做准入 |
| 「缓存命中了就该路由过去」 | 忘了排队 | 比的是完成时间 —— 本地命中但排队 250 ms(281 ms)会输给远端取回(137 ms) |
| 「平均延迟改善了就说明方案好」 | 漏了尾延迟 | 就改善均值,但 p99 由那 的慢路径决定 —— 要 p99 达标需要 ≥99% 命中 |
| 「扩容就是加卡提高吞吐」 | 忘了启动积压 | 多出的能力常是用来消化启动积压的:;共置 3.02 在 3.5 到达率下积压永不消退 |
| 「迁移慢是网络带宽不够」 | 归因错 | 追赶时间由初始状态量决定(41.1 GB / 25 GB/s ≈ 1.65 s);网络提速 4 倍,总时间只缩短 17%,瓶颈在通信组与执行图的软件准备 |
| 把本章的部署方案说成「我们线上就这么搭的」 | 触碰仓库硬规矩 | 这是书里的推导与实测,非一手经验;用「这套账我会这么算」「如果让我选我会先算哪三项」的口径讲 |
自测#
1. 客户说「我们的推理服务扛不住了,加了几台机器做 prefill/decode 分离,结果吞吐反而降了」。你怎么解释,并给出下一步建议?
答案
先怀疑是同构硬件。 在同构集群上,PD 分离不提吞吐、反而可能更低:八张 A100 共置时每个请求占 GPU 秒,八张合计 3.05 请求/s;分离后只能按整数张卡划分(3 张 P 给 3.50、5 张 D 给 2.83),上限 2.83 请求/s,比共置低 7%。原因是每个请求消耗的 GPU 秒完全没变,而整数划分让两池能力无法恰好相等,多出的能力只能闲置。
下一步建议按顺序做三件事:① 先确认硬件是否异构 —— 混合 A100 + H20 才可能拿到大幅提升(4×A100 做 P + 4×H20 做 D → 4.55 vs 共置 3.02,1.51×),因为 A100 prefill 快一倍多、H20 decode 快一倍(H20 显存带宽是 A100 的两倍而算力不到一半)。② 如果同构,先退回共置改用 chunked prefill —— 把长 prefill 切成块、捎带进 decode 步的访存空闲里执行;A100 一步 decode 的访存要 55.1 ms 而矩阵计算只需 4.2 ms,这段空闲足以处理约 490 个 prefill token,8192 token 分 17 块完成,首 token 约 0.94 秒,在途请求的输出间隔不受影响。③ 如果确实异构,检查配比 —— 最优配比是让两池能力接近相等;把一张卡从 P 调到 D,P 能力下降就会让整体被 P 限制(4.67→3.50)。
同时要说清:分离还有一项同构也拿得到的好处 —— 延迟稳定(D 卡每步固定 55 ms,不被长 prefill 打断)。如果他们的目标本来就不是吞吐而是尾延迟,那分离没做错,只是目标说错了。
2. 你要为一个 Qwen3-8B 服务做 PD 分离,请算出:8192 token 上下文的交接时间、交接期间两端的内存占用、以及这条链路每秒最多支持多少次交接。
答案
① 单份状态大小:Qwen3-8B 是 36 层、8 个 KV 头、每头 128 维、BF16()。每 token 的 KV 为 bytes KiB。8192 token:
② 交接时间:200 Gbit/s 网卡折合 25 GB/s,纯载荷
再加一次 5 μs 的启动同步,总计仍约 48.3 ms —— 对整份状态,固定开销只占万分之一,提高载荷带宽才是有效的(换 50 GB/s 就降到 24.2 ms)。
③ 两端内存占用:整份双缓冲交接时,源端保留完整状态、目的端分配完整接收缓冲,两端各 1.125 GiB,合计 2.25 GiB。这是容量规划里最容易漏的一项。分块传输可以让源端收到一块确认就提前释放一块,缩短重复驻留时间。
④ 每秒可交接次数: 次/s。若换成 DeepSeek-V3 的紧凑 MLA 状态(每 token 68.6 KiB,8192 token = 549 MiB),同样链路可支持约 43.4 次/s。
补充一个判断:链路是否成为瓶颈,条件是 。在 4.55 请求/s 下,GQA 状态要求链路至少约 5.5 GB/s,紧凑 MLA 只要约 2.6 GB/s —— 高于此值再提高带宽不会改变吞吐上限,低于此值所有配置都会被链路压平到同一个值。
3. 有人主张「把每层的 FFN 放到另一组卡上,单次传输只有几百 KB,比 PD 一次传 1 GB 划算多了」。这个论证错在哪?
答案
错在只看了单次交接,没看交接次数。 PD 是每个请求交接一次完整上下文 KV,代价随上下文长度增长但只付一次;AF 是每层、每步 decode 都交接激活,单次很小但代价随生成步数重复发生。
算给他看:Qwen3-8B 一步 decode 是 72 次交接、576 KiB,沿 25 GB/s 链路纯载荷约 23.6 μs,加 72 次 5 μs 启动共约 0.384 ms —— 确实远快于一次 PD 交接的 48.3 ms。但整条请求(1024 次 decode)累计约 393 ms,是 GQA 一次 PD 交接的 8.1 倍、紧凑 MLA 一次交接的 17 倍。输出越长,差距越大。
还有个更本质的点:AF 传的是 4096 维隐状态,和 KV 表示无关 —— 换成 MLA 一点字节都省不下来(仍是 72 次、576 KiB);而 PD 的交接量直接从 1.125 GiB 降到 549 MiB。MLA 只对 PD 这一侧的交接有帮助。
严格的判据是临界启动开销 :GQA 状态在 25 GB/s 下约 680 μs、MLA 状态约 324 μs。实际启动只有几微秒,远低于临界值 —— 所以「AF 单步更快」是对的,但整条请求上不成立。注意 的走向:状态越小、链路越快,留给每次启动的预算越少 —— AF 的优势窗口在收窄。
AF 的正确用法不是这个,而是 MoE 的专家分离 + micro-batch 流水:一个 micro-batch 做注意力时另一个做专家计算,理想下 个 micro-batch 需要 。MegaScale-Infer 报告每 GPU decode 吞吐达 TensorRT-LLM 的 1.90 倍,但那是分离放置、并行选择、流水与通信库的共同效果,不能当「只开 EP 分离」的加速比。
4. 一个 agent 产品的前缀很长(系统提示 + 工具定义 + 多轮历史),80% 的请求会复用同一段前缀。你会怎么设计缓存与路由?哪些数字决定容量?
答案
先定缓存标识,再定容量,最后定路由。
① 标识不能只看文本。 相同 token 配上不同 adapter 会得到不同的 K/V;相同后缀接在不同前文后面,注意力看到的也不同;滑动窗口/递推状态还要带 token 位置索引与更新时刻。缓存键必须沿前缀链标识出生成这份状态的整个计算。
② 容量由「复用间隔」决定,不是拍一个数。 ,其中 GB/s 是单卡连续无命中 8K prefill 的产生速率。关键是 由谁发起下一轮决定:实测个人对话负载 80% 的复用发生在 10 分钟内(需要 846 GB,超过每卡 275 GB 主机内存,得上 SSD),企业 API 负载 80% 在 10 秒内(只需 14.1 GB,HBM 就够)。agent 产品通常是程序驱动、间隔以秒计 → 大概率 HBM + 主机内存就够,不必上 SSD。
③ 但别按命中率买容量。 Mooncake 线上 trace:块数 1,000 → 50,000,命中率只从 30% 升到 50%,不限容量也只有 51% —— 一半以上的块从未被再用。按「保留多久、避免几次重算、增加几次搬移」算:,复用一次就划算(96.6 ms vs 重算 856 ms),临界带宽约 1.41 GB/s(恰好等于 )。若真要落 SSD,必须先做准入:1 DWPD 下 3.84 TB 盘每秒只能写 44.4 MB,全部写入是额度的 31.7 倍,长期只有约 3.2% 的新 KV 能落盘。
④ 让读取与计算重叠,而不是先读完再算。 命中后只需算新增 token(约 30.9 ms),整份读入要 48.3 ms —— 先读后算 79.2 ms,读取反超计算。用逐层预加载 + 预读缓冲 MB(约 14 层),可把读取完全掩盖,降到 30.9 ms。 时不需要缓冲 —— 新输入约 400 token 以上就够掩盖主机内存的读取。
⑤ 路由比的是完成时间,不是命中标记。 :本地命中但排队 250 ms 会是 281 ms,输给从 200 GbE 远端取回的 137 ms。两边打平带宽约 6.30 GB/s,取回与重算打平只有约 1.48 GB/s。agent 场景特别要注意尾延迟:命中概率只要 >7% 均值就优于重算,但 时那 10% 的请求仍走 967 ms 慢路径,p99 就是 967 ms —— 目标若是 220 ms 的 p99,需要 ≥99% 命中。
⑥ 最后问一句优化目标:是让这条请求更快,还是让全部任务更早完成?实测两者会背离(目标请求快 1.05 s,全局完成推迟 0.16 s)。
5. 一个 8 卡实例从停止状态启动要 10 秒,到达率 3.5 请求/s,要求启动后 60 秒内清空积压。你会怎么判断方案是否可行?这套判据还能用在哪?
答案
① 先算积压:启动期间新请求照常到达,就绪时积压约 个。
② 再算排空时间:。
- 直接 PD(异构): → 每秒净消化 个 → 约 33 秒排空,从开始启动算是第 43 秒,满足 60 秒目标。
- 理想分块共置: → 净消化 0.67 → 第 63 秒,错过期限。
- 不分块共置: → 积压持续增长,永远排不空。
③ 反推最低要求:50 秒要消除 35 个,同时还要接住每秒 3.5 个新请求,所以
直接 PD 的 4.55 只余 8% —— 这是关键的风险点:kernel 效率若只有假设值的九成,D 池掉到约 4.10,排空推迟到第 68.5 秒,直接错过期限。 所以容量规划不能按标称值卡着期限做,要留余量。
④ 这套判据的通用形式:先把每个请求的工作量换算成各类资源需求 → 按整数资源分配找出最忙的资源池 → 按执行顺序计入状态传输时间与内存占用 → 最后用服务率减到达率得到的净处理能力,求突发或启动积压的持续时间。瓶颈在 D 就加 P 没用;瓶颈在共享通道,加计算副本只会让更多请求排队等待取回。
它还能用在:① 任何有状态的滚动升级(新版本实例启动期间的积压);② 故障恢复时长的估算(挂掉的实例重建要多久才能吸收积压);③ 灰度扩容的节奏设计。这里还要补一句恢复侧的知识:输出记录决定继续哪条序列,KV checkpoint 决定还要重做多少计算 —— 两者末端不重合(输出已返回不等于 KV 已保存),所以要 token 级 WAL + KV checkpoint 两份记录,恢复时用已记录的 token 补算重建前缀,避免重新采样(重采样会改变后缀,对 RL 的 rollout 还会改变样本生成过程)。