两个同样长的 prompt,未必需要同样多的 GPU。一个请求第一次出现,要计算全部输入;另一个请求沿用已有对话,只增加很短的后缀。如果调度器只看原始长度,就可能把已经命中大部分前缀的请求也送进高并行度 worker,支付通信和多卡占用,却没有足够的剩余工作来利用它们。

反过来,只追逐缓存也不够。唯一持有前缀的 worker 可能已经排着很长的队,而另一组 GPU 虽然需要重算,却能更早完成。问题逐渐从“这个长请求能不能并行”变成:当前该把请求交给谁,集群里又应该保留多少种并行度的 worker?

2026 年 9 月 4 日公开的预印本 Adaptive Context Parallelism for Production LLM Serving提出 Vertumnus,将这两层决策与前缀副本管理放在一起。本文依据 v1 的 §2–§9 解释其设计,并用自制算例推导成本;没有运行作者的服务系统,也不把论文实验写成自己的 GPU 实测。

一张卡上的请求,和占用多张卡的请求,不能只比较耗时

Context Parallelism(CP)沿序列分布输入及 Attention 工作。一个 degree 为 C 的 worker 由 C 个 rank 协作,每个 rank 映射到一张 GPU。它与把多个独立请求分给不同 worker 的数据并行不是同一层事情。

CP 可以缩短单条长请求的 prefill,但也减少固定预算中独立服务的通道数。8 张 GPU 组成两个 CP=4 worker,和组成四个 CP=2 worker,拥有相同 GPU 数,却有不同的单请求能力与并发形态。每个 worker 内仍可有引擎自己的批处理,因此“通道更多”也不能简单等同于吞吐恰好翻倍。

把一条请求从 200 ms 降到 140 ms,若同时从 2 张卡增加到 4 张卡,执行阶段的 GPU-time 会从 400 GPU-ms 增到 560 GPU-ms。延迟变好了,但单位请求占用的资源时间变大了。在高负载下,这份机会成本会体现在其他请求的排队中。

Vertumnus 研究的是 P/D 分离系统中固定预算的 prefill 池。Decode 池独立配置,论文不通过修改 decode kernel 或 batching 来降低 TPOT。其 CP 基础与训练中的序列分布有联系,但本文关心的是持久 worker 的组织与路由;分布式 Attention 本身可参阅 Context Parallel。

命中六个 token 后,剩下的并不是一个长度二的独立 Attention

设输入总长为 L,其中 P 个前缀 token 的 KV 可在目标 worker 直接复用,剩余后缀长度为 \(R=L-P\)。不必重新生成前缀的 Q/K/V 和对应层输出,但后缀 query 仍然需要关注前缀 KV。

对于普通 dense causal Attention,第一个后缀 query 可见 P 个前缀和自己,第二个可见 P 个前缀及两个后缀位置。于是剩余合法 query-key pair 数为:

\[W(R,P)=\sum_{r=1}^{R}(P+r) =RP+\frac{R(R+1)}2\]

前一项是后缀读前缀,后一项是后缀内部因果关系。这是论文 §2.2构建性能模型的起点。

例如 L=8:完全未缓存时有 \(8\times9/2=36\) 对;若缓存 P=6,只留下两个 query,仍有 \(2\times6+3=15\) 对,而不是 3 对。逐行看就是第七个 token 读 7 个位置,第八个读 8 个位置。

在固定 L 时,省掉的是已经完成的前缀三角形:

\[W(L-P,P)=\frac{L(L+1)-P(P+1)}2\]

这个推导也说明,不能把“命中 75% 输入 token”直接换算成“prefill 时间下降 75%”。Attention、投影、MLP、通信与缓存访问各有成本,减少量并不遵循同一个比例。这里 W 是 dense causal 的 pair 计数,既不是完整 FLOP 数,也不是 sparse、linear 或 hybrid Attention 的通用计算量定律。

还有一个容易遗漏的下标:P 属于请求与 worker 的组合。同一请求在 A 上可能命中 6 个 token,在 B 上只命中 2 个。全局目录知道某段前缀存在,不代表每台 worker 都已经持有可用副本。

