ReMP:LLM 推理服务中的低停机运行时并行拓扑重配置

笔记日期: 2026-06-25 笔记作者: Zhongzhu Zhou 论文标题: ReMP: Low-Downtime Runtime Model-Parallelism Reconfiguration for LLM Serving 作者: Haipeng Yuan, Kaining Zheng, Yongshu Bai, Yuchen Zhang, Yunquan Zhang, Baodong Wu, Xiang Gao, Daning Cheng arXiv: 2606.18741 状态/Venue: arXiv 预印本,2026 年 6 月(中科院计算所、之江实验室、无问芯穹)

一句话总结

ReMP 把 LLM 推理系统中长期被视为”不可变”的张量并行(TP)和流水线并行(PP)拓扑变成了可以在服务运行时动态切换的资源——切换时间从重启的数分钟压缩到 1–7 秒,同时保留了已有的 KV Cache,避免了昂贵的 prefill 重计算。

前置知识:读懂这篇论文你需要知道什么

这篇论文同时涉及分布式模型并行和 LLM 推理服务两个领域。下面我从零开始把关键前置知识梳理清楚。

1. 自回归解码与 KV Cache

Transformer 语言模型是逐 token 生成的。在解码步骤 tt,模型接收所有已生成的 token,输出下一个 token 的概率分布。核心运算是 Attention:新 token 的 query 要对历史所有 key/value 做内积。

如果每步都重新算过去所有 token 的 key 和 value,代价是 O(T2)O(T^2)TT = 序列长度)。KV Cache 解决这个问题:第一次算完某个 token 的 key/value 后,就把它存到 GPU 显存里,后续步骤直接复用。代价从 O(T2)O(T^2) 降到每步 O(T)O(T),但带来了显存开销:

MKV=2LHkvdkvTBsizeof(dtype)(1)M_\text{KV} = 2 \cdot L \cdot H_\text{kv} \cdot d_\text{kv} \cdot T \cdot B \cdot \text{sizeof}(\text{dtype}) \tag{1}

其中 LL 是 Transformer 层数,HkvH_\text{kv} 是每层 KV head 数,dkvd_\text{kv} 是 head 维度,TT 是序列长度,BB 是批大小。对 Llama2-70B(80 层、8 个 KV head、dkv=128d_\text{kv}=128)、4K 上下文、批大小 32、BF16 精度:

MKV=2×80×8×128×4096×32×210.7 GB(2)M_\text{KV} = 2 \times 80 \times 8 \times 128 \times 4096 \times 32 \times 2 \approx 10.7 \text{ GB} \tag{2}

KV Cache 的物理位置和模型并行拓扑强绑定——这是 ReMP 核心问题的根源。

2. 张量并行(Tensor Parallelism,TP)

张量并行(Megatron-LM, 2021)沿某个维度把权重矩阵切分到多张 GPU 上,每张 GPU 只计算部分结果,最后通过 AllReduce 汇总。

以 Query 投影矩阵 WQRd×HdkW_Q \in \mathbb{R}^{d \times H \cdot d_k} 为例,按 head 维度切分为 rr 份:

WQ=[WQ(0)WQ(1)WQ(r1)](3)W_Q = [W_Q^{(0)} \,\big|\, W_Q^{(1)} \,\big|\, \cdots \,\big|\, W_Q^{(r-1)}] \tag{3}

GPU ii 持有 WQ(i)Rd×(H/r)dkW_Q^{(i)} \in \mathbb{R}^{d \times (H/r) \cdot d_k},计算 Q(i)=XWQ(i)Q^{(i)} = X W_Q^{(i)}。每个 Attention 子计算结束后,rr 张 GPU 做一次 AllReduce 合并输出。

TP 的权衡:

  • TP 度越高 → 每张 GPU 显存占用越小,单请求计算越快(多 GPU 并行),但 AllReduce 通信开销越大。
  • TP 度越低 → 通信越少,单 GPU 吞吐更高,适合大批量高并发场景。

从 KV Cache 角度:TP rank ii 存储当前 pipeline stage 中 所有层的 KV head [iHkv/r,(i+1)Hkv/r)[i \cdot H_\text{kv}/r,\, (i+1) \cdot H_\text{kv}/r)

3. 流水线并行(Pipeline Parallelism,PP)

流水线并行把模型的 LL 层按顺序切分给 pp 个 stage,stage ss 负责的层为:

层范围(s)=[sL/p,  (s+1)L/p)(4)\text{层范围}(s) = \left[s \cdot \lfloor L/p \rfloor,\; (s+1) \cdot \lfloor L/p \rfloor\right) \tag{4}

推理时,请求依次经过 stage 0 → 1 → … → p1p-1。多个 micro-batch 可以在流水线中重叠执行(pipeline bubble 的权衡)。

PP 与 KV Cache 的关系:请求的 KV Cache 按层分散在不同 stage——stage ss 只存自己负责层的 KV block。当 PP 度变化时,层的 ownership 改变,对应的 KV block 需要从旧 stage 的 GPU 迁移到新 stage 的 GPU。

4. vLLM 与 PagedAttention

vLLM(Kwon et al., 2023)是当前最主流的开源 LLM 推理引擎。其核心创新 PagedAttention 把 KV Cache 管理成操作系统式的页面(固定大小的 block),彻底消除了内存碎片,允许动态分配。每个 block 存储若干连续 token 的 KV 状态。

ReMP 基于 vLLM v1 实现,深度改造了 vLLM 的 executor、worker 管理、KV cache manager 和通信初始化流程。

5. 为什么 TP/PP 拓扑是”静态”的?

当 vLLM 以 TP2PP4 启动时,以下组件都以这个配置为前提完成初始化:

  • 权重分片:每张 GPU 加载对应 (pp_rank, tp_rank) 的权重切片。
  • NCCL 通信组:TP AllReduce 组和 PP P2P peer 关系在启动时创建,绑定到特定进程。
  • KV Cache 块:按特定 layer-to-stage 和 head-to-TP-rank 映射分配,索引表写死在代码路径里。
  • Worker 进程:恰好 TP×PP = 8 个 worker,每个绑定到一个 (pp_rank, tp_rank)。

