DualPath 完全拆解:双路径 KV-Cache 加载,把解码引擎的闲置网卡变成智能体推理加速器(SIGCOMM 2026)

7781 字
39 分钟
DualPath 完全拆解:双路径 KV-Cache 加载,把解码引擎的闲置网卡变成智能体推理加速器(SIGCOMM 2026)

AI 生成内容声明

背景:当 LLM 从聊天机器人变成”智能体”,推理瓶颈从算力变成了磁盘带宽#

本文的主角 DualPath 由 DeepSeek 与北京大学、清华大学联合完成,第一作者吴永彤是北大金鑫教授组的博士生(DeepSeek 系统组实习生),论文于 2026 年 2 月 25 日发布在 arXiv(arXiv:2602.21548),并被 ACM SIGCOMM 2026 接收(DOI: 10.1145/3789240.3829159)。它解决的是一个被大多数人忽略、却在生产环境中越来越致命的问题:多轮智能体推理中,从磁盘加载 KV-Cache 的速度成了整个系统的天花板

先铺垫:为什么推理系统的瓶颈会从 GPU 计算漂移到磁盘 I/O?这要从智能体(Agent)工作负载的特殊形状说起。

智能体推理:长上下文、短追加、多轮#

传统的人机对话是”你来一句、我回一句”,几轮就结束。智能体应用完全不一样:一个 coding agent 会调用 bash 命令、跑测试、读报错信息,和外部环境反复交互几十上百轮,直到修完 bug 才结束。每一轮中:

  • 模型收到的 prompt 是”历史完整上下文 + 新追加的一小段”(通常是一段工具输出);
  • 模型生成一小段输出(通常是一条新的工具调用指令)。

DeepSeek 从生产环境的 coding 智能体任务中采集的 trace 显示:平均交互轮数 157 轮,平均上下文长度 32.7k tokens,而每轮平均只追加 429 个 token(论文 Figure 2 给出了一个轨迹示例)。

智能体轨迹示例:每一轮 prompt 由历史上下文加一小段追加组成
智能体轨迹示例:每一轮 prompt 由历史上下文加一小段追加组成

图里横轴是轮次,每轮只追加几百个 token,但上下文像滚雪球一样越滚越长。这意味着什么?每一轮推理时,历史 token 的 KV-Cache 早就算过了、存在磁盘上,而这轮真正需要从头计算的只有新追加的几百个 token。KV-Cache 命中率因此高达 95% 以上——论文实测是 98.7%。也就是说,每一轮 99% 左右的”活”其实不是计算,而是把几十 KB 到几百 GB 的 KV-Cache 从存储里搬回 GPU

缓存计算比:衡量”搬运”和”计算”谁更重的标尺#

论文定义了一个很直观的指标——缓存计算比(cache-compute ratio):KV-Cache 需要加载的数据量(GB)与这轮实际需要的计算量(PFLOP)之比。比值越大,系统越偏 I/O 密集。论文对多种模型算了这个数(表 1,上下文 16K-64K,KV-Cache 为 FP8):

模型缓存计算比(GB/PFLOP)
Qwen2.5-32B(FP16)117-267
GPT-OSS-120B47-95
Qwen3-235B-A22B39-60
DeepSeek-V3.2 660B13-36
DeepSeek-V3 660B4.8-5.8

注意几个细节。第一,Dense 模型(Qwen2.5-32B)的比值比 MoE 模型高一个数量级:dense 模型每个 token 都要算完整 FFN,但它的 KV-Cache 也不小,加上 32B 模型 KV-Cache 用 FP16 存储(没量化),所以搬运占比畸高。第二,DeepSeek-V3.2 的比值(约 22 GB/PFLOP,对应 32.7k 平均上下文)比 V3 更高——因为 V3.2 引入了稀疏注意力(DeepSeek Sparse Attention),把每轮的计算量大幅削减,但 KV-Cache 该搬还得搬,于是 I/O 相对更突出。这揭示了一个反直觉的事实:模型越”省算力”,系统越容易被 I/O 卡死。

硬件趋势:算力涨了 14.4 倍,网络和显存没跟上#

再看硬件侧(论文 Figure 3 左图):

NVIDIA GPU 硬件趋势与不同 batch 下的 token 吞吐
NVIDIA GPU 硬件趋势与不同 batch 下的 token 吞吐

从左图可以看到,从 Ampere 到 Blackwell,GPU 的 FLOPS 涨了约 15 倍,但网络带宽和 HBM 容量只涨了约 2-3 倍,I/O 与计算之比下降了 14.4 倍。也就是说,同等算力下,现代 GPU 能”喂”进来的数据相对越来越少了。而 KV-Cache 恰恰是数据量最大的数据。右图更直接:每个请求 30K 上下文、追加 300 token 时,token 吞吐随 batch 增大先升后塌——batch 一大,KV-Cache 加载的带宽立刻饱和,GPU 开始空转等数据。

