Updating 1T Parameters in Seconds — P2P Weight Transfer in Large Scale Distributed RL

发表时间: 2026-04 · Blog post by Jiadong Guo, Xin Ji, Letian Ruan, Teng Ma, Chenyang Zhao, Yueming Yuan, Zhichen Zeng (lmsys.org)

原文: https://lmsys.org/blog/2026-04-29-p2p-update

作者/机构:Jiadong Guo, Xin Ji, Letian Ruan, Teng Ma, Chenyang Zhao, Yueming Yuan, Zhichen Zeng (April 29, 2026)

速读

一句话结论 本文针对大规模分布式强化学习中的权重同步卡点,提出了一种基于 RDMA 的 P2P 权重更新机制,在 SGLang 中将 1T 参数模型(Kimi-K2)的权重传输时间从 53 秒缩短至 7.2 秒,实现了 7 倍以上的加速。

要解决什么问题 在大规模分布式强化学习训练中,从训练节点到推理引擎的权重传输是一个阻塞全局的关键路径,传输期间训练和推理都会停滞,导致计算资源闲置。现有的开源方案(如基于 PyTorch FSDP 或 Megatron-LM)默认依赖 NCCL 的 broadcast 原语来进行权重同步,这种做法存在三个致命卡点。首先是冗余与低效,NCCL 广播通常由单一源节点发起,导致大量相同数据在网络中被重复发送,且此时绝大多数训练节点处于闲置状态,未能利用其带宽。其次是同步阻塞,NCCL 采用全局同步的锁步(lock-step)机制,任何一个接收端的慢启动都会导致整个通信组挂起。最后是缺乏弹性,NCCL 通信组一旦建立就固定了,难以动态适应新创建的推理引擎实例。随着模型参数量向万亿级别扩展,跨多机多机架的单点广播会迅速耗尽网络带宽,成为制约强化学习训练吞吐量的绝对瓶颈。

怎么做的 核心思路是摒弃 NCCL 的中心化全局广播,转而利用 RDMA 技术实现训练端到推理端的点对点(P2P)分片传输。RDMA 允许机器绕过内核网络栈和远程 CPU 直接访问内存,实现了零拷贝和单边操作,从而打破了全局同步的限制。该方法由三个关键设计构成。第一是源端 CPU 引擎副本,为了不占用宝贵的 GPU 显存且避免频繁的内存注册开销,系统在训练节点的 CPU 内存中创建推理引擎的模型副本。第二是 P2P 映射调度,系统会构建一个从训练节点到推理节点的轮询负载均衡映射表,不再由单一节点广播全量权重,而是让所有训练节点都参与进来,各自将特定的权重分片直接发送给对应的目标节点。第三是基于 Mooncake TransferEngine 的零拷贝传输,在启动时完成一次内存注册,后续传输直接在网络与内存间进行,避开了昂贵的 CUDA IPC 序列化和内核拷贝。在具体执行时,权重更新采用分桶模式,训练端首先像往常一样进行 All-Gather 收集当前桶的权重,然后将其加载到本地 CPU 的 SGLang 引擎副本中,最后通过 RDMA 发送给目标推理节点。假设每个推理节点需要的参数量为 $P$,分桶 All-Gather 的基础缓存大小为 $K$。传统 NCCL 广播需要在源端分配巨大的内存并在目标端分配 $K$ 的缓存;而本方法通过在源端 CPU 额外分配 $K^* + P$ 的内存($K^*$ 为处理多对一張量映射而略微扩大的缓存),换取了在目标接收端 $0$ 额外内存分配,用源端的内存空间置换了极致的网络利用率。