要切换拓扑,就必须把上述 全部 东西拆掉重建,也就是重启服务。重启一次 Llama2-70B 通常需要 2–10 分钟:checkpoint 从磁盘加载到显存、CUDA 上下文重建、NCCL 组协商……所有已有的 KV Cache 全部丢失,进行中的请求全部需要重新 prefill。

图 1:静态拓扑耦合 vs ReMP 解耦设计

graph TD
    subgraph 传统["传统 vLLM:所有状态绑定到拓扑"]
        W["权重分片<br/>(按 tp_rank/pp_rank 切分)"] --> T["TP/PP 拓扑<br/>(启动时固定)"]
        KV["KV Cache 布局<br/>(layer→stage, head→tp)"] --> T
        CC["NCCL 通信组<br/>(绑定到进程组)"] --> T
        WK["Worker 进程<br/>(绑定到 world_size)"] --> T
    end
    subgraph ReMP["ReMP:各状态独立解耦"]
        SWS["共享权重存储<br/>(CPU 共享内存,全量模型)"]
        MSS["MPU 状态空间<br/>(多拓扑快照预构建)"]
        KME["KV 迁移引擎<br/>(二维重映射 + P2P)"]
        WLM["Worker 生命周期管理<br/>(active/standby/wakeup)"]
        SA["调度器适配器<br/>(重绑定 block 表)"]
        RC["重配置控制器<br/>(事务协调入口)"]
        SWS --> RC
        MSS --> RC
        KME --> RC
        WLM --> RC
        SA --> RC
    end
    T -. "需要重启(分钟级)" .-> T
    RC -. "在线切换(秒级)" .-> RC

论文要解决的问题

ReMP 的核心观察是:真实生产环境中 LLM 推理流量不是恒定的,而是随时间周期性波动的。

论文展示了来自 INFINIGENCE AI 和之江实验室两个生产系统的流量曲线:白天高峰(08:00–20:00)流量比凌晨低谷高 3–5 倍。更关键的是,不同流量水平下最优的 TP/PP 组合是不同的

  • 低流量期(深夜凌晨):请求少,瓶颈是单请求延迟。高 TP 度更好——多 GPU 并行一个请求,降低 TTFT(首 token 时延)。
  • 高流量峰值(白天高峰):请求多,瓶颈是系统吞吐。高 PP 度更好——流水线并行处理更多请求,减少 AllReduce 通信开销,提升 tokens/秒。

图 2:不同流量下 TP/PP 最优策略的对比

graph LR
    subgraph 低流量["低流量期(凌晨)"]
        direction TB
        A1["高 TP 度(如 TP4PP2)"]
        A2["多 GPU 同时处理一个请求<br/>→ 每个请求更快"]
        A3["TTFT ↓,TPOT ↓<br/>延迟优先"]
        A1 --> A2 --> A3
    end
    subgraph 高流量["高流量期(白天)"]
        direction TB
        B1["高 PP 度(如 TP1PP8)"]
        B2["流水线处理更多并发请求<br/>→ 整体吞吐更高"]
        B3["Throughput ↑<br/>吞吐优先"]
        B1 --> B2 --> B3
    end
    低流量 -. "流量上升,触发切换" .-> 高流量
    高流量 -. "流量下降,触发切换" .-> 低流量
    style 低流量 fill:#e8f4e8
    style 高流量 fill:#fde8e8

由于重启代价极高,生产系统只能选一个拓扑忍受全天的次优性能。ReMP 要做的,就是让这次切换变得廉价,从而让系统可以动态追踪最优拓扑。

ReMP 的核心贡献

ReMP 通过三个耦合创新把 TP/PP 拓扑从”启动时静态参数”变成”运行时可调资源”:

  1. 状态解耦:把权重、KV Cache、通信组、Worker 进程从拓扑中解绑,让每个维度可以独立更新。
  2. 二维 KV Cache 迁移:沿 PP(层维度)和 TP(KV head 维度)同时做语义感知的 KV block 物理迁移。
  3. vLLM v1 端到端集成:深度改造 vLLM 的 executor、worker 管理、cache manager 和通信初始化,实现完整的在线切换路径。

系统设计详解

六大核心模块

ReMP 在 vLLM 基础上新增六个模块:

图 3:ReMP 架构全局图

graph TB
    RC["重配置控制器<br/>(事务入口,协调各模块)"]
    SWS["共享权重存储<br/>(CPU 共享内存,全量模型参数)"]
    MSS["MPU 状态空间<br/>(各候选拓扑的 NCCL 组快照)"]
    WLM["Worker 生命周期管理<br/>(active / standby / wakeup 三态)"]
    KME["KV 迁移引擎<br/>(计算迁移计划 + 执行 P2P 传输)"]
    SA["调度器适配器<br/>(重建 block 表 + 抢占无效请求)"]

    RC --> SWS
    RC --> MSS
    RC --> WLM
    RC --> KME
    RC --> SA

    SWS -. "CPU SharedStateDict<br/>→ 每个 Worker 读取目标分片" .-> WLM
    MSS -. "加载目标拓扑快照<br/>→ 更新 global parallel_state" .-> WLM
    KME -. "逐层批量 P2P 传输<br/>→ 传完即释放旧存储" .-> WLM
    SA -. "重建 block_manager<br/>→ 抢占失效请求" .-> RC

重配置控制器(Reconfiguration Controller):整个切换事务的入口。接收目标 (TP, PP) 配置,协调所有子模块,确保切换完成后模型分片、KV Cache、通信状态和调度器元数据在新拓扑下完全一致,然后恢复推理服务。

共享权重存储(Shared Weight Store):服务启动时,通过 MPClient → CheckPointManager → SharedStateDict 把完整模型参数加载到 CPU 共享内存(而不是每个 Worker 只加载自己的分片)。切换时,每个 Worker 直接从共享内存读取自己在新拓扑下的目标分片,不需要重新走磁盘 I/O 和 checkpoint 反序列化——这是切换快速的关键之一。

MPU 状态空间(MPU State Space):在服务启动时为所有候选拓扑(如 TP1PP8、TP2PP4、TP4PP2、TP8PP1)预构建 并行状态快照。每个快照包含 TP 通信组、PP peer rank 映射、rank-to-device 对应关系和 NCCL communicator handle。切换时,选择目标快照并应用到 vLLM 的全局 parallel_state——是 O(1)O(1) 操作,不需要重新协商 NCCL 组。