PD 分离架构:谁的网卡在扛活,谁的网卡在睡觉?#

要理解 DualPath 为什么这样设计,还得铺垫现代推理集群的骨架——预填充-解码分离(PD Disaggregation)。这是从 DistServe(OSDI’24)开始成为事实标准的设计:把 prefill(计算密集、适合大 batch)和 decode(访存密集、对延迟敏感)拆到两类专门的机器上——预填充引擎(Prefill Engine, PE)解码引擎(Decode Engine, DE)。多轮对话的 KV-Cache 则落盘到分布式存储(比如 DeepSeek 自研的 3FS),下一轮再读回来。

一台标准的 AI 服务器上有两类网卡(论文按 NVIDIA DGX SuperPOD 的典型配置描述):

  • 计算网卡(CNIC,也叫东西向网卡):每个 GPU 配一块 400Gbps RDMA 网卡,负责 GPU 之间的集体通信(AllToAll、AllReduce 等),走 InfiniBand 计算网络;
  • 存储网卡(SNIC,也叫南北向网卡):每节点一块 400Gbps 网卡,负责访问磁盘上的数据——数据集、模型权重、KV-Cache。

现在关键问题来了:在传统 PD 分离架构里,所有 KV-Cache 加载压力都落在 PE 的 SNIC 上(KV-Cache 必须读进 PE 才能做 prefill),PE 侧存储带宽被持续打满;而 DE 的 SNIC 几乎闲置——DE 不读历史 KV-Cache,它只接收 PE 传来的 KV-Cache,走的是计算网络。结果就是:集群一半的存储网卡在排队,另一半在睡觉

简单地给 PE 多配网卡不行:成本高、通用集群里不现实。而前面说过,计算网络带宽远大于存储网络,且集体通信是亚毫秒级的突发模式、中间有大把空闲窗口。论文的核心观察就是:能不能把 DE 侧闲置的存储带宽也用起来,把 KV-Cache 先读进 DE,再通过空闲的计算网络 RDMA 传给 PE?

核心思想:KV-Cache 加载不必以预填充为中心#

DualPath 的一句话概括:给 KV-Cache 加载开两条路,把集群所有引擎的存储网卡带宽聚合成一个全局可调度的资源池

论文 Figure 1 直观对比了”现在”和”DualPath”:

现有瓶颈(左)与 DualPath(右)的对比
现有瓶颈(左)与 DualPath(右)的对比

左图是现状:所有 KV-Cache 都从存储(Storage)流向 PE,PE 的存储网卡被压满(红色饱和);DE 不碰存储。右图是 DualPath:每个请求的 KV-Cache 可以走两条路之一——

  • 路径 A(PE 读路径):存储 → PE,传统路径,和现在一样;
  • 路径 B(DE 读路径):存储 → DE →(计算网络 RDMA)→ PE。KV-Cache 先读进解码引擎的 DRAM 缓冲池,再通过计算网络转给预填充引擎。

两条路径由中央调度器按实时负载动态分配。这样一来,存储带宽从”PE 独扛”变成”所有引擎共享”,DE 闲置的 SNIC 也被拉进来干活。

和以往工作的本质区别:之前所有 KV-Cache 加载优化都假设”加载路径只有一条”,只是在这条路径上做文章——Mooncake(FAST’25)把 KV-Cache 放进分布式 DRAM 池来缩短路径;KVPR、TailorKV 压缩或重算部分 KV-Cache 来减小数据量。它们都没有解决存储 I/O 在引擎之间失衡这个结构性问题。DualPath 则是改路径拓扑本身。

原理详解一:双路径的数据流,从存储到 HBM 的每一步#

铺垫:Layerwise Prefill(按层预填充)#

在讲数据流之前还要补一个背景。长上下文 prefill 会被 HBM 容量卡住:一个 batch 的激活值加完整 KV-Cache 必须同时驻留显存,batch 一大就装不下。Layerwise Prefill(LayerKV、PrefillOnly)利用 prefill 的强局部性解决了这个问题:prefill 是逐层算的,每层只需要该层自己的 KV-Cache,所以可以每层计算前加载该层 KV-Cache、算完即释放,显存里同时只保留一层的 KV-Cache,等效 batch 扩大约模型层数倍。DualPath 构建在 PD 分离和 Layerwise Prefill 之上。

这带来一个工程后果:KV-Cache 被切成按层的细粒度小块(每层一块),加载的数据流是”逐层流式”的。所以论文设计了两类块格式:

  • Full Block:形状 [layer,tokens,bytes][layer, tokens, bytes],包含所有层的 KV-Cache——与存储交互时用;
  • Layer Block:形状 [1,tokens,bytes][1, tokens, bytes],只含单层 KV-Cache——PE/DE 之间传输、加载进 HBM 时用。