效果如何 实验在配备 Infiniband 网络的 H100 8 卡 GPU 集群上进行,测试了从 9B 到 1T 参数规模的多种开源模型。对比基线包含三种路线:代表传统落盘方案的 Disk I/O 策略(耗时达数分钟)、代表当前主流对称训练标准的 NCCL 广播方案(update_weights_from_distributed),以及代表极致传输效率但缺乏开源生态兼容性的 Perplexity fabric-lib P2P 方案(仅支持 FSDP2 DTensor 且系统极度复杂)。量化结果显示,在 32 个节点上对 1T 参数的 Kimi-K2(采用 FP8 64-block 量化)进行权重同步时,NCCL 广播耗时 53.2 秒,而本文的 RDMA P2P 方法仅需 7.2 秒,实现了 7.37 倍的加速;在 16 个节点上同步 744B 的 DeepSeek-V3p2 模型时,耗时从 58.3 秒降至 8.4 秒,加速比达 6.88 倍。该方法在具有高专家并行度的大型 MoE 架构和多节点扩展时收益最明显。但作者也指出了该方案的代价与局限性:每个训练节点需要在 CPU 内存中额外常驻一个推理引擎副本(约占用 32GB);在单节点且专家并行度较小的场景下(如 30B 的 GLM4-MoE),将张量加载到本地 CPU 的开销会超过 P2P 传输的收益,导致速度反而不如 NCCL(0.59 倍);此外,虽然传输效率大幅提升,但绝对速度仍不及 Perplexity 的纯底层方案(约 1.2 秒),且部分依赖硬件的后处理逻辑(如 DeepSeekV3 的 MLA 局部张量计算)仍必须在权重传输完成后交由 GPU 独立执行。

主要贡献

在大型分布式强化学习(RL)工作负载中,从训练器到推理引擎的权重传输是一个关键的性能瓶颈,现有的基于NCCL的广播(broadcast)方法会导致严重的网络冗余并迫使推理服务器长时间处于闲置状态。为了解决这一问题,本文为SGLang引入了一种基于RDMA的点对点(Peer to Peer, P2P)权重更新机制,作为传统NCCL广播方法的补充,且兼容所有主流的开源模型。

该方案的核心创新点在于:

  1. 源端CPU引擎副本(Source-side CPU engine replica):在训练节点的CPU内存上构建模型副本,以极小的内存代价换取传输效率。
  2. P2P RDMA传输:通过Mooncake TransferEngine实现底层的高效零拷贝传输。

定量结果表明,对于拥有1T参数的Kimi-K2模型,该优化将权重传输时间缩短了7倍(从53秒降至7.2秒),其代价仅仅是每个训练rank在CPU内存上增加一个额外的推理引擎副本开销(32G)。这些优化大幅最小化了网络冗余,并使推理服务器能够以显著更快的速度恢复数据收集(rollout)工作。

背景知识与关键挑战

NCCL通信后端的局限性。NVIDIA的NCCL通过自动检测硬件拓扑并通过环形或树形算法协调数据流,优化了all-gather和broadcast等原语。作为PyTorch FSDP、DeepSpeed和Megatron-LM的默认通信后端,它是对称训练的行业标准。然而,它依赖于集合语义,要求每个rank同时调用相同的操作并匹配数据形状。虽然对于负载均衡的工作流非常高效,但这种设计在动态环境中成为一个负担:NCCL以锁步(lock-step)方式运行,这意味着来自单个接收者的“慢启动”可能会导致整个通信组挂起并使资源闲置。

RDMA的核心优势。RDMA(远程直接内存访问)允许机器访问远程内存,同时完全绕过远程CPU和内核网络栈。其高效性源于三个核心特征:

  • Kernel Bypass(内核旁路):应用程序直接向网卡(NIC)提交工作请求,消除了昂贵的系统调用和上下文切换。
  • Zero Copy(零拷贝):数据直接在已注册的内存区域(Memory Regions)和网络之间通过DMA移动,避免了内核缓冲区中的中间拷贝。
  • One-Sided Operations(单边操作):RDMA READ/WRITE操作由一端发起,不需要远程端的CPU主动参与或处理中断。

RDMA作为高速权重传输基础。与NCCL的全局同步不同,RDMA允许任何两个端点独立并发地进行通信,使其成为高速权重传输的理想基础。这正是本文描述的P2P权重更新机制利用基于RDMA的传输(通过Mooncake TransferEngine)作为其底层基础的原因。

分布式RL中的权重传输瓶颈。在大型分布式RL训练中,从训练器到推理引擎的权重传输是关键路径操作:在权重传输期间,整个RL训练会陷入停滞——训练器和推理端都无法取得进展,资源通常处于闲置状态。随着模型规模的增长,这种传输必须跨越多个主机和机架进行扩展,所有节点都在争夺有限的带宽。在miles/slime/verl等开源解决方案上,现有的基于NCCL的工作流依赖于来自单一源rank的broadcast原语,这在传输过程中迅速成为瓶颈。