Worker 生命周期管理(Worker Lifecycle Manager):维护每个 Worker 的三种状态:

  • active:当前参与服务的 Worker
  • standby:暂时闲置、保留进程但不接受任务的 Worker
  • waking_up:正在同步消息队列 ring index 准备加入活跃集合的 Worker

当新拓扑所需 GPU 数少于旧拓扑时,多余 Worker 在 KV 迁移完成后进入 standby 而非被销毁;当需要更多 GPU 时,standby Worker 被唤醒加入而非重新初始化——避免了 CUDA context 重建的开销。

KV 迁移引擎(KV Migration Engine):计算每个 KV block 的新旧 GPU 归属映射,然后通过批量 GPU P2P 传输逐层执行迁移。传完一层立即释放旧存储,峰值额外内存开销仅为一层 KV Cache 大小,而不是全量模型 KV Cache 大小。

调度器适配器(Scheduler Adapter):切换完成后:

  1. 根据新拓扑重新生成 KV Cache 配置(block size、num_blocks、layer-stage 映射)
  2. 更新 block_manager(扩展或收缩每张 GPU 的 block pool)
  3. 抢占那些 KV block 已分布在”旧 stage”而新拓扑下该 stage 不再存在的请求(这些请求需重新 prefill)
  4. 重建流水线并行的 batch 队列

二维 KV Cache 迁移:核心算法

KV Cache 的物理位置由两个正交维度共同决定,切换拓扑时两个维度都可能同时变化:

  • PP 维度(层维度):layer \ell 属于 stage s=/(L/PP)s = \lfloor \ell / (L/PP) \rfloor,PP 度改变则 layer 的所属 stage(及对应 GPU)改变。
  • TP 维度(head 维度):KV head hh 属于 TP rank r=h/(Hkv/TP)r = \lfloor h / (H_\text{kv}/TP) \rfloor,TP 度改变则 head 的所属 TP rank(及对应 GPU)改变。

对某个请求在 layer \ell、KV head hh 的 KV block,拓扑 (TPold,PPold)(TPnew,PPnew)(TP_\text{old}, PP_\text{old}) \to (TP_\text{new}, PP_\text{new}) 下的迁移映射:

sold=L/PPold,rold=hHkv/TPold(5)s_\text{old} = \left\lfloor \frac{\ell}{\lfloor L/PP_\text{old} \rfloor} \right\rfloor, \quad r_\text{old} = \left\lfloor \frac{h}{\lfloor H_\text{kv}/TP_\text{old} \rfloor} \right\rfloor \tag{5} gold=soldTPold+rold(6)g_\text{old} = s_\text{old} \cdot TP_\text{old} + r_\text{old} \tag{6} snew=L/PPnew,rnew=hHkv/TPnew(7)s_\text{new} = \left\lfloor \frac{\ell}{\lfloor L/PP_\text{new} \rfloor} \right\rfloor, \quad r_\text{new} = \left\lfloor \frac{h}{\lfloor H_\text{kv}/TP_\text{new} \rfloor} \right\rfloor \tag{7} gnew=snewTPnew+rnew(8)g_\text{new} = s_\text{new} \cdot TP_\text{new} + r_\text{new} \tag{8}

goldg_\text{old} 把该 KV block 的张量切片用 P2P 传输发送到 gnewg_\text{new}

图 4:TP2PP2 → TP1PP4 拓扑切换下的二维 KV Cache 迁移示例

graph LR
    subgraph OLD [旧拓扑 TP2PP2]
        G0o[GPU0 Stage0 TP0]
        G1o[GPU1 Stage0 TP1]
        G2o[GPU2 Stage1 TP0]
        G3o[GPU3 Stage1 TP1]
    end
    subgraph NEW [新拓扑 TP1PP4]
        G0n[GPU0 Stage0]
        G1n[GPU1 Stage1]
        G2n[GPU2 Stage2]
        G3n[GPU3 Stage3]
    end
    G0o -->|P2P| G0n
    G1o -->|P2P| G0n
    G0o -->|P2P| G1n
    G1o -->|P2P| G1n
    G2o -->|P2P| G2n
    G3o -->|P2P| G2n
    G2o -->|P2P| G3n
    G3o -->|P2P| G3n

重配置事务:关键路径优化

每次拓扑切换都被封装成一个原子性事务,经历以下状态序列:

SERVING(T_old)
  → QUIESCING(冻结调度器,等待飞行中的 forward pass 完成)
  → PREPARING_WORKERS(调整 worker 集合)
  → MPU_APPLIED(加载目标拓扑快照)
  → [MIGRATE_KV ‖ RELOAD_SHARDS](并行执行)
  → REBINDING(调度器适配器重绑定)
  → SERVING(T_new)

关键路径优化是并行执行 KV 迁移和模型分片重加载。这两个操作触碰的是完全不同的内存区域(模型参数 vs KV Cache block),没有数据依赖,可以并发:

Tswitchseq=Tworker+Tmpu+Tkv+Tmodel+Tsched(9)T_\text{switch}^\text{seq} = T_\text{worker} + T_\text{mpu} + T_\text{kv} + T_\text{model} + T_\text{sched} \tag{9} Tswitchoverlap=Tworker+Tmpu+max(Tkv,Tmodel)+Tsched(10)T_\text{switch}^\text{overlap} = T_\text{worker} + T_\text{mpu} + \max(T_\text{kv}, T_\text{model}) + T_\text{sched} \tag{10}

节省量 =min(Tkv,Tmodel)= \min(T_\text{kv}, T_\text{model}),对大模型(TmodelT_\text{model} 主导)和 Cache 大的请求(TkvT_\text{kv} 主导)都有明显加速。

图 5:重配置事务状态机与关键路径并行

stateDiagram-v2
    [*] --> 服务中_旧拓扑 : 推理服务运行在 T_old
    服务中_旧拓扑 --> 冻结调度器 : 触发拓扑切换
    冻结调度器 --> 准备Worker集合 : 调度器 drain 完成
    准备Worker集合 --> MPU快照应用 : worker 集合更新完成
    MPU快照应用 --> 并行迁移 : 开始并行
    并行迁移 --> KV迁移完成 : KV migration done
    并行迁移 --> 分片重载完成 : shard reload done
    KV迁移完成 --> 重绑定 : 等待两者均完成
    分片重载完成 --> 重绑定 : 等待两者均完成
    重绑定 --> 服务中_新拓扑 : 调度器恢复
    服务中_新拓扑 --> [*]
    note right of 并行迁移
        T_kv 和 T_model 同时跑
        关键路径 = max(T_kv, T_model)
        节省 = min(T_kv, T_model)
    end note