好处是零格式转换:把 nn 个 Layer Block 首尾拼接就是 Full Block,全程无需手动搬移内存布局。

路径 A:PE 读路径#

论文 Figure 4(a) 画出了 PE 读路径的完整数据流,共 9 个带编号的步骤:

PE 读路径数据流(Figure 4a)
PE 读路径数据流(Figure 4a)

按编号走一遍:

  1. 存储 → PE buffer:本轮命中(hit)token 的 KV-Cache 从分布式存储读出,先落进 PE 节点的一小块 DRAM 缓冲(PE buffer)。
  2. 同 1,持续流入。 3-4. PE buffer → PE HBM:prefill 计算到某一层时,把该层需要的 KV-Cache 从 PE buffer 拷进显存。注意 3、4 两步分别是”读 buffer”和”写 HBM”的镜像操作。 5-7. PE HBM → DE buffer:算完该层后,把这一层”历史命中 + 新追加”的完整 KV-Cache 通过计算网络 RDMA 传到对端 DE 的缓冲池(DE buffer)里攒着。第 3-7 步逐层重复,共 nlayern_{layer} 次——prefill 算完,DE buffer 里也就攒齐了完整 prompt 的 KV-Cache。 8-9. DE buffer → DE HBM:进入 decode 阶段前,DE 在显存里分配空间,把缓冲池里的完整 KV-Cache H2D 拷进显存,然后释放 CPU 内存,开始逐 token 解码。

关键点:整个 prefill 过程中,传输与计算是重叠的——算第 ll 层时,第 l+1l+1 层的 KV-Cache 已经在路上。这也是 Layerwise Prefill 能”隐藏”加载开销的原因。

路径 B:DE 读路径#

论文 Figure 4(b) 画了 DE 读路径,7 个编号步骤:

DE 读路径数据流(Figure 4b)
DE 读路径数据流(Figure 4b)

1-2. 存储 → DE buffer:命中 KV-Cache 从存储读出,直接进 DE 节点的 DRAM 缓冲池——这一步使用了 DE 的 SNIC,这就是整篇论文的差异化所在。 3-5. DE buffer → PE HBM:PE 做 prefill 时,按层从 DE buffer 读对应层的 KV-Cache(3、4 两步是镜像的搬运),同样与计算重叠,逐层重复。注意这里 KV-Cache 走的是计算网络——DE 的 CNIC 和 PE 的 CNIC 之间 RDMA。 6-7. DE buffer → DE HBM:decode 前 H2D 进显存,与路径 A 的 8-9 一样。

两路对比:路径 A 的 KV-Cache 流量是”存储→PE(存储网络)“再加”PE→DE(计算网络)“;路径 B 是”存储→DE(存储网络)“加”DE→PE(计算网络)“。两种路径在计算网络上产生的流量其实相同(最终 KV-Cache 都要到 DE),差别只在”哪一侧的 SNIC 承担存储读”——而这正是调度器可以操纵的自由度。

为什么加一层 DE buffer,而不是 GPUDirect RDMA 直通显存?#

细心的读者会发现:路径 B 中 KV-Cache 从 DE buffer 到 DE HBM 多了一次 H2D 拷贝(第 6-7 步),如果直接用 GPU Direct RDMA 让存储数据直达 DE 显存,能省掉这趟。论文明确讨论了这个问题,答案是为了省显存:智能体场景每轮生成很短,TTFT(首 token 延迟)在端到端请求时间里占比不小;如果 KV-Cache 直接驻留 DE 显存,大 batch 下显存很快被占满,反而压低吞吐。放在 DRAM 缓冲池里,等 decode 真正开始前再搬进显存,显存占用被压到最低。这是”用一次额外拷贝换显存容量”的权衡,论文认为在短生成的智能体场景下划算。

decode 阶段还有最后一环:每当攒满一个块(64 token),DE 立即把新产生的 KV-Cache 落盘到 3FS,供下一轮复用。这样多轮之间 KV-Cache 的持久化完全流水化,不用等整个轨迹结束。

原理详解二:无瓶颈分析——两条路径什么时候不会互相打架#

引入路径 B 之后,一个必须回答的问题是:存储、计算网络、DRAM 会不会出现新的瓶颈? 论文给出了一套完整的容量分析,值得逐条推导,因为里面的流量守恒思想很干净。

先定义符号:

  • PPDD:prefill 节点数、decode 节点数;
  • gg:每节点 GPU 数,每块 GPU 配一块带宽为 BB 的 CNIC;
  • ss:每节点存储带宽是计算带宽的 ss 倍(即每节点存储带宽 s×Bs \times B,由该节点所有引擎共享);
  • MM:每节点 DRAM 带宽。

