LLM 推理的 AF 分离与 PD 分离——disaggregation 全解

本文讲清现代 LLM 推理 serving 里的两类"分离(disaggregation)“技术:PD 分离(Prefill-Decode 分离)AF 分离(Attention-FFN 分离)。两者都是把原本"混在一起跑"的工作流按资源/瓶颈差异拆开,独立调度、独立优化、必要时独立部署。读完你能回答面试官"为什么要把 prefill 和 decode 拆开”“KV cache 怎么跨节点搬”“attention 和 FFN 为什么能分离”“两者能不能叠加用”。

术语说明:PD 分离是业界共识术语(Splitwise/Mooncake/DistServe)。AF 分离术语相对不那么标准化,本文取Attention-FFN 分离/解耦的含义——把 Transformer 内的 attention 计算与 FFN(MLP) 计算解耦,做算子级或微批级重叠调度(DeepSeek Dual-Batch Overlap 思路)。若你语境里的 AF 另有含义(如 Async-Fetch),可与本文思路类比。


一、为什么要"分离"——两个根本矛盾

LLM 推理有两个与生俱来的"不对称",是所有 disaggregation 的出发点:

矛盾 1:prefill 与 decode 的资源画像完全相反

Prefill(首 token 阶段) Decode(逐 token 阶段)
算一次的 token 数 整个 prompt(几百~几万) 1 个(+ spec decode 时几个)
算力/访存比 高(compute-bound) 低(memory-bound)
并行度 高(prompt 内 token 并行) 低(自回归串行,batch 内可并行但每步 token 少)
GPU 利用 高(大 GEMM 喂饱 SM) 低(小算子、SM 空闲、等访存)
KV cache 一次性算出全部 逐个追加,占大头

把两者放同一 batch 混跑(vLLM V0/V1 的 continuous batching 是迭代级混跑)会有资源互相干扰

  • prefill 的大 GEMM 抢算力,把 decode 的 token 挤延迟;
  • decode 的小算子拉低 batch 的整体算力利用率;
  • prefill 突发(长 prompt 进来)打断 decode 的稳定节奏。

矛盾 2:Transformer 内 attention 与 FFN 的瓶颈不同

一层 Transformer = attention + FFN(MoE 下 FFN 是稀疏 expert)。两者的算力/访存画像也不同:

  • Attention:算的是 QKTsoftmax×VQK^T \to \text{softmax} \to \times V,长上下文时 O(s2)O(s^2)访存/带宽敏感(尤其 decode 时 KV cache 读取是瓶颈)。
  • FFN/MLP:算的是几个大 GEMM(d4ddd \to 4d \to d 或 SwiGLU 三矩阵),算力密集,大矩阵能喂饱 tensor core。

把两层混着顺序执行(标准做法),会出现"算 attention 时 FFN 的算力单元闲着、算 FFN 时 attention 的访存通道闲着"的利用率凹陷。

分离的核心思想把资源画像不同、瓶颈不同的部分拆开,让各部分独立调度/独立部署/互相重叠,消除干扰、把硬件利用率拉满。 PD 分离针对矛盾 1(阶段级),AF 分离针对矛盾 2(算子级)。


二、PD 分离(Prefill-Decode Disaggregation)

2.1 思想

把 prefill 和 decode 从同一批 worker 拆到两个独立的 worker 池

  • Prefill pool:专门跑 prefill(算 prompt 的 KV,产第一个 token)。compute-bound,配高算力卡、大 batch、CUDA Graph/大 GEMM 优化。
  • Decode pool:专门跑 decode(逐 token 生成)。memory-bound,配大显存/带宽、长 batch、KV cache 优化。
  • prefill 算完一个请求的 KV 后,把 KV cache 迁移到 decode pool,由 decode 节点接力生成。
1
请求 ─► Prefill pool(算 KV + 首 token) ──KV 迁移──► Decode pool(逐 token 生成) ─► 返回