三种 world size 变化情况的处理:

情况一 — GPU 总数不变TPold×PPold=TPnew×PPnewTP_\text{old} \times PP_\text{old} = TP_\text{new} \times PP_\text{new}):Worker 集合不变,只做 KV 迁移 + MPU 快照切换 + 分片重载。例如 TP2PP4 → TP4PP2,都用 8 张 GPU,只改切法。

情况二 — 新拓扑 GPU 更少:有些旧 Worker 持有新拓扑需要的 KV 切片,先迁移出来,再让这些 Worker 进入 standby(保留进程但不接任务)。

情况三 — 新拓扑 GPU 更多:唤醒 standby Worker,同步消息队列 ring index(确保它们能接收控制消息),然后加入活跃集合。

算法伪代码

算法 1:ReMP 重配置事务

输入:T_old = (TP_old, PP_old),T_new = (TP_new, PP_new)
      当前调度器状态 S,KV 元数据 M,SharedStateDict W
输出:运行在 T_new 下、KV 状态已迁移的推理服务

1.  通知调度器冻结:scheduler.frozen = True
2.  等待所有飞行中的 forward pass 完成(quiesce)
3.  计算 world_size_old = TP_old × PP_old
4.  计算 world_size_new = TP_new × PP_new
5.  如果 world_size_new < world_size_old:
      a. 确定哪些旧 Worker 持有新拓扑需要的 KV 切片
      b. 先对这些 Worker 执行 2D KV 迁移(算法 2)
      c. 将多余 Worker 设为 STANDBY
    否则如果 world_size_new > world_size_old:
      a. 唤醒 standby Worker,同步 ring index
      b. 将其加入 active worker 集合
6.  从 MPU State Space 加载目标拓扑快照,应用到 global parallel_state
7.  并行启动:
      线程 A:执行 2D KV Cache 迁移(算法 2)
      线程 B:从 SharedStateDict 重载目标模型分片到 GPU
8.  等待线程 A 和线程 B 均完成
9.  调用 Scheduler Adapter:
      a. 重新生成 KV 配置(block_size, num_blocks, layer-stage 映射)
      b. 更新 block_manager(扩展/收缩 block pool)
      c. 抢占 KV block 已失效的请求(重新 prefill)
      d. 重建流水线并行 batch 队列
10. 恢复调度器:scheduler.frozen = False
    → 开始在 T_new 下提供服务

算法 2:二维 KV Cache 迁移

输入:T_old, T_new,所有存活的 KV block 集合 B
输出:所有 block 在 T_new 下物理位于正确的 GPU

For 每一层 ℓ = 0 to L-1:
  For 集合 B 中每个涉及 layer ℓ 的 KV block b:
    1. 计算 s_old = ⌊ℓ / (L / PP_old)⌋
    2. For block b 覆盖的每个 KV head h:
         r_old = ⌊h / (H_kv / TP_old)⌋
         g_old = s_old × TP_old + r_old

         s_new = ⌊ℓ / (L / PP_new)⌋
         r_new = ⌊h / (H_kv / TP_new)⌋
         g_new = s_new × TP_new + r_new

    3. 将 (g_old → g_new, tensor_slice) 加入本层的 P2P 批次
  4. 对本层执行批量 P2P 传输
  5. 立即释放 g_old 上的 layer ℓ 存储
  (峰值额外内存 = O(一层 KV Cache 大小),不是全量)

实验结果

实验设置

  • 硬件:两个平台——8×NVIDIA H100 80GB 和 8×NVIDIA RTX 5090 32GB
  • 模型:Llama2-7B、Llama2-13B、Llama2-70B
  • 基线:基于重启的拓扑切换(冷重启)
  • 指标:切换时间 TswitchT_\text{switch}、TTFT(首 token 时延)、TPOT(每输出 token 时延)、吞吐量

切换时间对比

在 H100 上使用 Llama2-7B:大多数拓扑切换在 1–3 秒 内完成。对 Llama2-70B(80 层、140B 参数量 BF16 权重),切换时间最长也在 7 秒以内——仍远快于重启需要的数分钟。

与重启方案的加速比:

  • 7B–13B 模型各种拓扑对:10–30 倍加速
  • 7B 部分切换(如 TP1PP8 → TP4PP2 在 H100 上约 1.2 秒 vs 重启约 170 秒):超过 100 倍加速

图 6:动态流量下 ReMP 自动切换拓扑的时间线

graph LR
    LowLoad[低负载 00:00-08:00] -->|ReMP 切换| TP4PP2[TP4PP2 延迟优先]
    PeakLoad[高峰 08:00-20:00] -->|ReMP 切换| TP1PP8[TP1PP8 吞吐优先]
    OffPeak[低负载 20:00-24:00] -->|ReMP 切换| TP4PP2b[TP4PP2 延迟优先]
    Fixed[固定拓扑基线] -->|无法切换| Stuck[TP1PP8 全天固定]

动态负载下的服务性能

在模拟昼夜流量模式下(参考论文 Figure 1 的两个生产系统流量曲线),ReMP 动态选择最优拓扑:

  • 低负载时切到高 TP 度(如 TP4PP2),单请求 TTFT 显著降低
  • 高峰时切到高 PP 度(如 TP1PP8),吞吐量显著提升

与固定 TP1PP8 和 TP2PP4 基线相比,ReMP 在 TTFT、TPOT 和输出吞吐量上均有明显提升——效果大小取决于流量峰谷比。

局限性与边界条件

主机内存需求大:共享权重存储需要把完整模型参数存在 CPU 内存里。Llama2-70B BF16 约需 140 GB 主机 RAM,实际建议机器有 ≥1.5× 全量参数的主机 RAM(约 210 GB)。内存受限的边缘服务器或低端集群无法使用。

候选拓扑必须在启动时预配置:MPU 状态空间在启动时为固定的候选拓扑集合预构建 NCCL 通信组。运行时动态增加新拓扑需要重建这些组(慢操作)。适合已知 2–4 个最优候选的场景,不适合完全动态的拓扑搜索。