假设:PCIe 拓扑合理(GPU-NIC 在同一 PCIe 交换域)、任务调度负载均衡、计算网络无拥塞、存储读带宽被用满。

负载均衡下,存储带宽在所有引擎间均分。PE 读路径的存储读全部由 PE 承担:总量为 PBsP \cdot BsPP 个 PE 节点各贡献 BsBs),均分给 DgDg 个 DE GPU,而每个 DE 又从 PgPg 个 PE GPU 各取一份,于是每对(PE GPU, DE GPU)的流量 Tp=BsDg2T_p = \dfrac{Bs}{Dg^2};DE 读路径对称地有 Tc=BsPg2T_c = \dfrac{Bs}{Pg^2}。某条链路上的总流量 = 所有用到该链路的 PE-DE 对之和。

PE 的 CNIC(PCIe 侧)——读方向:PE 读路径的步骤 3 和 5 都会产生 CNIC 流量,每对流量 TpT_p,共 DgDg 对:

2×Tp×Dg=2BsgB2 \times T_p \times Dg = \frac{2Bs}{g} \le B

因为 sgs \le g 在实际中恒成立(一台 8-GPU 机器不可能配 8 张存储网卡),所以 PE 读方向永远无瓶颈

PE 的 CNIC——写方向:PE 读路径的步骤 4 加 DE 读路径的步骤 5,每对流量 Tp+TcT_p + T_c

(Tp+Tc)×Dg=Bsg×(1+DP)B(T_p + T_c) \times Dg = \frac{Bs}{g} \times \left(1 + \frac{D}{P}\right) \le B

整理得:

PDsgs\frac{P}{D} \ge \frac{s}{g - s}

PE 写方向无瓶颈要求 P/D 不能太小——PE 太少时,每个 PE 要服务的 DE 太多,写流量爆掉。

DE 的 CNIC——读方向:PE 读路径步骤 8、DE 读路径步骤 3/6,每对流量 Tp+2TcT_p + 2T_c

(Tp+2Tc)×Pg=sg×(PD+2)×BB(T_p + 2T_c) \times Pg = \frac{s}{g} \times \left(\frac{P}{D} + 2\right) \times B \le B

整理得:

PDg2ss\frac{P}{D} \le \frac{g - 2s}{s}

DE 的 CNIC——写方向:PE 读路径步骤 7/9、DE 读路径步骤 7:

(2Tp+Tc)×PgB(2T_p + T_c) \times Pg \le B

即:

PDgs2s\frac{P}{D} \le \frac{g - s}{2s}

DRAM 压力:DRAM 是半双工,读写压力相加。PE 侧压力为 2sB2sB,一般远小于内存带宽;DE 侧压力为 (3+2P/D)Bs(3 + 2P/D)Bs,要求:

PDM/Bs32\frac{P}{D} \le \frac{M/Bs - 3}{2}

汇总——把所有约束合起来,DualPath 无瓶颈的 P/D 区间是:

sgsPDmin{g2ss, gs2s, M/Bs32}\frac{s}{g - s} \le \frac{P}{D} \le \min\left\{\frac{g-2s}{s},\ \frac{g-s}{2s},\ \frac{M/Bs-3}{2}\right\}

代入典型值(g=8, s=1, M500 GB/s, Bs50 GB/sg=8,\ s=1,\ M \approx 500\ \text{GB/s},\ Bs \approx 50\ \text{GB/s}):

17PD72\frac{1}{7} \le \frac{P}{D} \le \frac{7}{2}

这个区间覆盖了绝大多数实际部署的 P/D 配置(常见是 1:2 到 1:4 的 D 多 P 少,即 P/D 在 0.25-1 之间;实验里用了 1P1D、1P2D、2P1D、2P4D 等)。结论:在合理 P/D 下,双路径不会制造新的计算网络或内存瓶颈,存储带宽可以全量打满。 当然,分析依赖”负载均衡”这个前提——一旦某条路径上的任务扎堆,前面的推导就失效了,这正引出后面调度器的必要性。

工程挑战一:CNIC 为中心的流量管理——怎么让 KV-Cache 流量不碰伤推理通信#

双路径把 KV-Cache 流量引入了计算网络和 PCIe 链路,而计算网络上跑着延迟敏感的集体通信(MoE 的 AllToAll、张量/上下文并行的 ReduceScatter/AllGather)。一旦 KV-Cache 流量和它们抢带宽,推理延迟就会恶化。论文分析了为什么现有技术解决不了:

  • GPUDirect Storage(GDS):让存储数据绕过 CPU 直达 GPU,但它的数据路径和计算网络没有共享 QoS 控制,抢带宽时无约束;
  • CUDA copy engine:同样没有隔离机制;
  • 更深层的原因:现有 GPU 不支持 PCIe QoS,无法在硬件层面给不同 PCIe 流量分优先级;
  • 而且集体通信是亚毫秒级的突发,软件层面的流量整形器根本来不及在两次突发之间插入低优先级 I/O