2.2 为什么能赢

  1. 消除干扰:prefill 不再打断 decode 节奏,decode 节点可连续跑高 batch、稳定低尾延迟。
  2. 各自调优:prefill 池配大算力开大 GEMM、decode 池配大显存/高带宽堆 batch,硬件按画像配置而非折中。
  3. batch 更纯:prefill 池里全是 prefill(可长 prompt 拼批)、decode 池里全是 decode(可大 batch 拼),每个池内同质、调度简单、利用率高。
  4. 弹性:prefill 和 decode 负载特征不同(TTFT 敏感 vs 吞吐/TPOT 敏感),可独立扩容——prefill 突发多配 prefill 卡、decode 长输出多配 decode 卡。

2.3 核心挑战:KV cache 迁移

这是 PD 分离最难的工程点。prefill 算出的 KV 要搬到 decode 节点,KV 量 =4Ldsb= 4Ldsb(见显存篇),长上下文时几个 GB 起步。

  • 怎么搬:靠高带宽网络——RDMA/GPUDirect-RDMA 把 prefill 节点 GPU 显存的 KV 直接写到 decode 节点 GPU 显存(不经 host 内存中转),或走 CPU/NVMe 分层(热 KV 在 GPU、温在 CPU、冷在 NVMe)。
  • 开销:KV 迁移有延迟和带宽成本,若迁移时间 > 它省下的干扰时间就亏。所以 PD 分离要配高带宽互联(IB/RoCE/NVLink-domain),且只在迁移代价可控时划算。
  • Mooncake 的做法:KV-cache 中心化——prefill 节点把 KV 写进一个 KV 池(CPU+GPU+SSD 多层),decode 节点从池里取,KV 在多请求间可复用(共享 prefix)。vLLM V1 的 sleep()/wake_up()kv_connectorcoordinator 也实现这层。

2.4 其它挑战

  • Prefix cache 连续性:连续批处理里同 prefix 共享 KV,分离后跨池复用要专门设计(Mooncake 的 KV 池化、vLLM 的 prefix cache 跨节点)。
  • 负载均衡:prefill 和 decode 负载比随请求分布变(长短不一),两池配比要动态,不然某池积压。
  • 调度:何时把 prefill 完的请求"交接"给 decode 池,要避免 decode 池空等或 prefill 池拥塞——双池队列调度是个非平凡问题。
  • 显存碎片:decode 池大 batch + 长 KV,靠 PagedAttention 分页管(vLLM)。

2.5 代表系统

  • Splitwise(微软):PD 分离 + KV 迁移,量化分析最优配比。
  • Mooncake(月之暗面/Kimi):KV-cache-centric disaggregated,KV 池化、prefill/decode 解耦、高吞吐长上下文。
  • DistServe:把 prefill 和 decode 分到不同 GPU set,证明 disaggregation 在 TTFT/TPOT 两端都更好。
  • vLLM V1:原生支持 PD 分离(sleep/wake、kv_connector、coordinator、KV offload/tiering),对接 Mooncake/Dynamo 等。

面试金句:“PD 分离把 compute-bound 的 prefill 和 memory-bound 的 decode 拆到两个独立池,消除干扰、各自调优、独立扩容。命门是 KV cache 跨节点迁移——靠 RDMA/GPUDirect-RDMA 把 KV 从 prefill 节点搬到 decode 节点,或走 Mooncake 式的 KV 池化。只有迁移代价 < 省下的干扰代价时才划算,所以离不开高带宽互联。”


三、AF 分离(Attention-FFN 分离/解耦)

3.1 思想

把一层 Transformer 里的 attention 计算和 FFN(MLP) 计算解耦,让它们可以:

  • 在不同时间点跑(重叠调度,把 A 的空闲塞进 B 的算力);
  • 或放在不同硬件/SM/流上并行;
  • 或独立 batch(attention batch 和 FFN batch 分开组批)。