vLLM v1 深度依赖:ReMP 深度改造了 vLLM v1 的 executor 架构。移植到 SGLang、TensorRT-LLM 等其他引擎需要类似规模的重构工作。

调度器 quiescence 延迟:事务第一步需要等所有飞行中的请求完成(或至少完成当前 forward pass)。高负载下这个 drain 过程可能本身就需要数秒。

迁移中途失败没有 rollback 机制:P2P 传输中途失败会导致 KV Cache 状态不一致。论文没有讨论部分失败情况下如何回滚到 ToldT_\text{old}——在容错要求高的生产环境这是一个潜在隐患。

批判性分析:不足与可改进之处

论文的不足与缺陷

(a) 实验模型范围过窄。所有实验只用了 Llama2 系列(dense model)。MoE 模型(Mixtral、DeepSeek-V3、Qwen-MoE)有额外的 专家并行(EP) 维度——KV Cache 分布规则完全不同。论文声称支持”主流 LLM 推理”,但没有 MoE 实验,这个说法在当前 MoE 模型快速普及的背景下明显言过其实。

(b) 动态负载实验用的是合成流量,不是生产 trace。论文的动态负载实验用了一个简单的”阶梯式”流量模式(低→高→低)。真实生产流量有突发尖峰、多个拐点、分钟级噪声,可能需要比每小时一次更频繁的拓扑切换。论文没有评估快速、不规律流量变化下的系统行为。

(c) 没有消融实验量化关键路径并行的收益。论文给出了 TswitchoverlapT_\text{switch}^\text{overlap} vs TswitchseqT_\text{switch}^\text{seq} 的公式(式 9、10),但没有任何实验图表专门展示并行化带来的加速——我们不知道共享权重存储、MPU 快照预构建、KV 迁移并行化三个技术各自贡献了多少加速。

(d) 70B 模型 7 秒的停机时间依然有服务影响。对 TTFT SLO 严格的场景(如 P99 < 500 ms),即便 7 秒已经比重启快 100 倍,也是可感知的服务中断。论文没有说明这 7 秒内飞行中的请求如何处理——是被缓冲、被丢弃,还是被延迟?

(e) 调度器抢占的代价没有量化。切换后 Scheduler Adapter 会抢占那些 KV block 已失效的请求,这些请求要重做 prefill。论文没有报告典型切换下有多少请求被抢占、抢占对这些请求 TTFT 的影响。这是实际部署中用户感知到的质量损失,应该在论文里被量化。

作者淡化或回避的局限

(a) 专家并行维度完全缺席。整篇论文的”TP + PP”框架对 MoE 的 EP 维度视而不见。Mixtral、DeepSeek-V3 这类模型的 expert 分片是独立的第三维度,不能直接套用 ReMP 的 2D KV 迁移公式。

(b) 所有实验在单机 8 GPU 上做的,不涉及跨节点。真实部署常常跨越多个服务器节点,节点间通过 InfiniBand 连接(带宽远低于 NVLink)。跨节点 KV 迁移的带宽瓶颈会让 TkvT_\text{kv} 大幅增加,论文完全回避了这个场景。

(c) GPU 数量是固定的,不支持弹性伸缩。ReMP 只能在 固定的 GPU 池内 切换 TP/PP 配置,不能新增 GPU(scale-out)或归还 GPU(scale-in)。云原生弹性部署(按需增减 GPU)的需求无法通过 ReMP 满足。

可以改进的地方

(a) 扩展到 MoE + EP 三维迁移。2D KV 迁移(PP 层维度 + TP head 维度)可以扩展为 3D(再加 EP expert 维度)。鉴于 MoE 模型的普及,这是最高优先级的扩展方向。

(b) 引入预测式拓扑控制器。ReMP 是被动反应式的——流量变化后才触发切换。一个简单的时序预测器(哪怕只是移动平均)可以在流量峰值 到来前 就提前切换,消除 quiescence 阶段的服务降级时段。

(c) 量化并最小化抢占影响。应该有一张实验表格:每种拓扑切换下的请求抢占率、被抢占请求的 TTFT 膨胀量。这个数字对评估 ReMP 的真实部署代价至关重要。

(d) 跨节点 KV 迁移压缩。对于多机部署,可以在 KV block 传输时做轻量压缩(如 INT8 量化),以少量精度损失换取更快的跨节点迁移速度。

(e) 增加迁移失败的 rollback 机制。实现一个对迁移阶段的 checkpoint-and-rollback:若 P2P 传输中途失败,能够原子性地回退到 ToldT_\text{old},避免 KV Cache 状态污染。

可复现性说明

  • 代码:基于 vLLM v1 实现,论文截止日期时未附开源链接。
  • 硬件要求:至少 8 张支持 P2P CUDA 通信的 GPU(NVLink 或 PCIe P2P),主机 RAM ≥ 全量模型 BF16 参数量的 1.5 倍。
  • 论文未说明所用 NCCL 版本和 CUDA 版本(这两个对 P2P 传输带宽有实质影响)。

生产流量数据:ReMP 动机的量化依据

理解论文的出发点需要先看一下真实生产流量的规模。论文分析了两个真实系统:

INFINIGENCE AI 服务:20:00 的流量约是 04:00 的 3.8 倍。该服务运行一个需要 8 张 GPU 的模型。04:00 低谷期最优配置是 TP4PP2(高 TP 降低单请求延迟),20:00 高峰期最优配置是 TP1PP8(高 PP 提升吞吐量)。不能动态切换时,运维必须二选一:

  • 固定 TP4PP2:低谷期服务最优,但高峰吞吐比 TP1PP8 最优低约 40%。
  • 固定 TP1PP8:高峰吞吐最优,但低谷期 TTFT 比 TP4PP2 高约 3.1 倍。

之江实验室服务:峰谷比约为 4.2 倍,最优配置同样在高 TP(低谷)和高 PP(高峰)之间切换。

关键观察:流量模式在多天之间非常稳定——虽然单天有噪声,但多天平均的昼夜曲线是高度可预测的。这使得重配置问题变得可解:可以基于时刻(time-of-day)预测何时切换,而不需要实时流量预测。

共享权重存储的内存可行性分析

共享权重存储是 ReMP 最占主机内存的组件。不同模型的内存需求:

模型参数量BF16 大小建议主机 RAM(1.5×)
Llama2-7B7B~14 GB≥ 21 GB
Llama2-13B13B~26 GB≥ 39 GB
Llama2-70B70B~140 GB≥ 210 GB
Llama3-405B405B~810 GB≥ 1.2 TB

Llama3-405B 光共享权重存储就需要超过 1 TB 主机 RAM——很多 GPU 集群不具备这个条件。这是 ReMP 当前设计的实际可扩展性边界。

一个实际的缓解方案:在共享存储中使用量化权重(如 INT8),加载到 GPU 时再反量化。这会把存储需求降低 2×,代价是分片重载时增加一步反量化计算。

重配置触发策略:什么时候切换?

ReMP 论文主要关注”如何切换”(机制),但对”何时切换”(策略)没有深入展开。实践中触发重配置的策略至少有三种:

时刻触发(Time-based):利用流量的周期稳定性,在固定时刻触发切换(如每天 08:00 切到高 PP、20:00 切回高 TP)。实现最简单,但无法应对突发流量。

阈值触发(Threshold-based):监控当前 QPS 或 GPU 利用率,超过阈值时触发切换。能响应突发,但在阈值附近可能频繁抖动(“chattering”),每次切换都有 1–7 秒停机。

预测触发(Predictive):使用时间序列预测(移动平均、ARIMA 或简单 ML)提前 5–10 分钟预测流量变化,在峰值到来前完成切换。这是论文建议作为未来工作的方向,也是工程上最实用的方案。

论文的实验使用了简单的”知道流量会变化就提前切换”的理想策略,没有评估实际触发策略的延迟和抖动问题。

深入理解:KV Cache 迁移为什么难

值得专门花时间讲清楚 KV Cache 迁移的技术难度,因为这正是 ReMP 大部分创新所在。

语义一致性问题

KV Cache block 不是一块普通的字节数据——它编码了特定 token 在特定层、特定 attention head 的 key/value 向量。如果你把一个 block 从 GPU A 复制到 GPU B,而 GPU B 的 TP rank 或 PP stage 不同,那这个 block 的 head index 在新上下文里是错的。朴素复制会导致语义错误的 attention——模型会在错位的 head 切片上做内积,输出乱码。

ReMP 的二维映射(公式 5–8)确保每个 block 都到达正确持有 (stage, TP rank) 对的 GPU。这就是论文里说的”语义感知迁移”。

内存效率问题

朴素的迁移方案是:

  1. 为新拓扑在所有 GPU 上分配新的 KV 内存
  2. 把所有 block 从旧布局复制到新布局
  3. 释放旧内存

这样需要同时在 GPU 上保持旧旧两套 KV 布局——内存压力翻倍。满载服务器的 KV Cache 可能已经占用 40–50 GB 显存,这样做几乎必然 OOM。

ReMP 的逐层流式迁移解决了这个问题:对每一层 \ell,迁移完它的 block,立即释放旧 GPU 上这一层的存储,再继续下一层。峰值额外内存开销被限制在单层 KV 大小:

ΔMpeak=2HkvdkvTactivesizeof(dtype)(11)\Delta M_\text{peak} = 2 \cdot H_\text{kv} \cdot d_\text{kv} \cdot T_\text{active} \cdot \text{sizeof}(\text{dtype}) \tag{11}

其中 TactiveT_\text{active} 是所有活跃请求的 KV context 总长度。对 70B 模型(8 KV head,dkv=128d_\text{kv}=128)、10K tokens 的 context、BF16:

ΔMpeak=2×8×128×10000×240.96 MB/层(12)\Delta M_\text{peak} = 2 \times 8 \times 128 \times 10000 \times 2 \approx 40.96 \text{ MB/层} \tag{12}

相对于 GPU 总显存(80 GB)几乎可以忽略不计,逐层流式方案在满载服务器上是内存安全的。

顺序一致性问题

KV Cache block 是 PagedAttention 的页面,每个页面是一个 (block_size × d_model) 张量,关联到某个请求序列在特定层的位置。当 TP 变化时,沿 head 维度的页面边界也变了:TP2 下一个完整 head slice 在 TP4 下只有半个 head 的数据。

ReMP 在 block manager 层处理这个问题:Scheduler Adapter 在 KV 迁移完成后根据新 TP 配置重新生成 block table 的 head-slice offset。这就是为什么 Scheduler Adapter 必须在 KV 迁移引擎完成后运行——block table 更新和物理数据迁移是同一个事务中的串行步骤。

性能分析:ReMP 在哪些场景收益最大?

ReMP 相比重启方案的加速来自三个来源:

来源一:消除 checkpoint 磁盘 I/O

重启需要从磁盘(NVMe 或分布式存储)加载 checkpoint。即使最快的 NVMe 顺序读也只有约 7 GB/s。Llama2-70B BF16 推理权重约 140 GB,光 checkpoint 加载就需要 20 秒以上,网络存储上更长。

ReMP 的共享权重存储把瓶颈变成 CPU→GPU PCIe 带宽(约 32 GB/s,PCIe 4.0 ×16)。同时,每个 Worker 只需加载自己的分片 W/(TP×PP)|W|/(TP \times PP)

TdiskWBdisk,TsharedW/(TP×PP)BPCIe(13)T_\text{disk} \approx \frac{|W|}{B_\text{disk}}, \qquad T_\text{shared} \approx \frac{|W| / (TP \times PP)}{B_\text{PCIe}} \tag{13}

即使不考虑其他重启开销,分片重载阶段就比从磁盘重载快 4×TP×PP\approx 4 \times TP \times PP 倍(PCIe 是磁盘带宽的 ~4 倍,且只加载 1/(TP×PP)1/(TP \times PP) 的数据)。

来源二:消除 NCCL 组重建

NCCL 通信组创建(ncclCommInitAll)涉及跨所有参与进程的广播式握手。8 GPU 配置下通常需要 3–15 秒,取决于 GPU 互联拓扑和 NCCL 版本。MPU 状态空间的预构建快照把激活过程变成 O(1)O(1) 指针切换。

来源三:保留 KV Cache

不迁移的话,重启会让所有飞行中的请求重做 prefill 计算。一个 32K 上下文的长请求在 H100 上 prefill 需要 1–5 秒。如果有 50 个这样的请求在飞,重计算债务是巨大的。