当 P=L 时,公式给出零个未缓存 query 的 pair,只说明这本 Attention 计数账归零。真实服务仍可能需要处理边界 token、生成首个 logit、同步状态和传输 KV,具体取决于引擎的完整前缀命中约定。不能顺手把它解释成整条请求的 TTFT 为零。

先预测服务时间,再问多占 GPU 是否值得

论文使用无排队 profiling 拟合特定模型和硬件上的 prefill 时间:

\[\widehat E_C(R,P) =d+aR+bP+\frac{cR+\alpha W(R,P)}C\]

d 表示固定开销;aR 汇总不随 CP 等比例缩短的后缀相关成本;bP 表示缓存前缀的访问等开销;括号内包含可分布的逐 token 工作与 Attention 工作特征。§3.2明确采用拟合系数,而不是宣布真实执行都能严格按 1/C 加速。

为了看清这个模型的含义,暂把同一缓存条件下的两部分记为:

\[A=d+aR+bP,\qquad B=cR+\alpha W(R,P)\]

于是 \(\widehat E_C=A+B/C\)。增加 C 只能缩短第二部分;当 B 因缓存命中变小时,同样的并行度更容易被 A 主导。这是一种类似串并行分解的解释,不意味着框架通信开销在任意 C 上都恰好不变。拓扑、批大小、kernel 选择或模型结构变化后,profile 的有效范围需要重新验证。

对于候选 worker w,令服务时间预测为 \(s_{i,w}\),队列代理为 \(q_w\)。最简单的选择是比较 \(q_w+s_{i,w}\),但 Vertumnus 再加入 GPU-time 权衡:

\[J_{i,w}=q_w+\beta s_{i,w} +\lambda\left[C_ws_{i,w} -C_{min}\widehat E_{C_{min}}(R_{i,w},P_{i,w})\right]\]

其中 \(\beta\ge1\),\(\lambda\ge0\),\(C_{min}\) 是合格候选中的最小 CP degree。选择成本最小的 worker。λ 将 GPU-time 映射到成本尺度;它不是学习率,也不是无需校准的性能常数。

方括号里的基线必须保持当前候选的 P、R 不变:比较同一缓存条件下,采用较小 CP degree 会耗费多少 GPU-time。不能拿另一台 CP 较小、但没有该前缀的 worker 的服务时间直接代入,否则“多用卡的成本”会混入“换了缓存状态”的成本。§5.3采用的正是这个反事实基线。

在上述简化表达式内,可以进一步推导:

\[C\widehat E_C-C_{min}\widehat E_{C_{min}} =(C-C_{min})A\]

B 所代表的理想可并行工作在 GPU-time 中相消,额外代价来自每张卡共同承受的那部分 A。这个等式只属于给定拟合形式与相同缓存条件,不能推成所有 GPU 系统的物理规律。

下面全是人为设定的毫秒算例,不是论文数据。取 \(C_{min}=1\),两候选都无排队,\(\beta=1\),λ 数值取 1:

假设成本 CP=1:服务时间 / J CP=4:服务时间 / J 选择
A=2,B=24 26 / 26 8 / 14 CP=4
A=2,B=4 6 / 6 3 / 9 CP=1

第一行中,CP=4 多消耗 \(4\times8-26=6\) GPU-ms,却省去 18 ms 服务时间,因此仍值得。第二行仅为隔离机制而保持 A 不变、减小 B;多卡仍多付 6 GPU-ms,却只省 3 ms,于是小 degree 更合适。真实缓存命中也可能改变 A,应由完整 profile 计算。

即使第一行不变,把 λ 数值提高到 4,CP=4 的 J 也会成为 \(8+4\times6=32\),高于 CP=1 的 26。这不是找到了一个“正确 λ”,而是显示延迟与资源占用之间确实需要运营目标来取舍。

排队、缓存和候选资格,缺一项都会选错

再看一个只研究缓存与排队的独立例子,设 λ=0:热缓存 worker 的队列代理为 20 ms,服务时间 4 ms;冷缓存 worker 没有排队,但服务时间 14 ms。β=1 时,两者成本为 24 与 14,立即重算反而更早完成。