核心是利用 attention 和 FFN 的算力/访存画像差异做重叠,把一层的"串行 attention→FFN"变成"流水/重叠"。

3.2 为什么能赢

标准做法一层内是串行:norm → attention → norm → FFN,一个算完才下一个。问题:

  • attention 跑时 FFN 的 tensor core 闲(attention 访存敏感、tensor core 吃不饱);
  • FFN 跑时 attention 的访存通道闲(FFN 算力密集、访存通道空闲)。

如果把两个微批交错:

1
2
3
batch A: attention ──────────► FFN ──────►
batch B: attention ───────► FFN ──►
▲ A的attention时 B的FFN在跑(或反过来), 算力和访存重叠

A 的 attention(访存 bound)和 B 的 FFN(compute bound)在同一 GPU 上可重叠——一个吃访存一个吃算力,互补而非争抢。于是单卡利用率从"两段都半闲"拉到"两段都满",吞吐提升。

3.3 DeepSeek Dual-Batch Overlap(典型实现)

DeepSeek-V3 的双批重叠(Dual-Batch Overlap)就是 AF 分离的工程化:

  • 把一个 batch 切成两半(dual batch)。
  • 前一半的 attention 和后一半的 FFN 在同一 GPU 上用不同 CUDA stream 重叠执行——attention 走访存、FFN 走算力,互补填满 GPU。
  • 配合 stream/event 做依赖同步(A 的 attention 结果是 A 的 FFN 输入,要等)。

这本质是算子级流水:把"attention + FFN"的串行变成两批之间的流水重叠,类似把"流水线气泡"用另一批的工作填上——和 PP 的 interleaved 思想同源,但粒度细到层内 attention/FFN。

3.4 MoE 场景下的 AF 分离更香

MoE 模型(DeepSeek-V3、Mixtral)里 FFN 是稀疏的 MoE 层:

  • attention 是 dense(所有 token 都走同一个 attention);
  • FFN 是 sparse(每 token 只路由到 K 个 expert)。
    两者算力/访存/调度特征差异更大,解耦后可各自 batch:attention batch 按 token 组织、MoE 按 expert 负载组织(all-to-all dispatch),独立调优。MoE 的 dispatch all-to-all 和 attention 还能进一步重叠。

3.5 实现层次

AF 分离可在多个粒度做:

  • 算子级重叠(dual-batch / dual-stream,单卡内 attention/FFN 重叠)——成本最低、收益直接。
  • 微批流水(多个 micro-batch 在 attention/FFN 两段间流水)——类似 PP 的 1F1B,但段是 attention/FFN。
  • 跨硬件/SM 分配(极端:attention 和 FFN 放不同硬件或固定 SM 分区,heterogeneous placement)——研究/特殊部署,工程复杂。

3.6 挑战

  • 依赖同步:A 的 FFN 依赖 A 的 attention 输出,必须 stream/event 同步,重叠窗口受最长段约束。
  • 显存:要同时持有两个微批的中间激活,显存增加。
  • 调度复杂:双批切分要平衡、stream 编排要正确,bug 难调。
  • 收益前提:attention 和 FFN 真的要"一个访存 bound 一个 compute bound"才能互补;若都 compute bound 重叠收益小(所以长上下文/MoE 场景收益大,短 prompt 普通 FFN 收益小)。

面试金句:“AF 分离把一层内 attention(访存 bound)和 FFN(compute bound)解耦,用 dual-batch/dual-stream 让 A 的 attention 和 B 的 FFN 重叠——一个吃访存一个吃算力,把单卡利用率从’两段都半闲’拉满。DeepSeek-V3 的 Dual-Batch Overlap 是典型实现。MoE 下 attention dense/FFN sparse 差异更大,解耦收益更香。”


四、PD 与 AF 的对比