ReMP 就地迁移 KV block,飞行中的请求可以从中断处继续。唯一的例外是 “preempted” 请求(其 KV block 所在 Worker 在 world size 变化中消失),但这在大多数切换中只是少数。

TP 与 PP 权衡的定量分析

为了理解动态拓扑切换为什么重要,我们来建立一个简单的 TP/PP 吞吐量权衡模型。

在 TP 度为 rr 时,每层 Transformer 的通信开销是一次大小为 2d2d 的 AllReduce(column-then-row 并行)。批大小 BB、序列长度 TT 下,有效吞吐量近似为:

ThroughputTPBTTcompute(r)+Tallreduce(r)(14)\text{Throughput}_\text{TP} \approx \frac{B \cdot T}{T_\text{compute}(r) + T_\text{allreduce}(r)} \tag{14}

其中 Tcompute(r)1/rT_\text{compute}(r) \propto 1/r(TP 线性加速),Tallreduce(r)d(r1)/r/BNVLinkT_\text{allreduce}(r) \propto d(r-1)/r / B_\text{NVLink}(AllReduce 代价随 rr 增大)。

  • BB(低流量)TcomputeT_\text{compute} 主导,增大 rr 有益(更快的单请求延迟)。
  • BB(高流量)TallreduceT_\text{allreduce} 显著,增大 rr 反而有害。

在 PP 度为 pp 时,流水线 bubble 比例为:

bubble fraction=p1p+m/p1(15)\text{bubble fraction} = \frac{p-1}{p + \lceil m/p \rceil - 1} \tag{15}

其中 mm 是 micro-batch 数量。

  • 高并发(mm 大):bubble 可忽略,PP 主要起显存分区作用,有益。
  • 低并发(mm 小):bubble 按 1/m1/m 比例浪费,PP 效果差。

公式 14 和 15 共同解释了:低流量时用高 TP、高 PP 亏;高流量时用高 PP、高 TP 亏。这就是 ReMP 动态切换的量化依据。

核心设计决策的深层解析

ReMP 的每个主要设计选择背后都有具体的工程权衡。以下逐一分析。

为什么把完整模型存在 CPU 内存而不是磁盘?

朴素重启方案的瓶颈之一是从磁盘加载 checkpoint。即使 NVMe 顺序读约 7 GB/s,70B BF16 权重也需要 20 秒。CPU 共享内存的 PCIe 带宽(约 32 GB/s)是 NVMe 的 4–5 倍,且不需要文件系统操作。

更重要的是,CPU 共享内存跨进程可见:所有 Worker 进程(同一机器上)可以直接读取同一份内存,没有额外的内存拷贝。每个 Worker 只需要读出自己的分片 W/(TP×PP)|W|/(TP \times PP),并行加载时整体带宽进一步提升。

代价:主机 RAM 需求大(70B 约 140 GB)。这是 ReMP 最大的部署门槛。

为什么要预构建所有候选拓扑的 NCCL 通信组?

NCCL 通信组初始化(ncclCommInitAll)是一个阻塞操作,需要所有参与进程同步,通常需要 3–15 秒。如果每次切换都重新建立通信组,切换时间就会被这个步骤主导。

预构建方案在启动时一次性支付这个代价,此后切换只是指针切换(O(1)O(1))。

代价:内存中需要保留多份 NCCL communicator 对象(每个候选拓扑一份)。对 4 个候选拓扑,每份约几 MB,总开销可以忽略。

限制:候选拓扑集合在启动时确定。想在运行时添加新的候选拓扑,需要重新做 ncclCommInitAll——慢操作,绕过了预构建的价值。

为什么让 Worker 在不需要时保持 standby 而不是退出?

Worker 进程退出后,重新启动的代价包括:

  1. CUDA context 初始化:30–60 秒(GPU memory 分配 + driver 握手)
  2. NCCL group 重建:3–15 秒
  3. 进程 fork + Python interpreter 初始化:5–10 秒

合计可能超过 60 秒——比 ReMP 的整个切换时间大一个数量级。

保持 standby 的代价是 GPU 显存持续被占用(standby Worker 的模型分片和 KV buffer)。对有多个候选拓扑的场景,这意味着机器上最大那个 world_size 的 GPU 数都要始终保持进程存活。

为什么按层流式迁移 KV Cache 而不是一次性迁移?

一次性迁移需要同时在 GPU 上保持旧旧两套完整 KV 布局——对满载服务器几乎必然 OOM(KV Cache 本身就可能占 40–50 GB,两套就是 80–100 GB)。

按层流式:传完一层,立即释放旧层,峰值额外内存只有一层(见公式 11–12,约 40 MB/层),内存安全性有保障。额外代价是层间有微小串行化(等待每层传输完成后再释放和推进到下一层),但对 P2P 传输主导的时间来说这个开销很小。

与相关工作的对比

系统动态拓扑切换KV Cache 保留TP 调整PP 调整
vLLM(基线)❌(重启)
Llumnix请求迁移(同拓扑)✅(拓扑内)
SpotServe抢占式弹性部分
PipeLive流水线重配置部分✅(仅 PP)
ReMP✅ TP+PP 联合✅ 二维迁移

核心差异:ReMP 是第一个同时对 TP 和 PP 两个维度做在线重配置并保留 KV Cache 的系统。PipeLive 只处理 PP 变化,Llumnix 在同一拓扑内迁移请求但不改变拓扑本身。

实验设计的深入解读

切换时间 Breakdown

论文报告的 1–7 秒切换时间对应以下时间分布(以 Llama2-7B TP2PP4 → TP4PP2 在 H100 为例估计):

  • TworkerT_\text{worker}(Worker 状态更新):~0.1–0.3 秒(world size 不变则最短)
  • TmpuT_\text{mpu}(MPU 快照切换):~0.05 秒(预构建后是 O(1)O(1) 操作)
  • max(Tkv,Tmodel)\max(T_\text{kv}, T_\text{model})(并行执行瓶颈):对 7B 约 0.5–1.5 秒(NVLink 带宽 ~600 GB/s,7B BF16 每 GPU 分片约 4 GB,PCIe 32 GB/s 下 0.125 秒/GPU)
  • TschedT_\text{sched}(调度器重绑定):~0.1–0.2 秒