DualPath 的方案可以概括为:让所有进出 GPU 的流量都走 CNIC。KV-Cache 加载时,先从存储读进 host DRAM,然后提交一个 RDMA Write 请求到 GPU 配对的 CNIC,由 CNIC 完成本地 H2D 拷贝;写回存储同理:先经 CNIC D2H 到 host DRAM,再经存储网络落盘。这样 CNIC 成了 GPU 所有 PCIe 流量的唯一出口,其网卡仲裁器(arbiter)就能统一做优先级调度。

隔离机制在 InfiniBand 上用的是虚拟通道(Virtual Lane, VL):把模型推理通信放进一个高优先级 VL,其余所有流量(包括 KV-Cache)放进低优先级 VL;在所有交换机和网卡上配置加权轮询(WRR),给高优先级 VL 保留约 99% 的带宽,剩下 1% 给低优先级 VL 防止饿死。论文给出了生产环境的具体配置:

qos_max_vls 4
qos_high_limit 240
qos_vlarb_high 0:192,1:192,2:0,3:192
qos_vlarb_low 0:192,1:192,2:64,3:192

效果是:KV-Cache 传输只能利用计算网络的空闲带宽,推理通信几乎不受影响;反过来 KV-Cache 也不会饿死。这套原则可以平移到 RoCE(用 DSCP 标记 + 硬件 Traffic Class + PFC)、UnifiedBus、Ultra Ethernet 等新互联——它们都在向”异构流量 QoS 隔离”收敛。

微基准数据是这套设计的一个亮点:cudaMemcpyAsync 提交一次拷贝的延迟约 5-7 微秒(CUDA 驱动闭源,无法进一步拆解),而提交一次 RDMA Write 工作请求只涉及几次用户态的 mmio 写网卡寄存器,约 1 微秒,且能用 doorbell batching 进一步摊薄。对 Layerwise Prefill 产生的大量细粒度小块(Layer Block)传输来说,单次提交开销直接决定吞吐——这是”绕远路”(相比 GDS 直达显存)反而更快的核心原因。

工程挑战二:自适应请求调度——每个请求该走哪条路?#

两条路径都有了,流量也能隔离了,剩下的问题是:谁来决定每个请求走哪条路? 一个朴素策略(比如轮询)会把某条路径打满,重新制造瓶颈。而且需要同时平衡两个维度:存储网卡的流量和 GPU 的计算负载。论文把调度拆成两级。

引擎间调度(Inter-Engine Scheduling)#

先解决规模问题:如果调度器要管理每块 GPU,几千张卡的集群会压垮它。DualPath 把引擎分成组,每组只有 rank 0 的 Leader Engine 与中央调度器交互,组内成员统一行动(同节点引擎必在同组,组内全 PE 或全 DE)。每次组主动来取任务时,每个引擎上报三个指标:

  • seqeseq_e:分配给它的未完成请求数;
  • toketok_e:这些请求的 token 总数(token 数与 GPU 负载、磁盘读负载、网络负载都强相关,所以用它当代理指标);
  • read_qn(e)read\_q_{n(e)}:所在节点的磁盘读队列长度。

PE 调度的规则(论文 Algorithm 1)如下。定义两个常数:短读队列阈值 α\alpha、未完成 token 上限 β\beta(论文实现里 α\alpha 取”3 秒能读完的 token 数”,β\beta 取”一块 GPU 5 秒能处理的 token 数”,均预先 profiling 标定)。所有 PE 分成三类:

  • C1C_1(过载):toke>βtok_e > \beta——不再分配新请求;
  • C2C_2(优先):read_qαread\_q \le \alphatokeβtok_e \le \beta——磁盘队列短,后续请求若不来,存储网卡容易空闲,所以优先喂饱;
  • C3C_3(次选):read_q>αread\_q > \alphatokeβtok_e \le \beta

分配时从 C2C_2 里选 toketok_e 最小的 PE,C2C_2 空则从 C3C_3 选;两类都空就结束本次取任务。伪代码:

算法 1:Inter-PE Scheduling
输入:等待队列 Q、PE 组 G_PE、各引擎上报的 (read_q, tok_e)、常数 α、β
1: 每个引擎 e 上报 (read_q_n(e), tok_e)
2: C1 ← {e ∈ G_PE : tok_e > β} # 过载
3: C2 ← {e ∈ G_PE : read_q_n(e) ≤ α ∧ tok_e ≤ β} # 磁盘队列短
4: C3 ← {e ∈ G_PE : read_q_n(e) > α ∧ tok_e ≤ β} # 磁盘队列长
5: while Q 非空 do
6: r ← Q 队首
7: if C2 ≠ ∅ then
8: pe* ← argmin_{e∈C2} tok_e
9: else if C3 ≠ ∅ then
10: pe* ← argmin_{e∈C3} tok_e
11: else
12: 结束本次取任务,返回已分配的请求
13: end if
14: 将 r 分配给 pe*;更新 tok_pe* += tokens(r);r 出队
15: end while

引擎间 PE 调度示意:组内 8 块 GPU 由调度器选择最优者
引擎间 PE 调度示意:组内 8 块 GPU 由调度器选择最优者

DE 调度是两级且不保全局 FIFO:请求先进全局队列;某个 DE 组取任务时,先把请求分给”组内总 toketok_e 最小”的组(跨组平衡 token 数,也就平衡了网卡和 GPU 负载);组内再按 HBM 余量筛选——先估算最多能放多少请求(假设无碎片的上界,集合 RR),算一个高 token 阈值:

Z=1.05×rRlenr+eEtokeEZ = 1.05 \times \frac{\sum_{r \in R} len_r + \sum_{e \in E} tok_e}{|E|}

然后逐个出队:对显存足够的 DE,若 toke+len(r)>Ztok_e + len(r) > Z 视为”高负载 DE”,否则视为”普通 DE”;优先把请求给普通 DE(内部选 seqeseq_e 最小的,平衡请求数),普通 DE 全满才给高负载 DE(选 toketok_e 最小的,避免显存耗尽引发抢占)。

最后一步——选读取侧:PE、DE 都定好后,请求的 KV-Cache 在磁盘读队列更短的那一侧读。这一条简单规则就把存储流量动态分布到了两条路径上。论文承认”把请求拆成两半、两侧同时读”可能更好,留作未来工作。

引擎内调度(Intra-Engine Scheduling):计算配额#

只有 PE 需要引擎内调度——DE 总是把组内所有请求塞进前向 batch。为什么 PE 需要?因为注意力层广泛使用数据并行(尤其 MLA 模型):每个 GPU 服务不同的请求子集,而 attention 阶段之后所有 GPU 必须同步进入 FFN 阶段。如果各 GPU 的 attention 耗时参差不齐,先算完的 GPU 只能空转等伙伴——形成气泡。

DualPath 的做法是给每个前向 batch 设一个计算配额(compute quota):请求按 FIFO 打包,每个请求用二元组 (cached,bsz)(cached, bsz) 描述——cachedcached 是已有 KV-Cache 的 token 数(存储命中或之前前向算过),bszbsz 是本 batch 需要计算 KV-Cache 的 token 数。由这些元组估算 attention 层的理论计算量,再通过预先 profiling 拟合的”理论计算量→墙钟时间”映射预测执行时间。只要预测时间不超过配额(实现里 300ms),就继续往 batch 里加请求;超了就对 bszbsz 做二分搜索,找能塞进剩余配额的最大 bszbsz',对该请求做分块 prefill(chunked prefill)。

引擎内调度:左为基于计算配额的批选择,右为应用配额前后的 GPU 时间线
引擎内调度:左为基于计算配额的批选择,右为应用配额前后的 GPU 时间线

右图展示了收益:不加配额时,各 GPU 的 attention 段长短不一,同步点之后大片气泡;加配额后各 GPU 的 attention 段被截齐,气泡消失。论文数据显示 attention 执行时间在所有 GPU 间的 Max/Avg 比低至 1.06(任务前 5% 时段),基本齐平。

实验评估:生产集群上的真实数字#

实现与平台#

DualPath 基于 DeepSeek 自研推理框架实现,改动约 5000 行;kernel 层组合了 FlashMLA(MLA 解码)、DeepGEMM(MoE GEMM)、DeepEP(专家并行通信)——与主流开源框架同栈。存储用 3FS(io_uring 风格接口做内核旁路)。测试集群每节点 8 块 Hopper GPU、8 块 400Gbps RDMA 网卡 + 1 块存储网卡,计算与存储网络物理隔离,3FS 无内部 DRAM 缓存、可打满 400Gbps 存储带宽。评估了三个模型:DeepSeek-V3.2 660B(MoE + 稀疏注意力)、DS 27B(660B 的缩配内部版)、Qwen2.5-32B(dense + GQA)。数据集是三个生产 agent RL trace(32K/48K/64K 上下文上限,各 500 条轨迹)。

基线有三个:SGL(MC)(SGLang + HiCache + Mooncake Store + 3FS,Mooncake Transfer Engine 做 PD 传输)、Basic(未修改的自研框架)、Oracle(DualPath 基础上绕过所有磁盘读、D2H/H2D、PD 间传输——零 I/O 开销的理论上界)。