PD 分离 AF 分离
拆什么 阶段(prefill / decode) 算子(attention / FFN)
粒度 节点/池级 算子/微批/stream 级
针对矛盾 prefill compute-bound vs decode memory-bound attention 访存-bound vs FFN compute-bound
典型手段 独立 worker 池 + KV 跨节点迁移 dual-batch / dual-stream 重叠
通信负担 重(KV cache GB 级跨机搬) 轻(同卡内 stream 同步,激活不大)
部署复杂 高(双池、KV 池、调度、负载均衡) 中(双批切分、stream 编排)
对硬件要求 高带宽互联(IB/RoCE/GPUDirect-RDMA) 单卡多 stream 算力/访存互补
代表 Splitwise / Mooncake / DistServe / vLLM V1 DeepSeek-V3 Dual-Batch Overlap

两者互补、可叠加

  • PD 分离解决阶段间资源冲突(prefill/decode 不互相挤)。
  • AF 分离解决算子间资源浪费(attention/FFN 不互相等)。
  • 一个在外(节点池分离),一个在内(单卡算子重叠),正交可叠加:prefill 池内部用 AF 重叠提 prefill 利用率、decode 池内部也可用 AF(decode 的 attention 是 KV 访存大头、FFN 是小 GEMM,重叠仍有空间)。Mooncake/DeepSeek 类系统往往两者都用——PD 分离做架构、AF 重叠做算子级优化。

五、什么时候用、什么时候别用

PD 分离适合

  • 长上下文/长输出占比高(prefill 与 decode 画像差异大、干扰重)。
  • 集群大、有高带宽互联(KV 迁移代价可承担)。
  • TTFT 和 TPOT 有独立 SLA 要分别优化。
  • 别用:单卡小集群、KV 迁移代价 > 收益、请求短且均匀(prefill/decode 差异小,混跑 continuous batching 就够)。

AF 分离适合

  • 长上下文(attention 访存 bound 明显)或 MoE(attention dense / FFN sparse 差异大)。
  • 单卡利用率凹陷明显(profile 出 attention/FFN 没喂饱)。
  • 别用:短 prompt + 普通 dense FFN(两段都偏 compute bound,重叠互补收益小,徒增复杂度和显存)。

先做简单的:continuous batching + PagedAttention + prefix cache + CUDA Graph + 融合 kernel 这些"非分离"优化先吃干净,再上 PD/AF。分离是"榨干最后利用率"的高级手段,复杂度代价大,别为用而用。


六、面试速答清单

Q1:PD 分离是什么,为什么能提速?

把 prefill(compute-bound)和 decode(memory-bound)从同一 worker 拆到两个独立池。好处:消除两阶段资源干扰、各自按画像调优(prefill 大 GEMM 高算力、decode 大显存高带宽堆 batch)、独立扩容(TTFT/TPOT 分别 SLA)。命门是 KV cache 跨节点迁移——靠 RDMA/GPUDirect-RDMA 或 Mooncake 式 KV 池化,迁移代价要小于省下的干扰代价,所以离不开高带宽互联。

Q2:PD 分离的 KV cache 迁移怎么解决?

prefill 算完 KV 后要搬到 decode 节点。靠 GPUDirect-RDMA 跨节点直接写显存(不经 host 中转)最快;Mooncake 把 KV 池化在 GPU+CPU+SSD 多层,prefill 写池、decode 取池,还能跨请求复用同 prefix 的 KV。vLLM V1 用 sleep/wake、kv_connector、coordinator 实现这层。迁移是 GB 级跨机数据,是 PD 分离性能/工程的核心矛盾。

Q3:AF 分离是什么,attention 和 FFN 为什么能分离?

把一层内 attention(访存 bound、KV 读取是瓶颈)和 FFN(compute bound、大 GEMM)解耦,用 dual-batch/dual-stream 让 A 的 attention 和 B 的 FFN 重叠执行——一个吃访存一个吃算力,互补填满 GPU,把"两段都半闲"拉到"两段都满"。DeepSeek-V3 Dual-Batch Overlap 是典型。MoE 下 attention dense / FFN sparse 差异更大收益更香。