图1:左侧:分布式训练/推理RL期间miles中当前的权重传输工作流。在源端,所有节点参与TP和EP维度的all gather,导致每个PP rank在head rank处生成一个聚集的张量。head rank参与分布式更新组,通过update_weight_from_distributed API将完整权重广播给每个引擎rank,本地rank在此加载其对应的分片。此过程针对每个PP rank和每个分桶权重张量运行。右侧:P2P更新设计依赖于源端引擎副本作为中介。分桶权重更新的第一个all gather步骤与miles相同。但随后,权重被加载到CPU内存中sglang引擎分片的本地副本中,该副本将正确形状的权重发送到sglang。每个副本的权重可以发送到多个sglang rank。每个目标sglang TP rank都需要接收来自每个pp源的数据。
图1:左侧:分布式训练/推理RL期间miles中当前的权重传输工作流。在源端,所有节点参与TP和EP维度的all gather,导致每个PP rank在head rank处生成一个聚集的张量。head rank参与分布式更新组,通过update_weight_from_distributed API将完整权重广播给每个引擎rank,本地rank在此加载其对应的分片。此过程针对每个PP rank和每个分桶权重张量运行。右侧:P2P更新设计依赖于源端引擎副本作为中介。分桶权重更新的第一个all gather步骤与miles相同。但随后,权重被加载到CPU内存中sglang引擎分片的本地副本中,该副本将正确形状的权重发送到sglang。每个副本的权重可以发送到多个sglang rank。每个目标sglang TP rank都需要接收来自每个pp源的数据。

现有NCCL广播方案的挑战。现有的NCCL广播解决方案面临以下挑战:

  • 冗余(Redundancy):相同的数据在网络中被多次发送。
  • 不活跃(Inactivity):大多数训练器rank在传输期间保持空闲,而只有少数rank参与广播。
  • 僵化(Rigidity):一旦定义,NCCL通信组就是固定的,因此随着新创建的引擎实例进行动态扩展是一项复杂的更改。

不同传输策略的性能对比评估。该对比评估了传输1T FP8 Kimi K2模型(约1TB)的性能。注意:排除了update_weights_from_tensor接口,因为它仅支持同机部署(colocated)场景。

策略 效率 开源支持 动态性 训练端支持 系统复杂度 架构灵活性
磁盘I/O策略 (update_weights_from_disk) 🆘 ~几分钟 ✅ 是 ✅ 是 ✅ Megatron FSDP 😊 简单 😊 单一API
NCCL广播 (update_weights_from_distributed) 🥵 ~50秒 ✅ 是 🚫 否 (需重建NCCL组) ✅ Megatron FSDP 😊 简单 😊 单一API
Perplexity https://arxiv.org/abs/2510.27656 P2P ⚡ ~1.2秒 🚫 否 (仅RDMA库) ✅ 是 ❓ 仅FSDP2 DTensor 🥵 非常复杂 🥵 仅写入
RDMA P2P (本文实现) 😊 ~7秒 ✅ 是 ✅ 是 ✅ Megatron FSDP 😥 复杂 😀 多个API

本方案的架构灵活性与性能权衡。虽然与Perplexity的方法相比在传输效率上存在权衡,但我们的解决方案与现有的SGLang接口相比提供了显著的性能提升。此外,通过将这些功能封装到新的API接口中,我们实现了高度的架构灵活性。参考miles的运行说明及支持的模型完整列表。

方法细节

从集中式广播到分布式P2P映射的设计转变。我们的设计从集中式广播转向通过RDMA(远程直接内存访问)进行的分布式P2P映射;同时保持与所有现有开源模型和任何并行配置的兼容性,并复用现有接口。具体包含以下核心设计:

  • 源端引擎副本(Source-Side Engine Replicas):我们在训练rank的CPU内存中创建模型副本。这避免了在没有重复注册和注销的情况下浪费GPU VRAM。
  • P2P映射启发式算法(P2P Mapping Heuristics):我们在训练器rank和推理rank之间实现了点对点映射。不再由少数几个rank广播所有内容,而是每个训练器rank都参与其中,将其特定的分片直接发送到目标。
  • 零拷贝传输(Zero-Copy Transfer):使用TransferEngine,内存只在启动时注册一次,绕过了昂贵的CUDA IPC句柄序列化和内核端拷贝。

