笔记日期: 2026-08-30 论文标题: VPP: Virtual Pipeline Parallelism for Efficient Chunked Prefill in Long-Context LLM Inference 论文作者: Yan Shi, Xiaochao Wang, Jingchun Gao, Jintao Luo, Xinyi Zhou, Feng Liu, Kui Luo, Xushi Li, Xinjie Guo, Liangjun Feng(华为技术有限公司 + 上海交通大学) arXiv: 2608.26523 发表情况: arXiv 预印本,2026 年 8 月 27 日提交
1. 这篇论文到底在解决什么问题
如果你现在正在服务长上下文 LLM 请求——比如带着 10 万+ token 检索上下文的 agent 流水线,或者单个请求就携带 50 万 token 的场景——你大概已经体会过,预填充(prefill,读入整段 prompt 并生成第一个输出 token 的阶段)是整个推理服务链路里最让人头疼的部分。它是计算密集型的,可能让一块加速卡持续忙碌几十秒,而在这期间,排在它后面的所有其他请求都要遭受排头阻塞(head-of-line blocking)。
业界标准的应对方案是分块预填充(chunked prefill):把一个长 prompt 切成固定大小的若干块,让解码步骤和短请求可以穿插在这些块之间执行。如果再把分块预填充和流水线并行(按层把模型本身切分到多台设备上)结合起来,就得到了分块预填充流水线并行(Chunked Prefill Pipeline Parallelism,CPP)——这些 chunk 成为在流水线各阶段中流动的工作单元。
这篇论文关注的具体病理现象是:在因果注意力(causal attention)机制下,一个 prompt 中越靠后的 chunk,需要关注的前缀(已经缓存的 key/value)就越长,所以它的注意力计算成本会一路走高。如果每个 chunk 的 token 数相同,但处理耗时却不同,流水线各阶段就会失步——某些阶段空等其他阶段——从而产生流水线气泡(pipeline bubble)。论文用真实 trace 展示了 chunk 延迟随 chunk 序号几乎线性增长的现象,在一个 128K 长度的序列上,vanilla CPP 的气泡能吃掉高达 15% 以上的预填充总时间。
目前业界的现有解法是动态分块预填充流水线并行(Dynamic Chunked prefill Pipeline Parallelism,DCPP):不再使用固定大小的 chunk,而是在运行时动态调整 chunk 边界,让每个 chunk 花费大致相同的墙钟时间,从而重新平衡流水线。SGLang 和 vLLM-Ascend 都实现了这一思路的变体。VPP 的核心论点是:DCPP 是在错误的地方解决不平衡问题——它通过把 chunk 变得更小、更多来消除延迟异质性,但这种碎片化本身是有代价的(更多的 kernel 启动、更多的调度开销、更多的通信往返),而且这个代价会随序列长度增长,最终超过它带来的气泡削减收益。在一个 512K token 的 DeepSeek-V3.1 工作负载上,论文实测 DCPP 反而比朴素 CPP 慢了 4.65% 的端到端 TTFT,原因正是它需要 4.4 倍多的调度调用次数去追赶不断移动的平衡目标。
VPP 的方案是:保持 chunk 大小固定(完全不做动态调整),转而调整流水线布局本身——重新安排哪个虚拟流水线阶段跑在哪个物理 rank 上,以及执行顺序——使得因果注意力带来的、可预测的近似线性延迟增长,被并行执行直接吸收掉,而不是靠缩小 chunk 来消除它。
2. 前置知识:理解这篇论文之前你需要知道什么
2.1 预填充 vs. 解码,以及为什么预填充主导 TTFT
服务一个 LLM 请求包含两个计算特征迥异的阶段:
- 预填充(Prefill):一次性处理整个 prompt 的所有 token,填充 KV 缓存并生成第一个输出 token。由于大量 token 是并行处理的,这个阶段是计算密集型(compute-bound)——会把加速卡的算力打满。
- 解码(Decode):逐 token 自回归生成,每一步只处理一个新 token,但需要读取全部已累积的 KV 缓存。这个阶段是访存密集型(memory-bound)。
首 token 时延(TTFT)主要由预填充决定;token 间时延(ITL)主要由解码决定。这篇论文关注的完全是 TTFT/预填充这一侧。
2.2 因果自注意力的成本,精确复述一遍
对于长度为 、隐藏维度为 的序列,自注意力把输入投影为查询、键、值 ,计算:
这里的主导成本是 :相对序列长度是二次方的。在因果掩码下,第 个 token 只能关注 的 token,所以在从左到右处理一个 prompt 的过程中,越靠后的位置本来就要做更多的注意力计算——即便 FFN 和归一化层的每 token FLOPs 是恒定的。这个不对称性正是这篇论文要解决的一切问题的物理根源。
2.3 分块预填充
分块预填充(Agrawal 等人,Sarathi,2023)不再把整个 prompt 当成一次巨大的前向传播来跑,而是把 prompt 切成固定大小的 chunk,逐个处理(或者与其他请求的解码步骤交替执行)。这限定了单次调度步骤能够独占加速卡多长时间,从而缓解了并发解码工作的排头阻塞问题。
但关键在于——在因果注意力下,同一个 prompt 里越靠后的 chunk 需要关注越长的前缀(所有前面 chunk 的 KV 缓存),所以它的注意力 FLOPs、进而它的墙钟延迟,会随 chunk 序号一路攀升,即便每个 chunk 的 token 数是相同的。
2.4 流水线并行与流水线气泡
当单块加速卡无法容纳或高效执行整个模型时,流水线并行(PP)按层切分模型:每台设备(一个”流水线 rank”)拥有一段连续的层,称为一个流水线阶段(stage),只有阶段之间的边界激活值需要通信。相比张量并行(在每一层都需要集合通信来切分权重矩阵)或上下文并行(切分序列本身,需要交换 KV 状态),这在通信侧要便宜得多。
PP 的代价是流水线气泡——某个阶段处于空闲的时间段。两个来源:
- 填充/排空气泡(fill/drain bubbles):在流水线最开始(预热)和最末尾(冷却)的阶段,不是每个 stage 都有可做的工作——这是流水线结构本身固有的,朴素方案下无法避免。
- 不平衡气泡(imbalance bubbles):如果不同的 chunk(训练里对应不同的 micro-batch)耗时不同,跑得快的 stage 就要空等跑得慢的 stage。
把分块预填充和流水线并行结合起来就是 CPP:预填充 chunk 成为在流水线阶段中流动的工作单元。在等大小 chunk 下,不平衡气泡正是 §2.3 中”越靠后的 chunk 越慢”的问题,被平移到了多阶段流水线上。
2.5 DCPP——现有的”解法”
DCPP(SGLang 和 vLLM-Ascend 均已采用)正面攻击不平衡气泡:不再使用固定的 token 预算,而是估计后续 chunk 的执行成本(通过运行时 profiling / 在线校准),并收缩后面那些更贵的 chunk 的边界,让每个 chunk 的墙钟时间大致相等。这直接解决了不平衡气泡问题——但代价是更细的粒度:更多、更小的 chunk 意味着更多的 kernel 启动、更多的调度步骤、更多的通信事件。
3. 论文的动机性实测(第 4 节)
在提出 VPP 之前,作者先做了一组受控对比:DCPP vs. CPP,在 DeepSeek-V3.1 预填充上,序列长度从 64K 扫到 512K,使用 TP8+CPP2 和 TP8+DCPP2(8 路张量并行、2 路流水线并行),chunk 大小预算在 {8K, 16K, 24K, 32K} 中扫描,报告最优结果。
结果: 在 64K–256K 序列上 DCPP 领先 1.6%–3.6%,但在 512K 时反过来输了 4.4% 吞吐量和 4.6% TTFT。
图 1 把这个现象具象化。