对 70B 模型,TmodelT_\text{model}(每 GPU 分片约 17 GB,PCIe 带宽下约 0.5 秒)和 TkvT_\text{kv}(满载时约 1–3 秒)都更大,合计 3–7 秒。

为什么有些切换超过 100 倍加速

重启 7B 模型的时间构成(H100 NVMe):

  • 从磁盘加载 14 GB checkpoint:~2 秒(NVMe 7 GB/s)
  • CUDA context 初始化:~30–60 秒(驱动初始化 + 显存分配)
  • NCCL 组建立:~5–10 秒
  • KV Cache 重新分配 + 热身:~5 秒

合计:~50–90 秒

ReMP 同等切换:~1–2 秒

因此 50–90 倍的加速是合理的,特定场景(CUDA 初始化慢的机器)甚至超过 100 倍。

ReMP 对 vLLM 的改造范围

ReMP 需要改动 vLLM v1 的四个核心子系统,理解改造范围有助于评估可移植性:

Executor(执行器):vLLM 的 executor 是调度推理步骤的中央控制面。ReMP 在 executor 主循环里插入 ReconfigurationController,负责冻结调度器和管理事务生命周期。这需要访问 vLLM 的内部 executor API(非公开接口),因此实现是版本相关的。

Worker 管理:vLLM v1 通过 Ray 分布式 executor 管理 worker 进程。ReMP 扩展了 worker 状态机,增加 standby/wakeup 状态,需要修改 vLLM 的 worker spawn 和 process group 管理代码。

KV Cache 管理器:vLLM 的 BlockSpaceManager 追踪哪些 KV block 属于哪些序列。拓扑切换后 block 地址变了(不同 GPU、不同 offset),ReMP 扩展了 block manager,支持”重映射事务”——原子性地更新所有 block-to-sequence 关联。

通信初始化:vLLM 在启动时通过 initialize_model_parallel() 初始化一次 NCCL 组。ReMP 把这一步替换为多拓扑初始化,在启动时就构建所有候选拓扑的通信组并存入 MPU 状态空间。

对 SGLang 或 TensorRT-LLM 的移植,需要对这两个引擎的类似子系统做同等规模的改造。设计思路是通用的,但实现与 vLLM v1 内部架构深度耦合。

可复现性说明

  • 代码:基于 vLLM v1 实现,论文截止时未附开源链接。
  • 硬件:至少 8 张支持 P2P CUDA 通信的 GPU(NVLink 推荐,PCIe P2P 也支持但迁移慢)。
  • 主机内存:≥ 全量模型 BF16 参数量的 1.5 倍(70B 约需 210 GB 主机 RAM)。
  • NCCL 和 CUDA 版本未指定,这两个对 P2P 传输带宽有实质影响,复现时应留意。
  • 动态负载实验需要流量模拟器,按论文 Figure 1 的昼夜流量模式校准。

总结

ReMP 解决了 LLM 推理服务领域一个长期存在但鲜有人正面攻克的问题:TP/PP 拓扑一旦部署就无法调整。通过把权重、KV Cache、通信组、Worker 进程从拓扑中解耦,加上二维 KV Cache 在线迁移和关键路径重叠优化,ReMP 把拓扑切换从”不可承受的重启”变成”可接受的秒级操作”。

逐层流式迁移的内存安全性、O(1)O(1) 拓扑快照切换、TkvT_\text{kv}TmodelT_\text{model} 的并行化——这三个工程选择单独看都显平凡,但组合起来让在线重配置从不可能变成了实际可用。

主要的开放问题——MoE 专家并行支持、跨节点迁移、弹性 GPU 伸缩——是清晰的下一步,论文打下了坚实基础。

对实践者而言:ReMP 对流量峰谷比 ≥3× 的高流量生产部署最有价值,且模型规模要大到足以让不同 TP/PP 配置产生可测量的性能差异(通常 ≥13B 参数量)。轻负载或小模型场景下,单一高 TP 配置往往已经足够好,ReMP 的增益有限。

核心公式汇总

以下是本笔记中涉及的主要数学关系,便于复习参考:

KV Cache 内存占用(公式 1):

MKV=2LHkvdkvTBsizeof(dtype)M_\text{KV} = 2 \cdot L \cdot H_\text{kv} \cdot d_\text{kv} \cdot T \cdot B \cdot \text{sizeof}(\text{dtype})

TP 权重分片 — 按 head 维度切分 Query 投影(公式 3):

WQ=[WQ(0)WQ(1)WQ(r1)],WQ(i)Rd×(H/r)dkW_Q = [W_Q^{(0)} \mid W_Q^{(1)} \mid \cdots \mid W_Q^{(r-1)}], \quad W_Q^{(i)} \in \mathbb{R}^{d \times (H/r) \cdot d_k}

PP 层归属 — stage ss 负责的层(公式 4):

层范围(s)=[sL/p,  (s+1)L/p)\text{层范围}(s) = \left[s \cdot \lfloor L/p \rfloor,\; (s+1) \cdot \lfloor L/p \rfloor\right)

KV block 在旧拓扑下的源 GPU(公式 5–6):

sold=L/PPold,rold=hHkv/TPold,gold=soldTPold+rolds_\text{old} = \left\lfloor \frac{\ell}{\lfloor L/PP_\text{old} \rfloor} \right\rfloor, \quad r_\text{old} = \left\lfloor \frac{h}{\lfloor H_\text{kv}/TP_\text{old} \rfloor} \right\rfloor, \quad g_\text{old} = s_\text{old} \cdot TP_\text{old} + r_\text{old}

并行 vs 串行切换时间(公式 9–10):

Tswitchseq=Tworker+Tmpu+Tkv+Tmodel+TschedT_\text{switch}^\text{seq} = T_\text{worker} + T_\text{mpu} + T_\text{kv} + T_\text{model} + T_\text{sched} Tswitchoverlap=Tworker+Tmpu+max(Tkv,Tmodel)+TschedT_\text{switch}^\text{overlap} = T_\text{worker} + T_\text{mpu} + \max(T_\text{kv}, T_\text{model}) + T_\text{sched}

流水线 bubble 比例(公式 15):

bubble fraction=p1p+m/p1\text{bubble fraction} = \frac{p-1}{p + \lceil m/p \rceil - 1}

这些公式共同描述了驱动 ReMP 设计的内存-计算-通信权衡关系。