底层基础设施与接口依赖。该实现严重依赖于现有的基础设施和接口:

  • TransferEngine 作为底层传输层,以实现网络上CPU和GPU之间的RDMA零拷贝传输。
  • 通过 Rfork 复用权重注册信息,这是通过SGLang API暴露的新的远程实例权重加载机制。
  • 标准的SGLang API load_weight(huggingface_tensor),支持所有的量化和分片配置。

SGLang侧新增接口需求。SGLang侧需要几个新的接口:

  • 暴露用于创建副本的模型并行度。
  • 将Hugging Face张量与其对应的SGLang张量分片进行映射。
  • 一个后处理权重引擎调用,用于GPU本地处理(如后量化)。

权重更新初始化阶段的调用流程。这些接口已合并到以miles为目标的sglang-miles分支中。在权重更新期间,调用方在初始化阶段的操作如下:

步骤 描述
get_remote_instance_transfer_engine_info 调用SGLang API获取权重注册信息
get_parallelism_info 调用SGLang API获取并行度定义信息(tp, ep等)
build_transfer_plan 构建训练 -> 推理rank映射关系
create_engine_replica 创建CPU引擎副本

每次更新期间的调用流程。在每次更新期间,执行以下步骤:

步骤 描述
pause_and_register_engine 调用SGLang API暂停引擎,并注册副本权重(仅一次)
update_weight (非专家和专家) 分桶权重更新,先非专家权重后专家权重
post_process_weights 调用SGLang API对加载的权重进行后处理,如量化
update_weight_version 调用SGLang API更新权重版本
continue_generation 调用SGLang API恢复操作

通用权重更新设计的内存与网络权衡。其结果是一个通用的权重更新设计,能够处理任何模型和所有常见的量化逻辑,同时实现无冗余的快速RDMA零拷贝传输,以及更高的带宽利用率。假设有 $M$ 个用于训练的源rank,以及 $N$ 个用于SGLang推理的目标rank;源rank的pp_size为 $pp$,目标rank的ep_size为 $ep$;每个引擎rank有 $P$ 个参数。我们还分配 $K$ 作为分桶all gather的内存缓冲区。如果我们假设模型仅包含专家权重:

参与的源Rank数量 每个推理Rank接收的参数量 源端分配的额外缓冲区 目标端分配的额外缓冲区
NCCL Broadcast $pp$ $ep \times P$ $K$ $K$
RDMA P2P $M$ $P$ $K^* + P$ $0$

上表说明了RDMA P2P设计如何通过权衡内存分配来实现更少的网络传输和更高的利用率。所有源rank都参与传输,而NCCL仅每个流水线并行组的head rank参与;只有必要的张量被发送过网络,而NCCL需要将完整的all-gathered张量发送到每个rank。RDMA P2P通过在源端CPU上额外分配 $P$ 的内存来进行权衡,同时接收端不再需要任何内存分配。我们注意到 $K^*$ 在实践中略大于 $K$,这是因为在更新完整的SGLang张量之前,某些Hugging Face张量需要在本地缓存,因为通常存在多对一张量映射(在sglang中,q_proj, k_proj, v_proj → qkv_proj)。

实验结果

作者在配备InfiniBand连接的H100 8卡GPU主机上对常见的开源模型进行了传输速度的性能分析(Profile)。记录的时间为从引擎暂停调用返回到发出continue_generation调用之间的时间。

实验数据表:

模型系列 模型名称 总参数量 训练配置 推理配置 NCCL (ms) RDMA (ms) 加速比
GLM4 GLM-Z1-9B-0414 9B TP=2, PP=1, CP=2, EP=1, ETP=1, 1节点 TP=4, EP=1, 1节点 694.6 707.1 0.98x
DeepSeek-V2 ★ Moonlight-16B-A3B 16B(3B) TP=2, PP=1, CP=1, EP=8, ETP=1, 1节点 TP=8, EP=8, 1节点 1,482.0 1,073.3 1.38x
GLM4-MoE ★ GLM-4.7-9B-Flash 30B(3B) TP=4, PP=1, CP=1, EP=8, ETP=1, 1节点 TP=4, EP=4, 1节点 2,508.6 4,229.0 0.59x
Qwen3-MoE ★ Qwen3-30B-A3B 30B(3B) TP=4, PP=1, CP=1, EP=8, ETP=1, 2节点 TP=8, EP=8, 2节点 2,670.0 2,160.2 1.24x
GLM4-MoE ★ GLM-4.5-Air 106B(12B) TP=1, PP=4, CP=1, EP=8, ETP=1, 4节点 TP=8, EP=8, 4节点 5,001.1 2,637.2 1.90x
Qwen3-MoE ★ Qwen3-235B-A22B 235B(22B) TP=4, PP=4, CP=2, EP=16, ETP=1, 8节点 TP=32, EP=32, 8节点 10,753.6 3,162.0 3.40x
DeepSeek-V3p2 ★ GLM-5 744B(40B) TP=4, PP=8, CP=2, EP=16, ETP=1, 16节点 TP=64, EP=64, 16节点 58,301.5 8,479.7 6.88x
DeepSeek-V3 ★ Kimi-K2-fp8 (64块量化) 1T(64B) TP=8, PP=8, CP=4, EP=32, ETP=1, 32节点 TP=32, EP=32, 32节点 53,279.1 7,227.3 7.37x

注:针对Kimi-K2的特殊处理:调整了Kimi K2以在fp8中使用[64, 64]块量化大小,以适应性能分析配置。

分析结论:

  1. 性能提升在具有高专家并行度(EP)的大型MoE(混合专家)架构中最为明显。
  2. 在节点配置较低的情况下(如上述GLM4-MoE 30B示例),当EP较小时,将张量本地加载到CPU模型的成本超过了P2P传输的好处(导致RDMA耗时反而长于NCCL,加速比为0.59x)。
  3. 随着参与节点的增多,P2P传输展现出极好的扩展性(例如1T参数下达到了7.37倍的加速)。

补充细节:使用方法

P2P更新的启用与配置方式。在miles中,通过--update-weight-transfer-mode p2p启用P2P更新。这将使SGLang引擎通过--sglang-remote-instance-weight-loader-start-seed-via-transfer-engine注册其权重内存,并选择P2P更新流程而不是NCCL广播。Miles依赖于SGLang上的sglang-miles分支,其中包含支持P2P传输的更高级实验性功能。

结论与未来工作

本文成功在SGLang中实现并验证了基于RDMA的P2P权重传输机制,大幅提升了大规模分布式RL中参数更新的速度。针对未来的工作,作者提出了以下展望:

  1. 扩展支持范围:计划为GB200等较新的硬件提供官方支持,并支持SGLang侧的流水线并行(pipeline parallel)。支持更多的量化方式,并将SGLang侧的更改合并到主分支(main)中。
  2. 实验大页内存分配(Huge Page Allocation):考虑启用Transfer Engine的大页GPU分配,而不是永久分配CPU内存,这可以大幅改善注册和注销的成本。这将使得在传输时能够进行就地(in-place)GPU副本创建和内存注册。

附录:工程实现细节

源端副本位置的设计迭代与权衡。我们最初的设计将源端副本放置在GPU上。为了避免浪费训练时的GPU内存利用率,我们在那里投入了大量精力进行优化——如何以流水线方式进行注册、传输和注销?我们能否将模型卸载到CPU上,同时保持网卡上通过虚拟内存进行的内存注册完整无缺?令人惊讶的是,鉴于现代集群上可用的大量带宽,传输本身是最不耗时的——在RDMA的情况下,相比之下,权重注册是最大的时间消耗,整个副本需要花费数十秒。转移到CPU解决了这个问题。

解决CPU内存溢出与内存复用策略。另一个障碍是CPU最初可能会发生OOM(内存溢出)。多个GPU共享同一个HBM,并且在每个源rank上为每个目标rank创建副本很快就会变得无法管理——但我们很快意识到,所有的SGLang rank都是同构构建的,这意味着我们可以牺牲一点传输并发性来优先考虑内存复用。我们最终的设计复用了相同的底层物理内存,并仔细协调了向不同引擎分片逐个进行的传输。