离线推理(RL rollout 场景):最高 1.87 倍#

离线场景模拟 RL 训练的 rollout 阶段:nn 个智能体同时开始,度量全体完成的 JCT(任务完成时间)。

  • DS 660B:DualPath 比 Basic 最高快 1.87 倍,且与 Oracle 几乎持平——KV-Cache I/O 开销基本被消除了;
  • DS 27B:最高 1.78 倍,但比 Oracle 仍慢 1.09-1.85 倍——1P1D 配置下存储带宽总量有限,巧妇难为无米之炊;
  • 加大 batch 和上下文上限,收益更显著(带宽瓶颈更突出时双路径的价值更大)。

两个敏感性实验很有说服力:

  • 缩放每轮追加长度:追加越多,GPU 计算压力越大,Basic 与 DualPath 的差距逐渐缩小(瓶颈从 I/O 漂向计算);在不同追加规模下 DualPath 仍有 1.82-1.99 倍 加速。生成长度同理——生成越长,prefill 间隙越大、KV-Cache 加载压力相对越小。
  • 变 P/D 比(DS 27B 上 1P1D/2P1D/1P2D):DualPath 平均 1.64 倍、最高 2.46 倍。最有意思的发现是”等效配置”现象:Basic 1P1D 和 Basic 1P2D 表现相当,DualPath 1P1D 和 Basic 2P1D 表现相当,DualPath 2P1D 和 DualPath 1P2D 表现相当——每组对比的共性是”可用存储带宽相等”(Basic 只能用 prefill 侧带宽,DualPath 能用全部节点带宽)。这直接证实了:在智能体场景,存储带宽就是主导瓶颈,加多少个 PE 都不如把 DE 的带宽利用起来。

在线服务:SLO 不变,容量平均提升 1.96 倍#

在线场景按泊松过程以每秒到达率(APS)注入智能体,要求 TTFT ≤ 4 秒、TPOT ≤ 50ms。结果:DualPath 能支撑的 APS 容量比 Basic 高 1.67 倍(DS 27B)和 2.25 倍(DS 660B)——论文摘要口径是”在线服务吞吐平均提升 1.96 倍且不违反 SLO”。同时 TTST(第二 token 延迟)与 Basic 相当,TPOT 无额外解码开销——说明双路径的流量隔离确实没有伤到推理通信。TTFT 分解图显示:Basic 在高 APS 下排队时间(Sch. 部分)急剧膨胀,而 DualPath 的各组成保持平稳。

消融:三件套各自贡献多少?#

把 Basic → DualPath 的改动拆成三件:Layerwise Prefill、双路径加载、调度算法,逐步叠加(DS 660B、64K、1024/2048 agents):

配置JCT 相对 Basic 减少
Basic0%
+ Layerwise Prefill17.21%
+ 双路径加载38.19%
+ 调度算法(完整 DualPath)45.62%

双路径加载贡献了最大增量,调度算法再补一刀。存储网卡负载均衡度(Max/Avg 流量比,1.0 为完美)从轮询调度的 1.53 降到 1.18——调度器确实把流量摊匀到了所有 SNIC 上。

大规模扩展:1152 块 GPU 上的近线性#

最后是生产级验证(表 3):离线从 2P4D/2K agents 扩到 48P96D/48K agents(1152 块 GPU),JCT 几乎不变(3167s vs 3201s)——近线性扩展。在线从 2P4D/0.4 APS 扩到 44P88D/8.8 APS:吞吐 22 倍,TTFT(1.739s→1.847s)与 TPOT(0.039s→0.036s)几乎不变。调度器全程 CPU 占用低于 10 核,不是瓶颈。

还有一个容易被忽略的对比:DualPath 给 DeepSeek 模型每节点只分配 80GB DRAM 做缓冲,而 SGL(MC) 每节点要用 1.5TB DRAM 当 KV-Cache 缓存。在 RL rollout 阶段,DRAM 要腾给 offload 的优化器状态和奖励模型——DualPath 把 KV-Cache 直接放 SSD 后端、只留小块缓冲的设计,在显存/内存受限场景是决定性优势(这也是 Mooncake 式 DRAM 池方案在这类场景不可用的原因)。