若 β=3,成本变成 32 与 42,又会偏向热缓存 worker。增大 β 会放大服务时间差,可能保留缓存局部性,但它也会放大 CP 带来的服务时间差,并非只作用于缓存的独立开关。过度偏好某台 worker,仍可能制造更长队列。

论文用未完成请求的预测工作量作为轻量队列代理;这不等于在 continuous batching 下精确知道每个请求何时开始。实际进度、批次形成和共享资源都可能造成预测误差,因此应监控预测误差与真实等待,而不是只比较一张静态成本表。

预测误差还会进入反馈循环:一台 worker 的服务时间长期被低估,就会持续得到更多请求;它的队列变长后,局部缓存优势可能再次把请求吸引回来。部署验证既要看平均误差,也要按 CP degree、缓存比例和请求长度分桶,观察错误是否系统性偏向某一类候选。仅在无队列的孤立请求上拟合得好,并不自动证明满载时路由稳定。

成本排序之前,还要先构造候选集合。正在 drain、不可用、无法满足序列长度或内存要求的 worker 不应参与竞争。Cache miss 本身则不是失格理由,因为可以重算。选中后应立即记入预计负载,避免一批并发到达的请求都看到同一份“空闲”快照,挤进同一台机器。这些步骤是请求调度设计的一部分,而不是公式之后可有可无的实现细节。

路由决定用谁,慢一层的控制器决定谁应该存在

如果整个池只有 CP=4,路由算法再聪明也选不到 CP=2。Vertumnus 因而把第二层决策放到较慢的负载窗口:在固定 GPU 数内拆分或合并 worker。

固定 8 张 GPU 的示意:
[CP4, CP4] → [CP4, CP2, CP2] → [CP2, CP2, CP2, CP2]
               拆分一个 CP4,不是新增两组 GPU

高负载持续超过容量阈值时,拆分增加独立服务通道;负载较低且长请求更需要并行时,合并提供更大的 CP degree。这里的容量来自“仍能满足目标 TTFT SLO 的 profile”,不是仅按 GPU 数相加。

长请求比例与整体负载可能给出相反信号:长请求变多似乎应该合并,但合并后若总服务容量不够,排队会抵消单请求收益。论文让容量约束优先,并要求条件持续多个观察窗口才重组;拆与并使用不同触发区域,避免短暂噪声造成来回切换。§6中的操作还必须符合预先建立的拓扑分组,不是任意挑几张跨节点 GPU 拼成 worker。

这不同于启动新的推理实例。Vertumnus 的各 rank 已有常驻模型状态,必要的通信组也预先建立并预热,因此能在原地切换。不能把这一前提删掉后,宣传成“任意模型并行都可瞬间重配”。

固定预算还意味着转换期间没有额外备用 GPU 凭空出现。参与 drain 的 worker 暂停接收新请求,其他 worker 必须承受转移的队列;最终组成的容量足够,并不代表转换过程也没有延迟尖峰。选择 drain 较短、缺失 KV 搬运较少的相邻操作,是把这一过渡成本纳入控制,而不是只检查转换后的拓扑图是否好看。

真正困难的不是修改 degree,而是让所有状态属于同一代配置

设 CP=4 worker 正在执行一个 engine step。若其中两张卡先切到 CP=2,而另两张仍按旧 collective 通信,序列分片与通信组就失去一致性。正确切换必须有明确边界:

移出候选集合、停止新 admission
        ↓
重定向排队请求,等待旧配置中的执行结束
        ↓
参与 rank 到达共同边界,启用预建通信组
        ↓
提交新 epoch,核对 rank 确认与调度/缓存元数据
        ↓
新 worker 才重新接收请求

论文把 CP 几何固定在一次 engine step 内;边界处观察到的新配置只作用于下一步。合并时,参与的兄弟组要在共同模型输入边界停稳,控制器等所有 rank 确认新 epoch 后才暴露新 worker。这是在系统层保护计算语义,不是每个 token 到来时随意修改一个整数。