点对点传输计划的映射规则。该计划将每个训练rank映射到其目标rollout引擎rank。使用带有负载均衡的轮询(round-robin)分配:第一批rank获得1:1的映射,剩余的目标被均匀分配。这最小化了每个源的RDMA会话数量。

传输映射的具体示例设定。假设训练配置为pp=4,有32个训练rank和2个SGLang引擎实例,每个实例有16个rank。在all-gather之后,每个pp组中的每个rank都包含针对特定pp rank的完全all-gather张量。每个目标rank都需要接收来自每个pp rank的权重。我们从pp_rank=0开始:我们需要将8个训练rank映射到所有32个目标rank。

多轮轮询映射的具体执行步骤。

  1. 轮询(Round-Robin):映射 src_rank 0 -> tgt_rank 0,…… 映射 src_rank 7 -> tgt_rank 7。
  2. 所有现有的源具有相同的负载。进行另一轮轮询以分配 src_rank 0 -> tgt_rank 8,…… src_rank 7 -> tgt_rank 15。
  3. 最后,注意到 tgt_rank 16 与 tgt_rank 0 是相同的。回顾现有的分配,并将相同的引擎rank添加到其现有的源中。我们最终得到 src_rank 0 -> [tgt_rank [0, 16], tgt_rank [8, 24]] 等。

识别待传输SGLang张量的映射机制。为了以分桶(bucketed)方式执行权重传输本身,我们需要在每次model.load_weight()之后识别准备发送过去的参数。我们首先构建Hugging Face张量与其对应的SGLang张量或张量分片之间的映射。

sglang_name, shard_id, num_shards, expert_id, num_local_experts = parameter_mapper.map(hf_tensor)

专家张量映射的具体解析。其中,对于每个模型和每个Hugging Face张量,我们获得它在SGLang中映射到的专家和张量分片。例如:

model.layers.0.mlp.experts.3.down_proj.weight -> model.layers.0.mlp.experts.w2_weight, w2, 2, 3, 5

SGLang张量的发送条件。只有当所有num_local_experts的所有num_shards都已更新时,我们才能发送一个SGLang张量。

基于共享副本的多目标传输流设计。为了支持使用相同的底层内存向多个目标引擎实例传输,我们利用了基于线程池的任务池和用于部分更新张量的缓存缓冲区。SGLang张量的传输任务要么处于关键路径上需要等待,要么可以提交到池中并在最后才检查完成情况。TransferEngine库移除了GIL并在多线程配置中启用了并行性。

图2:使用相同的底层副本,一个训练源rank和2个目标rank的传输流示例。
图2:使用相同的底层副本,一个训练源rank和2个目标rank的传输流示例。

分桶更新中的张量缓冲机制分析。请注意,红色的张量是在all-gather之后产生的,并包含所有专家和tp分片。然而,一次分桶更新可能并不包含SGLang张量的所有张量分片——如上图所示,q_proj和k_proj已被收集,但在上一次分桶更新中被缓冲在副本之外。这也是为什么共享副本更新需要大于1的缓冲区大小。在最坏的情况下,缓冲区可能是原始缓冲区的num_shard倍!然而,由于named_parameters通常将相关的张量排序在一起,实际需要的额外缓冲区很小。

副本更新与传输任务的调度逻辑。一旦一个SGLang张量收集了所有必要的分片,我们就更新副本,使用其自身的并行度信息加载对应于第一个目标引擎rank(例如SGLang引擎rank 0)的权重。传输(send 1)位于关键路径上,必须在继续再次更新底层CPU分片之前完成(即目标权重已更新),使用相同的Hugging Face张量,但使用来自另一个引擎rank的并行度信息。对于要更新的最后一个引擎rank,不再有人等待相同的SGLang张量,因此任务(send 2)可以进入线程池,我们立即继续进行下一次all-gather。

其他量化与后处理逻辑的处理。并非所有必要的张量都注册在SGLang的model.named_parameters()中。例如,在DeepSeekV3模型系列中,MLA包含本地张量w_kc和w_vc,这些张量是在所有权重完全加载后在post_weight_load()调用中生成的。SGLang包含大量自定义量化和特定于硬件的逻辑,这些逻辑依赖于设备,无法在我们的CPU副本上执行。