Q4:PD 和 AF 是不是冲突?能不能一起用?

不冲突,正交可叠加。PD 在节点池级解决阶段间资源冲突,AF 在单卡算子级解决 attention/FFN 资源浪费——一外一内。prefill 池内部可用 AF 提 prefill 利用率,decode 池内部也可用 AF(decode attention 是 KV 访存大头、FFN 小 GEMM,重叠仍有空间)。Mooncake/DeepSeek 类系统往往两者都用。

Q5:什么场景不适合上分离?

PD 别用:单卡小集群、KV 迁移代价超收益、请求短且均匀(混跑 continuous batching 就够)。AF 别用:短 prompt + dense FFN(两段都偏 compute bound,互补收益小徒增复杂度和显存)。原则:continuous batching + PagedAttention + prefix cache + CUDA Graph + 融合 kernel 这些非分离优化先吃干净,再上 PD/AF——分离是榨干最后利用率的高级手段,别为用而用。

Q6:和 vLLM V1 continuous batching 比呢?

vLLM V1 的 continuous batching 是迭代级混跑——prefill 和 decode 在同一 worker、同一 batch 用 token 预算调度混跑,不分池、不搬 KV。它简单、对小集群/通用场景够用,但解决不了"prefill 突发打断 decode"“两阶段画像差异大需独立调优”。PD 分离是它的进阶——把混跑拆成分离,代价是 KV 迁移和双池调度。vLLM V1 同时支持两者(continuous batching 内核 + PD 分离的 sleep/wake/kv_connector)。


七、一张图收口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PD 分离(阶段级, 节点池):                  AF 分离(算子级, 单卡):
请求 层内: norm→attention→norm→FFN
│ 串行: attention跑时FFN闲, FFN跑时attn访存闲
▼ ▼ 解耦 + 双批重叠
Prefill 池 (compute-bound, 大GEMM) batchA: attn ──────► FFN ──►
│ 算 KV + 首 token, 写入 KV 池 batchB: attn ─────► FFN
│ ──RDMA 搬 KV──► (A的attn访存 bound 与 B的FFN compute bound 互补重叠)

Decode 池 (memory-bound, 大显存高带宽)
│ 逐 token 生成, PagedAttention 管 KV

返回

正交可叠加: Prefill 池内部用 AF 提利用率, Decode 池内部也可用 AF

主线一句话:PD 分离在节点池级把 prefill(compute-bound) 和 decode(memory-bound) 拆开靠 KV 跨节点迁移接力;AF 分离在单卡算子级把 attention(访存-bound) 和 FFN(compute-bound) 用双批重叠填满利用率。一外一内、正交可叠加——但都是"榨干最后利用率"的高级手段,先把 continuous batching/PagedAttention/prefix cache/CUDA Graph/融合 kernel 吃干净再上。 把这两类分离的动机、手段、命门(KV 迁移 / 依赖同步)、适用边界讲顺,disaggregation 这关就稳了。


参考资料

  • Splitwise (Agrawal et al., ISCA 2024): Splitwise: Efficient Generative LLM Inference Using Phase Splitting
  • DistServe (Zhong et al., OSDI 2024): DistServe: Disaggregating Prefill and Decoding for Goodput-optimized LLM Serving
  • Mooncake (Qin et al., 2024): Mooncake: A KVCache-centric Disaggregated Architecture
  • DeepSeek-V3 技术报告:Dual-Batch Overlap / MoE serving
  • vLLM V1 core/kv_offload/engine/coordinator.pyworker/gpu/kv_connector.py
  • 与本文 vLLM 源码精读、RDMA、显存计算法则、GPU Kernel/Stream 篇交叉对照