面板 (b) 是关键的诊断图:CPP 用 8 个调度步骤完成一个 128K prompt,每 chunk 延迟大致线性增长,越到后面灰色的”气泡”区域越明显地堆积。DCPP 用 17 个调度步骤完成同一个 prompt——是 CPP 的两倍多——用碎片化换来了更平坦的逐 chunk 延迟曲线。
在 512K 这个点上,DCPP 的最优 chunk 预算是 24K,论文的 profiler trace 显示:
| CPP(秒) | DCPP(秒) | 差值 (秒) | /CPP | |
|---|---|---|---|---|
| 计算 | 252.59 | 279.07 | +26.48 | +10.48% |
| 气泡 | 38.64 | 20.47 | -18.17 | -47.03% |
| 暴露通信 | 16.67 | 22.68 | +6.01 | +36.05% |
| 总计 | 307.90 | 322.22 | +14.32 | +4.65% |
DCPP确实兑现了它的承诺——气泡时间降低了 47%,节省了 18.17 秒。但这份节省被计算时间增加 10.5%(+26.48s,更小的 chunk 降低了算子效率)和暴露通信增加 36.1%(+6.01s,更多的 chunk 边界意味着更多的收发事件)完全吃掉,甚至还倒亏。这篇论文的总结性洞察值得牢记:DCPP 用调度开销换取负载均衡收益,而这笔交易恰恰在序列变长时变得不划算——而这正是长上下文推理服务最关心的场景。
设计选择讨论。 为什么不干脆调细 DCPP 的 chunk 粒度来降低碎片化成本?论文没有明确排除这个方向,但张力是结构性的:DCPP 的整套机制本身就是细粒度动态调整——如果你把它变粗以降低碎片化成本,你就丢掉了原本用来做平衡的精度。在 DCPP 自己的设计空间里,没有一个自由参数能逃开这个权衡;“收缩 chunk 以拉平延迟”这件事从一开始就把这个矛盾内建进去了。
4. VPP:设计与实现(第 5 节)
4.1 核心洞察
论文观察到:在固定 chunk 大小、因果注意力下,逐 chunk 延迟的增长不是随机噪声——它是近似线性的,随 chunk 序号增长。设 chunk (第 个 chunk,从 0 开始计数)的每阶段延迟为 ,这个经验规律可以写作:
其中 是第一个 chunk 的每阶段延迟。这一规律在注意力主导每 chunk 成本、且 MoE 路由是专家并行(因此 MoE 开销是一个大致固定的加性项,而不是随 本身继续增长的量)时成立。
与 DCPP 试图消除这种异质性不同,VPP 的洞察是:利用它的可预测性来构造一个流水线调度方案,让后面 chunk 的额外工作量被流水线中别处的额外并行工作量刻意匹配上,使得没有任何地方处于空闲。
4.2 VPP:V 形流水线调度(§5.1)
考虑最简单的具体实例:两个物理流水线 rank, 和 。模型的层被切分成四个虚拟阶段 。VPP 不像传统方式那样把 stage 映射到 rank (单向流动),而是把两个边界阶段()放在 上,把两个中间阶段()放在 上。
一个 chunk 按如下”折返”模式在 rank 之间穿行:
启动这个 chunk,交给 连续执行两个中间阶段,再交回给 执行最后阶段。因为 chunk 的走向是 rankrankrank 再折回,这条轨迹形成一个视觉上的”V”字——这正是 Virtual Pipeline Parallelism(虚拟流水线并行)名字的由来(这个说法也呼应了训练场景里”虚拟流水线”交错调度的概念,但这里应用于折返布局而非交错的 micro-batch)。
为什么这样能平衡流水线? 我们一步步把推导展开。当 忙于运行 chunk 的两个中间阶段时,它的总占用时间大约是
在同一时间窗口里, 并没有闲着——它有两块现成可做的工作:完成前一个 chunk 的退出阶段 ,延迟是 ;以及启动下一个 chunk 的进入阶段 ,延迟是 。把这两者相加:
这正是整个方法的代数核心:当 线性假设(式 2)成立时, 在 忙于处理 中间阶段的同时能做的工作总量,恰好等于 的占用时间,对任意 都成立。换句话说,后面 chunk 增长的成本并没有被消除,而是被重新调度了,使得两个 rank 始终有事可做,而不是一个 rank 干等另一个。
边界情况是 :当 处理它的两个中间阶段()时, 只有 可做(延迟 ),因为不存在 ——由于线性公式的巧合,数字依然对得上,所以即使是第一个 chunk 也不会引入不平衡。
V 形调度逻辑的编号伪代码:
算法 1:V 形虚拟阶段调度(两 rank 情形)
输入:chunk 序列 C_0, ..., C_{N-1};虚拟阶段 s_0, s_1, s_2, s_3
映射到物理 rank pp_0(s_0, s_3)和 pp_1(s_1, s_2)
输出:每个 rank 的执行调度
1: for k = 0 到 N-1:
2: pp_0 对 chunk C_k 执行 s_0 # 进入阶段
3: pp_0 把激活值发送给 pp_1
4: pp_1 对 chunk C_k 执行 s_1
5: pp_1 对 chunk C_k 执行 s_2 # 两个中间阶段,连续执行
6: pp_1 把激活值发回 pp_0
7: pp_0 对 chunk C_k 执行 s_3 # 退出阶段
8: # 注:当 pp_1 在第 4-5 行处理 C_k 时,pp_0 会
9: # 并发地处理第 7 行的 C_{k-1} 和第 2 行的 C_{k+1},
10: # 填补本来会空闲的时间
11: end for
具体数值演算。 假设 (任意延迟单位),当前在 chunk 。此时 : 花 单位时间跑 和 。与此同时, 跑 (延迟 )和 (延迟 ),总计 单位——正好对上。这正是式(3b)的具体实例:。
这个方法会在哪里失效(作者明确说明的边界条件): 整套推导依赖线性 规律成立。如果注意力不再是主导成本(例如某种非常稀疏的注意力,其 FLOPs 随上下文长度增长不再是线性的;或者专家并行被关闭,使 MoE 路由/通信从固定成本变成了随之增长的成本),式(3b)里的等式就不再精确成立,稳态气泡可能重新出现。论文后面用 GLM-5.2 的 DSA(稀疏)注意力实测确认了这一点——细节见 §7.5。
图 2 把由此产生的调度时间线和朴素 CPP 做了可视化对比。