局限与未解决的问题#

  1. 整请求单侧读取。每个请求的 KV-Cache 只从 PE 或 DE 一侧读,不拆开并行双读。论文明确说”拆两半从两侧读”可能更好,留作未来工作——调度器的读队列判断在请求粒度上是粗的。
  2. 小模型的 P-D 传输开销。DS 27B 的 TPOT 明显高于 Oracle,说明小模型上”PE→DE 传 KV-Cache”的基础开销占比不小(大模型可摊销掉)。这对智能体 + 小模型组合的部署是一个真实短板。
  3. 大规模部署不自动产生额外收益。48P96D 相对”多个等成本小集群”没有更多 JCT/容量增益——因为没做精细的并行与 P/D 调优(需要大量实验预算)。论文给的理由是:大集群减少碎片化、给突发在线请求提供更多调度机会、便于后续调参。这是个诚实的边界声明。
  4. 工作集与在线容量存在 r² 扩张。论文的工作集分析:给定到达率 λ\lambda 和平均 JCT Tˉ\bar{T},KV-Cache 工作集约 λTˉ×total_lenavg/2\lambda \bar{T} \times total\_len_{avg}/2。DS 660B 在线场景下,APS 0.1→工作集 69GB,APS 0.45→681GB;而生产环境若因工具调用延迟等使 JCT 放大 rr 倍,容量只增 rr 倍、工作集却扩 r2r^2 倍,存储成本按 r3r^3 增长——在线智能体场景的规模化成本结构仍然陡峭
  5. 动态负载下的 P/D 自适应。离线 rollout 负载高度动态(RL 实验前半段 prefill 压力远大于后半段),论文认为需要模拟器或在线调节机制来自适应调整 P/D 与并行度——目前是静态配置。
  6. TTFT 尾部延迟。论文期望在大规模部署下进一步压低 TTFT 百分位,调度算法仍有优化空间。

小结#

DualPath 讲了一个”资源池化”的故事:智能体推理把 KV-Cache 命中率推到 98%+,系统瓶颈从算力漂移到存储 I/O;而传统 PD 分离架构把全部存储带宽压力压在 PE 侧,DE 的网卡闲置。DualPath 通过双路径加载把存储带宽变成全局可调度资源,用 CNIC 统一流量出口 + InfiniBand 虚拟通道隔离保证不伤推理通信,用两级调度器在网卡流量和 GPU 计算两个维度上做负载均衡——最终离线吞吐最高 1.87 倍、在线容量平均 1.96 倍、1152 块 GPU 近线性扩展,全部在生产级 agent 负载上验证。

对做推理系统的人来说,这篇论文的启发在于:当计算不再是瓶颈时,数据的物理位置和搬运路径就是系统设计的主战场——而”闲置资源的再调度”往往比”堆更多资源”更便宜也更有效。对 DeepSeek 而言,它和 DSpark(投机解码)、DualPath(存储 I/O)一起构成了”V4 前夜”的推理系统布局:一个在时间维度省 token,一个在空间维度省带宽。

参考资料#

  1. DualPath: Breaking the Storage Bandwidth Bottleneck in Agentic LLM Inference(arXiv 摘要页)
  2. DualPath 论文 HTML 全文(arXiv:2602.21548v1)
  3. DualPath 论文(ACM SIGCOMM 2026 会议记录,DOI: 10.1145/3789240.3829159)
  4. ITHome 报道:DeepSeek 新论文剧透 V4 新框架,用闲置网卡加速智能体推理性能
  5. 量子位报道:DeepSeek 新论文剧透 V4 新框架!用闲置网卡加速智能体推理性能
  6. 网易科技报道:DeepSeek 新论文来了!联手清华、北大,优化智能体大模型推理
  7. 3FS:DeepSeek 开源的分布式文件系统(GitHub)
  8. FlashMLA:DeepSeek 高效 MLA 解码内核(GitHub)
  9. DeepGEMM:DeepSeek 的 MoE GEMM 库(GitHub)
  10. DeepEP:DeepSeek 的专家并行通信库(GitHub)
  11. Mooncake: A KVCache-centric Architecture for Serving LLM Chatbot(FAST’25)
  12. LayerKV: Optimizing Large Language Model Serving with Layer-wise KV Cache Management
  13. PrefillOnly: An Inference Engine for Prefill-only Workloads in LLM Applications(SOSP’25)
  14. DistServe: Disaggregating Prefill and Decoding for Goodput-optimized LLM Serving(OSDI’24)
  15. Strata: Hierarchical Context Caching for Long Context LLM Serving
  16. SGLang HiCache 官方文档
  17. DeepSeek-V3.2: Pushing the Frontier of Open Large Language Models

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

DualPath 完全拆解:双路径 KV-Cache 加载,把解码引擎的闲置网卡变成智能体推理加速器(SIGCOMM 2026)
https://pinghaoyang.com.cn/aigc/posts/dualpath/
作者
平昊阳
发布于
2026-08-22
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
平昊阳
乘长风,破巨浪, 展鸿图于未央!
--
总访问量
--
访客数
公告
欢迎来到我的个人博客!欢迎关注交流吖!
更多相关公告,见
社交-留言」。
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
站点统计
文章
66
分类
16
标签
93
总字数
477,284
运行时长
0
最后活动
0 天前

文章目录