缓存又增加了一层状态。旧 KV 仍留在 HBM,只能说明数据没有被删除;它可能尚未按新 CP 几何拥有完整的 rank ownership 与寻址。论文不把全部重新分布塞进切换关键路径,而是记录待协调状态,后续复用时从有效副本只传缺失块。§6.2 与 §8给出了这一协议。

因此需要区分三种情况:逻辑前缀在全局目录中存在、必要块在某处物理驻留、目标 worker 已可立即复用。只有最后一种才是路由成本里的 ready hit。迁移中源块要防止被驱逐,目标要预留空间;全部必要块安装完成再发布映射。若有效源副本已经消失,缺失部分应作为普通 cache miss 处理,不能继续使用旧命中长度估算执行。

为什么全局 Prefix Trie 还需要记录“本来可以命中的需求”

一个热点前缀仅在 CP=2 有副本,而适合 CP=4 的请求频繁出现。如果只统计 CP=4 的实际命中次数,它将永远是零;但零命中可能来自“没人需要”,也可能来自“根本没有副本”。这两种情况对复制决策正好相反。

Vertumnus 的全局 trie 因而记录两类统计:已有副本实际承担了多少复用,以及某个 degree 缺副本时损失了多少预测缓存收益。后者使用去掉临时队列影响的偏好 degree,再累计从父前缀扩展一个块带来的边际服务时间节省,避免把完整长前缀的收益在每个节点重复计数。§7 的两段算法据此分别产生跨 degree 首副本、同 degree 追加副本与回收候选。

这些不是无限复制指令。复制占用带宽与 cache 容量,目标还必须补齐缺失的祖先路径;回收不能破坏仍需保留的后代前缀。统计随时间衰减,复制和回收采用不同阈值,每轮执行数量也受限制。

缓存管理的价值因此未必表现为命中率大涨。相同热点多一份副本,可以让请求在保留命中的同时避开拥堵;命中率几乎相同,TTFT 却可能下降。只优化全局 hit rate,会漏掉“命中发生在哪里”和“为了命中等了多久”。

论文结果回答了什么,尚未替部署回答什么

§9报告的实验使用 64 张 H20-3e,其中 40 张用于 prefill,24 张用于独立 decode;比较固定预算、模型配置与缓存容量下的多种工作负载。Decode 被配置为不成为瓶颈,因此结果不能直接推广到 decode 已经饱和的集群。

Qwen3-30B-A3B 在最高测试负载的 Production workload 上,相对最强基线的 mean TTFT 降低 28.1%;13.3 个百分点的 token-weighted SLO 提升则来自 Tool-Agent workload。它们不是同一组实验结果,更不是所有请求都快了相同比例。

Token-weighted SLO 的权重是每条请求的输入长度。两个长度分别为 100、900 的请求,若只有短请求达标,请求达标率是 50%,输入 token 加权达标率却只有 10%。这不是测量错误,而是回答了不同的问题:有多少请求被服务好,与有多少输入需求来自达标请求。

论文的观察窗口与缓存扫描周期均为 20 秒;实测一次重组的 drain 加 switch 为 1.1–5.1 秒,switch 本身少于 1 秒。不能把“20 秒观察”叫作“20 秒停机”,也不能把“switch 少于 1 秒”写成整个转换零成本。副本后续协调还可能给首次复用增加成本,需与切换关键路径分开观察。

迁移到其他部署时,至少应重新测四组量:不同 P/R/C 下的预测误差;固定预算的 TTFT 与 GPU-time;缓存冷暖、热点迁移与副本带宽;drain、epoch 切换和首次复用延迟。同时检查下游 decode 容量,避免 prefill 提速后只是把队列推向另一侧。

本文的计数、成本比较与状态反例可用配套 CPU 检查复算:

node scripts/reviews/sep30-vertumnus-examples.mjs

它验证这里写出的数学与简化不变量,不运行 RTP-LLM,不验证分布式 collective、真实重配置协议或 GPU 性能。Vertumnus 值得关注之处,不是某个并行度永远最好,而是把“还剩多少计算”“缓存在哪里”“哪些 worker 应该存在”作为彼此影响的决策。长上下文服务的优化对象,正在从单条执行路径扩展到有状态、可重组的整个 prefill 池。