注意 CPP 那一行里气泡(虚线区域)如何随着序列推进逐渐累积、变大——这正是不平衡问题的直接可视化。在 VPP 那一行里,折返布局让两个 rank 几乎全程持续忙碌,只在末尾留下一段残余的”排空”气泡。
4.3 剩余的两个低效点,以及它们为何重要
即便 V 形布局解决了计算平衡问题,图 2 中 VPP 那一行仍能看到两处间隙(低效点):
- 图 2 中 rank 之间的每一个箭头都代表一次同步、阻塞的发送/接收。接收方必须等待完整的激活值传输完成才能开始计算——通信被直接放在了关键路径上。
- 一个请求的最后几个 chunk 不可避免地会留下一个 rank 空闲的窗口——即一个 rank 的工作先做完了,但另一个 rank 还在收尾——这是任何有限请求的流水线执行都固有的排空气泡。
VPP 用接下来的两项进一步优化来解决这两个问题。
4.4 VPP-Async:通信与计算重叠(§5.2)
以 和 之间的交接为例,朴素的 V 形方案让 把 向前发给 ,与此同时 把 发回给 。两个方向都是阻塞的:每个 rank 都要等待各自的入站传输完成才能继续,尽管这里并不存在真正强制这样做的数据依赖——这个阻塞纯粹是指令顺序造成的假象。
解法: 一旦所需的前缀 KV 缓存在本地已就绪,一个 rank 就可以重新排列自己的本地指令流,避免同时发起双向通信。具体来说,VPP-Async 交换了 发起 尾部阶段计算()和 头部阶段计算()的顺序,使得每一次通信事件发起时,对端 rank 正忙于有用的计算,而不是恰好也在尝试通信。这通过两个交替的 HCCL 通信组实现,用点对点收发交换激活值——专门用来避免双向传输如果放在单个通信组上可能引发的死锁。
设计选择讨论: 为什么用重排序而不是干脆在框架层面让通信本身变成异步/非阻塞的?论文的做法实现成本更低(不需要重新设计整套激活值传递 API),并且直接针对根因(顺序)下手,而不是用更多异步原语来掩盖一个调度问题(这可能引入新的竞态)。权衡在于:它要求调度器提前知道哪些 chunk 对会发生冲突,这把实现和这个特定的 V 形布局绑在了一起,而不是一个通用的异步通信库。
4.5 带流水线打包的 VPP-Async:跨请求气泡压缩(§5.3)
即便隐藏了请求内部的阻塞,任何单个请求的尾部仍会留下一个真实的排空气泡:最后几个 chunk()完成它们的 V 形穿行后,一个 rank 已经没有这个请求的活可干了,只能等待请求彻底完成。
但在真实的服务系统里,请求是持续到达的。VPP 的第三项优化是把下一个请求 的前导 chunk 打包进当前请求 的尾部排空气泡窗口。论文计算这个排空窗口的持续时间大约是 ——而由于任何请求最早的几个 chunk 延迟最低(由式 2, 较小时 ),它们恰好是填进不断收缩的尾部窗口的合适大小,不会进一步把窗口撑大。
编号伪代码:
算法 2:跨请求流水线打包
输入:即将完成的当前请求 R,其 chunk 为 C_0...C_{N-1};
已排队的下一个请求 R',其 chunk 为 C'_0, C'_1, ...
输出:把 R' 的前导 chunk 填进 R 的排空气泡的打包调度
1: 当 R 在其 V 形穿行中仍有 chunk 未完成时:
2: 按算法 1(第 1-11 行)正常调度 R 的下一个 chunk
3: 结束循环
4: # R 进入排空阶段:只剩下它最后阶段的完成动作
5: 排空窗口估计值 <- (N + 1) * t
6: j <- 0
7: 当排空窗口估计值未耗尽 且 R' 还有 chunk 时:
8: 把 C'_j 调度进 R 排空过程释放出的空闲 rank 槽位
9: j <- j + 1
10: 排空窗口估计值 <- 排空窗口估计值 - tau(C'_j)
11: 结束循环
12: 一旦 R 彻底完成,恢复正常的 V 形调度
继续处理 R'(及后续请求)的剩余部分
这是一个双队列调度器:等待其初始虚拟阶段穿行的 batch 放在第一个队列里;越过折返点之后的 batch 迁移到延续队列,执行最后阶段和采样。调度器额外维护一个正在处理的 batch,并用异步任务句柄来确保这套记账逻辑永远不会阻塞主调度循环。
4.6 设计选择汇总表
为了让上面积累的设计决策更容易一览,这里汇总 VPP 做出的每一个非平凡选择、其明显的替代方案、以及各自假设可能失效的边界:
| 设计选择 | 为什么有效 | 明显的替代方案 | 在哪里失效 |
|---|---|---|---|
| 固定 chunk 大小 + 折返布局 | 利用可预测的线性 增长(式 2),而非与之对抗 | 动态 chunk 调整大小(DCPP) | 若 非线性(如稀疏注意力,见 §7.5)则失效 |
| V 形(边界阶段/中间阶段)rank 分配 | 对任意 都能代数上平衡 和 的工作量(式 3b) | 传统的单向 stage-到-rank 映射 | 需要阶段数与 rank 数精确匹配;推广到阶段数/rank 数为奇数的情形更难 |
| 同步收发重排序(VPP-Async) | 无需新的异步原语即可消除暴露通信 | 框架层面的完全异步通信 API | 需要调度器提前知道 chunk 对的冲突模式;把实现和这个特定布局绑定 |
| 跨请求流水线打包 | 把排空气泡的空闲时间重新利用起来 | 干脆接受排空气泡是流水线固有开销 | 需要下一个请求已排队就绪;在低并发/突发到达场景下无收益 |
| 24K 作为经验上偏好的 chunk 大小 | 留下一个不均等的余数 chunk,缩短尾部气泡 | 均分的 chunk 大小(8K/16K/32K) | 在很短的序列下(DeepSeek/GLM 上的 64K)流水线永远达不到稳态,偏好反而反转 |
4.7 第二个数值演算:排空打包的算术细节
有必要用具体数字来落实算法 2 中抽象的 估计值。假设一个请求 有 个 chunk,(与前面 §4.2 的例子保持一致的任意延迟单位)。排空窗口的估计持续时间是 单位。现在假设下一个排队的请求 遵循同样的线性延迟规律,。从 开始贪心打包:
所以 的前三个 chunk 能装进这个 9 单位的排空窗口,还剩 3 单位的余量(不够再塞进第四个 chunk,因为 )。这直接说明了为什么论文特意打包前导 chunk——按照这同一条创造出排空气泡大小的线性规律,前导 chunk 恰恰是最便宜的。如果调度器反过来先打包 更靠后、更贵的 chunk(一个朴素的替代方案),要么会溢出排空窗口,在 自己的关键路径上重新引入气泡,要么会填不满窗口、浪费空闲时间——所以先进先出/最早 chunk 优先的打包顺序不是一个随意的实现细节,而是整个方法所依赖的同一条线性延迟结构的直接推论。
4.8 与相关系统的设计对比一览表
为了让读者更容易把 VPP 放进整个流水线/分块预填充生态里定位,这里给出一张更细的对比表,涵盖论文提到的所有相关方法在”平衡机制""是否需要在线校准""主要开销来源”三个维度上的差异:
| 系统 | 平衡机制 | 是否需要在线校准 | 主要开销来源 |
|---|---|---|---|
| CPP(朴素分块预填充+流水线并行) | 无——完全不做平衡 | 否 | 不平衡气泡随序列变长持续累积 |
| DCPP(SGLang / vLLM-Ascend) | 动态收缩/放大 chunk 边界 | 是(运行时 profiling+校准) | 碎片化:更多 kernel 启动、更多通信事件 |
| gLLM(token 节流) | 按 token 计数节流 micro-batch | 是(但只用简单代理指标) | 对上下文相关的注意力成本建模不准确 |
| TeraPipe / Seq1F1B(训练场景) | 动态规划/FLOPs 估计做非均匀切分 | 是(但可在训练步骤间摊销) | 与 DCPP 类似,但训练场景下校准成本更容易摊销 |
| VPP(本文) | 固定 chunk + V 形折返布局重塑 | 否(依赖离线观测到的线性 规律) | 暴露通信增加(但被通信-计算重叠大幅抵消) |
这张表最值得注意的一列是”是否需要在线校准”——VPP 是表里唯一一个完全不需要运行时校准的方案,这也是它在长序列上避免碎片化开销的根本原因:它把”平衡”这件事从一个运行时决策问题,变成了一个离线的、一次性的布局设计问题。
5. 实现细节(§5.4)
VPP 基于 vLLM-Ascend,用 PyTorch 和 HCCL 实现,目标平台是华为 Ascend 910C NPU。有几个实现细节值得单独指出,因为它们影响这个想法向其他技术栈迁移的难易程度:
- 控制面逻辑(虚拟阶段调度器、折返式层分配、逐 batch 流水线状态机)全部用纯 Python 实现;实际的注意力/MoE 计算 kernel 则原封不动地复用上游 vLLM-Ascend 的实现。这是一个有意义的设计选择——VPP 是一个调度层,不是新的 kernel,这也是它能做成一个单一开关(启用/禁用)而不触碰计算路径的原因。
- 折返拓扑可以推广到超过 2 个 rank / 4 个阶段的情形——“前一半虚拟阶段跨 rank 向前穿行,后一半反向穿行”,其中折返点(两个连续虚拟阶段落在同一物理设备上)不需要设备间通信——这是折返点本身带来的一个不错的免费效率收益。
- 实现支持不均匀的层划分——也就是说,不必让每个虚拟阶段拿到完全相同数量的 transformer 层;这是一个用于在上述纯延迟调度问题之外,平衡显存/计算的额外旋钮。
设计选择讨论——为什么专门选 vLLM-Ascend,这对可移植性意味着什么? 论文并未声称 VPP 的算法本身是 Ascend 专属的——§4.2 的推导与硬件架构无关——但实现依赖 Ascend 专属的原语(HCCL 通信组、CANN 工具链、torch_npu)。移植到 GPU 需要把 HCCL 换成 NCCL,并重新验证异步通信组的死锁规避逻辑,但核心调度算法里没有任何东西是 NPU 专属的。一个显而易见的替代方案——把这实现成一个独立于硬件后端的纯 vLLM 调度器插件——架构上会更干净,但论文并没有尝试这样做,大概是因为落地的时间压力是要在 Ascend 硬件上具体交付(华为是第一作者所在单位)。
6. 实验设置(§6.1)
- 硬件: 华为 Atlas 900 A3 SuperPoD 计算节点,16 块 Ascend 910C NPU(每块 64GB HBM),HCCS 互联,所有集合通信均通过 HCCL。
- 软件: PyTorch v2.10.0 + torch_npu,CANN v8.3,vLLM v0.23.0 配合 vLLM-Ascend v0.23.0。
- 模型: 三个覆盖不同注意力机制的 MoE 模型——Qwen3-Coder-30B-A3B-Instruct(总参数 30.5B / 激活 3.3B,128 个专家,GQA 注意力)、DeepSeek-V3.1-Terminus(总参数 671B / 激活 37B,256 个专家,MLA 注意力)、GLM-5.2(总参数 744B / 激活 40B,256 个专家,MLA + DSA 稀疏注意力)。
- 基线: TP8+CPP2(分块预填充流水线并行,静态 chunk)、TP8+DCPP2(动态 chunk 调整大小,采用 vLLM-Ascend 推荐配置:最小 chunk 4096 token,平滑因子 1.0)、TP8+VPP2(本文提出的方案)。均使用 8 路张量并行加 2 路流水线并行。
- 工作负载: (1) 短序列:100 个并发请求,输入 4K/8K/16K token;(2) 长序列:单请求依次处理,输入 64K 到 1M token;(3) 混合长度:从 GSM8K 合成的 500 个请求,长度从 7 到 122,710 token(均值约 22.5K,中位数约 10K,p90 约 64K),并发度 16。
- 指标: 吞吐量(token/s)和 TTFT(秒),每个(策略、工作负载、chunk 预算)组合都平均 4 次运行,chunk 大小在 {8K, 16K, 24K, 32K} 中扫描,每个策略报告其最优预算下的结果。
可复现性说明: 实现已开源在 github.com/RookieCoder-Camera/vllm-ascend/tree/vpp-dev,这对复现是个明显的加分项——但需要注意这是一个开发分支,不是一个打了 tag 的正式发布版本,论文除了上面列出的大版本号之外,并没有给出具体的 commit hash 或精确的软件版本;任何试图复现精确数字的人,都应该预期会有一些因分支漂移带来的偏差。
7. 实验结果
7.1 长序列——VPP 设计初衷最能发挥的场景

VPP 在几乎所有配置下都同时超过 CPP 和 DCPP。相对 CPP:吞吐量提升最高达 7.3%(Qwen)、10.0%(DeepSeek)、4.1%(GLM),TTFT 也有相应的降低。相对 DCPP:优势在 Qwen 和 DeepSeek 上大体随序列长度增大——Qwen 上(128K–512K)为 3.7%–8.7%,DeepSeek 上(64K–512K)为 2.6%–13.1%。这个”越长优势越大”的趋势正是论文核心实证主张的验证:序列越长,DCPP 的碎片化开销累积得越多,VPP 通过完全规避这种碎片化而能取得的胜幅也就越大。
GLM 是一个有意思的例外:VPP 相对 DCPP 的收益在 64K 处达到峰值(8.5%),然后随序列变长而收窄,而不是继续增大。论文将其归因于 GLM 的 DSA(稀疏)注意力打破了线性 假设——这正是 §4.2 中标注的边界条件,在这里以实验数据的形式浮现出来。我们会在后面回来讨论这一点。
7.2 混合长度序列——更贴近真实场景的测试

Qwen 上:相对 CPP +9.0%,相对 DCPP +0.8%。DeepSeek(VPP 表现最好的情形):相对 CPP +13.8%,相对 DCPP +6.7%。GLM 上:相对两个基线都是较温和的 +1.3%–1.6%。这大概是最贴近实际生产意义的一组结果,因为生产服务流量很少是均匀的长或均匀的短——它是一个长尾混合分布,而 VPP 在这个混合分布上保持了优势,而且不需要事先知道某个具体请求属于哪种模式(相对于一个需要在线切换模式的假设方案,这是一个真实的优势)。
7.3 短序列——检查是否存在回退
论文(§6.2,论文原文对应的 Fig.3 本文未单独重现)报告称,在短序列(4K–16K)上,VPP 相对 DCPP”几乎没有回退”——在 Qwen 和 GLM 上表现相当,VPP 在 DeepSeek 上取得最大优势,4K chunk 大小下达到 10.8%。相对 CPP,VPP 在 Qwen 上最多回退 6.8%,原因归结于 Qwen 较小的激活参数量(3.3B),这使得调度开销相对计算量更难被摊薄——也就是说,当模型本身跑起来很便宜时,没有足够的真实工作量来摊销 VPP 自身的记账开销。这是作者如实报告而非隐藏的一个负面结果,是好的实践,不过正如下文第 9 节所讨论的,这一点也相对缺乏深入分析。
7.4 收益来源拆解(§6.3,表 2)
为了理解 VPP 的 TTFT 改善究竟来自哪里(而不只是报告一个聚合数字),作者对 DeepSeek-V3.1 的 512K 工作负载做了 profiling,把差异拆解为计算、气泡、暴露通信三个维度。

| DCPP(秒) | VPP(秒) | 差值 (秒) | /DCPP | |
|---|---|---|---|---|
| (a) 端到端拆解 | ||||
| 计算 | 279.07 | 250.84 | -28.23 | -10.11% |
| 气泡 | 20.47 | 0.39 | -20.07 | -98.04% |
| 暴露通信 | 22.68 | 30.63 | +7.95 | +35.05% |
| 净值(profiler) | -40.36 | -12.53% | ||
| (b) 计算细分 | ||||
| 注意力 | 228.13 | 233.08 | +4.96 | +2.17% |
| Slice | 22.90 | 5.18 | -17.72 | -77.38% |
| MatMulV3 | 13.06 | 2.54 | -10.52 | -80.55% |
| (c) 气泡细分 | ||||
| 空闲 | 18.61 | 0.39 | -18.22 | -97.90% |
| 启动阻塞 | 1.85 | 0.00036 | -1.85 | -99.98% |
这里有几点值得停下来仔细想想:
- VPP 的注意力 kernel 时间实际上更长(+2.17%),而不是更短,这一点相对 DCPP 是一个重要的、不那么直观的结果。这告诉我们一件重要的事:VPP 的胜利不是来自算注意力算得更快。它来自更少、更大的注意力调用——DCPP 发起更多、更小的注意力调用(对应它更多的小 chunk),虽然每次调用本身很便宜,但总的kernel 启动和调度开销(主要体现在”Slice”和”MatMulV3”上,分别下降了 77% 和 81%)才是主导因素。这个区分很重要:它告诉你 VPP 不是一个”更快的 kernel”的故事,而是一个”消除碎片化”的故事,这与论文自己的叙事一致。
- 气泡削减接近完全消除(98.04%),这正是摘要开头就抛出的那个头条数字。
- 暴露通信反而增加了 35%——这是这个方法诚实的代价。论文解释这主要来自额外的 AllGather 辅助开销:VPP 发起的 AllGather 调用次数减少了 4.58 倍,但每次调用的平均延迟增加了 11 倍,净效果是 AllGather 总开销增加了 39.7%(5.27 秒)。然而,重叠比率——有多少通信被隐藏在有用计算之后——从 0.05% 跃升到 51.75%,这正是净效果依然是胜利的原因,即便原始通信时间本身在增加。这是一个真正有意思的系统层面的教训:即使某个原始指标出现回退(通信时间增加),只要重叠改善得足够多,净结果依然可以是胜利;如果只看”通信时间增加了 35%“这个表面数字而不看这个背景,会得出误导性的结论。
7.5 稀疏注意力的影响(作者主动披露的一个真实局限)
论文明确研究了 GLM-5.2 的 DSA(稀疏)注意力在 128K token 时的表现,以理解为什么 VPP 对这个模型的优势在更长上下文时会收窄。由于 DSA 降低了注意力成本随累积前缀长度增长的速率,VPP 整套推导所依赖的近似线性 增长不再严格成立:前面的 chunk 依然能从 V 形交错调度中获益,但”调度效率随序列变长逐渐退化”,气泡开始重新出现。作者坦承 VPP”在低于 512K 的上下文下依然取得正向收益”,但在长序列扫描(图 4)中并未报告 GLM 在 512K 及以上的具体数字(图 4 的 GLM 面板在 512K/1M 处显示了一个”X”标记,意味着没有报告数据)——这个空白值得特别指出(见第 9 节)。
7.6 VPP 变体消融实验(§6.4)
在一个 256K token 的 DeepSeek 工作负载上,32K chunk 大小,三个并发请求,论文分离出了每一项优化各自的贡献:
- Vanilla VPP → VPP-Async:端到端延迟降低 3.13%,得益于把未重叠通信削减 48%,并把通信重叠比率从 0.02% 提升到 54.03%。
- VPP-Async → 带流水线打包的 VPP-Async:(相对 vanilla VPP)进一步降低到总计 6.89%,得益于把跨请求气泡延迟削减 33.2%。
- 三个变体之间的计算时间本身最多相差 0.82 秒(0.42%)——这确认了(与 §7.4 一致)各变体之间的增益几乎全部是一个调度效应,而不是计算效率效应。

气泡比例的数字讲的是同一个故事,只是量化了一遍:11.0%(vanilla VPP)→ 8.3%(VPP-Async)→ 2.4%(带打包的 VPP-Async),每一步优化都精准削减了之前识别出的某个具体空闲来源。
7.7 Chunk 大小敏感性(§6.4,对应论文原文图 8)
把 chunk 大小从 8K 扫到 32K,序列长度从 64K 到 1M,得到一个反直觉的结果:24K 在大多数配置下表现最好——不是因为它是某种普遍最优的 token 数,而是因为它不能把序列均匀切分——最后那个更小的”余数”chunk 恰好在流水线排空阶段执行,它的计算和通信能与前面的 chunk 重叠,从而缩小了本来在均分 chunk 大小下会更大的尾部气泡。例外情形:在 DeepSeek 和 GLM 上的 64K 序列长度,吞吐量随 chunk 变小单调下降,因为只有寥寥几个 chunk 时,流水线永远达不到稳态,更小的 chunk 只是直接缩短了(本来就占主导的)尾部气泡,而没有提供平衡收益。
7.8 数值化总结表:关键结果一览
为了方便回顾,这里把前面分散在 §7.1–§7.7 中的关键数字集中到一张表里:
| 实验场景 | VPP 相对 CPP | VPP 相对 DCPP | 关键观察 |
|---|---|---|---|
| 长序列(DeepSeek,最佳情况) | +10.0% 吐吐量 | +2.6%–13.1%,随长度增长 | 优势随序列变长而扩大 |
| 长序列(GLM,DSA 稀疏注意力) | +4.1% 吐吐量 | +8.5% 峰值(64K),后逐渐收窄 | 线性 假设失效的直接证据 |
| 混合长度(DeepSeek,最佳情况) | +13.8% 吐吐量 | +6.7% 吐吐量 | 最贴近生产环境的场景 |
| 短序列(Qwen,最差情况) | -6.8% 回退 | 大致持平 | 小模型难以摊销调度开销 |
| 512K DeepSeek 深度拆解 | — | TTFT 净降 12.53%(40.36 秒) | 气泡降 98.04%,通信增 35.05% |
| 消融实验(vanilla → 完整版) | — | 端到端延迟降 6.89% | 计算时间变化 ≤ 0.42%,证明纯调度效应 |
把这些数字摆在一起看,整体图景相当一致:VPP 在”注意力成本主导、序列足够长”的场景下收益最大,在”模型很小、序列很短”的场景下收益最小甚至轻微回退,而在真实世界最常见的混合长度工作负载上,它的收益一直为正。
8. 可复现性说明
已披露的信息:模型名称/规模、硬件(16 块 Ascend 910C,64GB HBM)、软件栈版本(PyTorch 2.10.0、CANN 8.3、vLLM/vLLM-Ascend 0.23.0)、并行配置(TP8 + PP2)、chunk 大小扫描范围、工作负载构造方式(GSM8K 衍生的混合长度,附具体的百分位统计),以及一个开源实现分支。
以下内容未被披露或存在模糊之处,对精确复现会有影响:
- 用于 §5.2/§5.4 描述的异步通信组切换的具体 HCCL 通信组配置参数(缓冲区大小、超时设置)。
- 算法 2 中”打包窗口”大小的确切启发式或阈值,超出 这个近似估计之外——这是一个固定常量、一个运行时估计值,还是每个请求自适应调整的?论文描述了意图但没有给出精确的在线估计流程。
- DCPP 自身内部的成本估计/校准超参数(论文只说使用了”vLLM-Ascend 推荐配置”,而没有列出底层在线估计器的具体参数),这在一定程度上限制了对 DCPP 基线数字的独立验证。
- 除”平均 4 次运行”之外,没有报告任何运行间方差——所有吞吐量/TTFT 数字都没有给出标准差或置信区间。
9. 局限性、作者低估的部分与批判性分析
(a) 这篇论文特有的弱点。
- 全文任何地方都没有报告标准差或方差。 论文里每一个数字——13.1% 的头条收益、98.0% 的气泡削减、消融实验的每一个差值——都只是 4 次运行的裸平均值。对于一篇在好几处都在讨论个位数百分点差异的系统论文来说(例如 GLM 混合工作负载上 1.3%–1.6% 的收益),完全没有方差信息使得读者无法判断这些较小的收益究竟是有统计意义的信号,还是噪声范围内的波动。这是一个直白的、本可以修复的疏漏,实实在在削弱了对较边缘结果的信心。
- GLM/DSA 局限被报告了,但没有被充分刻画。 作者定性地告诉我们 VPP 的优势在稀疏注意力下会收窄,并在 512K 之后停止为 GLM 报告数字,但他们没有量化究竟多严重线性 假设会失效(例如,DSA 相对稠密注意力实际测得的 增长曲线是什么样的?),也没有在本文范围内尝试任何针对这一情形的修复或自适应机制——它纯粹被标注为未来工作。考虑到稀疏注意力在前沿长上下文模型里正变得越来越常见(这正是 GLM-5.2 一开始采用 DSA 的原因),这在一篇卖点就是长上下文服务的论文里,是一个真实存在的空白。
- 只深入评估了一个特定的两 rank、四虚拟阶段的具体实例。 所有实验都使用 TP8+“VPP2”,即 2 路流水线并行。§4.2 的核心推导是以 2-rank/4-stage 情形陈述的;论文声称折返拓扑”能推广”到更深的流水线,但没有展示,比如说,4 路或 8 路流水线并行下的实验结果——而在那种情形下,匹配跨 rank 工作量的组合数学(式 3b 那种干净的代数匹配)大概率会难得多,尤其是当你增加更多虚拟阶段、每阶段成本更参差不齐的时候。
(b) 作者低估或省略的局限性。
- 短序列 Qwen 回退(相对 CPP 最多 -6.8%)只用一句轻描淡写的话解释(“更小的激活参数量……使得调度开销更难被摊销”),没有给出任何类似表 2 那种针对长序列情形的分解分析。考虑到表 2 的方法(计算/气泡/通信分解)显然是作者手头现成、并在论文其他地方用过的工具,唯独在这个负面结果上不用它,读起来像是一种编辑上的取舍——在坏消息的地方少投入分析精力。
- 完全没有讨论调度器复杂度或维护成本。 VPP 引入了双队列调度器、折返式层分配机制、跨 rank 重排序逻辑、跨请求打包启发式——这比 CPP(在各阶段间简单轮转)甚至比 DCPP(其复杂度至少集中在一个单一、易于理解的”估计成本、调整 chunk”循环里)要复杂得多。论文没有讨论工程/维护开销、可调试性,也没有讨论 VPP 如何与真实生产系统中同时需要处理的其他关切(例如请求抢占、优先级调度、SLO 感知的准入控制)相互作用。
- 论文没有讨论折返布局的显存影响。 让 chunk 在 rank 之间穿行再折返意味着在途 chunk 的激活值可能需要缓冲更长时间,而(§5.4 提到的)“额外一个在途 batch”的记账逻辑本身也有一定的显存占用,论文全文都没有量化这一点。
(c) 具体、可操作的改进建议。
- 为每一个吞吐量/TTFT 数字报告方差/置信区间(至少是 4 次运行的标准差),尤其是 GLM 混合工作负载那些较小的收益,那里信噪比最不清晰。
- 把表 2 风格的计算/气泡/通信分解也应用到短序列 Qwen 回退的情形上,让读者能具体看到这个损失究竟是由调度器开销、通信,还是别的什么主导的——目前这只是被断言,而没有被展示出来。
- 至少提供一个流水线并行度大于 2 的实验数据点(例如 TP4+VPP4 或类似配置),哪怕规模较小,来实证支撑”能推广到更深流水线”这一说法,而不是把它纯粹留作一个架构上的断言。
- 针对 DSA/稀疏注意力的局限,报告 GLM 在 512K/1M 时实际测得的 -vs- 曲线,并与式(2)中的线性模型做定量对比——这能让读者(以及作者自己在后续工作中)清楚地看到,在需要某种修正机制(例如自适应的折返点重新校准)之前,还有多少余量。
9b. 与相关流水线平衡工作的对比
有必要精确定位 VPP 相对论文自己引用的两条相关工作线索(第 3 节)的位置,因为这些区别很容易被模糊掉:
- gLLM(token 节流):主要按token 数量来平衡流水线 micro-batch,没有显式建模上下文相关的注意力成本。这意味着 gLLM 的平衡信号是一个代理指标(token 数量),用来近似真实的成本驱动因素(累积的 KV 缓存长度),而 VPP 的式(2)直接建模了真实的延迟增长。权衡在于:gLLM 的代理指标在线计算更简单,但在因果注意力这个 VPP 所针对的具体场景下,会系统性地误估成本。
- TeraPipe / Seq1F1B(训练时的 token 级序列切分):这些方法把非均匀 chunk 边界搜索表述为一个优化问题(TeraPipe 用动态规划),来平衡 micro-batch 执行时间——概念上是训练时代对 DCPP 的类比。这两者都延续了DCPP 式的哲学:通过调整大小来平衡,而不是 VPP 那种固定大小、重塑布局的哲学。由于这些方法针对的是训练场景(在训练场景里,micro-batch 会在很多训练步骤中重复出现相同的结构,使得运行时校准的成本容易被摊销),反对动态调整大小的碎片化开销论点在那里的说服力比在 VPP 针对的推理服务场景里要弱——在推理服务场景中,每一次预填充请求通常都是独一无二的,校准成本无法在重复的相同 batch 之间被摊销。
- SGLang / vLLM-Ascend 自身的 DCPP 实现:两者都采用运行时 profiling 和在线校准来预测 chunk 延迟并动态调整边界——这正是 VPP 的动机性实测(第 4 节)在实证层面对比、并在长序列长度下胜出的那一类方法。
贯穿始终的一条主线是:此前的每一种方法都把 chunk 延迟异质性当作需要被消除的东西;VPP 是这一脉络里第一个把它当作工作负载一个已知、可利用的结构性特征,并直接围绕它来设计流水线布局的方法。
9b2. 补充:把结果放到生产部署视角下重新评估
从工程落地角度多问一句:如果你是一个已经在生产环境里跑着 vLLM 或 SGLang 的团队,看到这篇论文会怎么想?具体而言有三个落地问题值得先问自己:
- 我的流量真的是长序列主导吗? 若你的业务中短请求占绝对多数(如典型的聊天机场景),那么基于 §7.3 的结果,VPP 在短序列上对 CPP 有回退风险(Qwen 最多 -6.8%),引入它的收益未必能覆盖这个成本。
- 我的模型用的是稠密注意力还是稀疏注意力? §7.5 已经明确告诉我们,若你的模型使用类似 DSA 这样的稀疏注意力,收益会随上下文变长而收窄,并且论文本身对 512K 以上的情况没有报告数据,意味着在那个区间的行为目前未知。
- 我能接受多一套调度器的工程复杂度吗? 如第 9 节所论,VPP 引入了双队列调度器、折返式层分配、跨 rank 重排序、跨请求打包启盈式算法——这些都是真实的工程维护成本,而论文对这一块几乎未加讨论。
对于大多数长上下文、MoE 为主、使用因果稠密注意力(而非 DSA 类稀疏注意力)的部署场景,本文的证据链条相当完整;但对于以短请求为主、或已经采用稀疏注意力的团队,建议先自行复现论文的§6.2/§7.5 实验,确认自己具体工作负载下的收益区间,而不是直接相信论文汇报的平均数字。
9c. 补一个思想实验:如果把 VPP 的思路搭在 DCPP 上会怎样
一个自然会想到的问题是:能不能把 VPP 的 V 形折返布局和 DCPP 的动态 chunk 调整结合起来?论文本身没有讨论这个混合方案,但根据它自己提供的证据,我们可以做一个推理。
假设在 GLM 的 DSA 稀疏注意力场景下,先用一个轻量级的在线估计器监测 是否仍然近似线性,若偏离超过某个阈值,则对后续少数 chunk 的边界做小幅度的动态调整(而不是像 DCPP 那样对每个 chunk 都做)。这相当于把 DCPP 当成一个局部的、仅在线性假设失效时才触发的”修正项”,而非全程默认启用的机制。
这个混合方案的代价是实现复杂度会进一步上升:调度器需要同时维护两套逻辑(V 形折返的固定布局 + DCPP 的在线估计器),并且需要一个明确的切换阈值来决定何时从”纯 V 形模式”切换到”V 形 + 微调模式”。这正是本文第 9 节指出的那个未解决问题的一个具体可行的未来工作方向:不是在”固定大小”和”动态调整”之间二选一,而是设计一个分层机制:默认走 VPP 的固定布局路径,仅当在线监测到 偏离线性超过阈值时,才局部触发 DCPP 风格的微调。
9d. 术语对照补充
为方便读者核对本文中英文术语与论文原文的对应关系,补一张简要对照表:
| 本文中文词 | 论文原文英文 | 含义 |
|---|---|---|
| 分块预填充 | Chunked Prefill | 将 prompt 切分为固定大小块依次处理 |
| 流水线并行 | Pipeline Parallelism (PP) | 按层切分模型到多设备 |
| 流水线气泡 | Pipeline Bubble | 某阶段空闲等待的时间 |
| 动态分块预填充流水线并行 | Dynamic Chunked prefill Pipeline Parallelism (DCPP) | 运行时动态调整 chunk 边界以拉平执行时间 |
| 虚拟阶段 | Virtual Stage | VPP 将模型层切分得比物理 rank 数更细的逻辑单位 |
| 折返布局 | Fold-back Layout | 边界阶段与中间阶段分别固定在不同 rank 上、形成 V 字运行轨迹的布局 |
| 排空气泡 | Drain Bubble | 请求尾部因一个 rank 无事可做而产生的空闲 |
| 流水线打包 | Pipelined Packing | 把下一个请求的前导 chunk 填进当前请求的排空窗口 |
| 暴露通信 | Exposed Communication | 未被计算重叠遮盖、直接讫逗到关键路径上的通信时间 |
| 重叠比率 | Overlap Ratio | 通信时间中能与计算并行执行的比例 |
10. 结语
VPP 是一个很干净的系统论文范例,它的胜利来自改变不平衡被吸收的地方,而不是试图消除不平衡的根源。固定 chunk 大小下,注意力成本在因果掩码下近似线性增长,这个观察本身并不新鲜——DCPP 隐含地也是建立在同样的观察之上——但 VPP 的洞察在于,这种可预测性可以通过流水线布局设计(V 形折返调度)来利用,而不是通过调整 chunk 大小,而后者的碎片化成本正是在长上下文长度下真正占主导的因素。头条数字——在一个真实的 512K token DeepSeek-V3.1 工作负载上,相对现有生产级 DCPP 基线,气泡比例降低 98.0%,吞吐量最高提升 13.1%——是一个扎实、可信的系统贡献,背后有真正细致的多层次拆解(计算 vs. 气泡 vs. 通信)支撑,避免了只报告聚合数字这种常见陷阱。论文对自身方法核心假设(逐 chunk 延迟线性增长)在哪里会失效的坦诚态度——特别是对 GLM-5.2 这类采用 DSA 稀疏注意力的模型——是科学严谨性的良好信号,尽管(如第 9 节所述)这个局限本应得到比目前更定量的处理。如果你正在构建或维护一个采用流水线并行的长上下文 LLM 服务系统,这篇论文的 V 形调度思路本身非常值得仔细阅读,无论你用的是 Ascend 还是 GPU 硬件——式(2)–(3b)里的核心代数与硬件无关,可以直接迁移。