Updating 1T Parameters in Seconds — P2P Weight Transfer in Large Scale Distributed RL
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广播方法的补充,且兼容所有主流的开源模型。
该方案的核心创新点在于:
- 源端CPU引擎副本(Source-side CPU engine replica):在训练节点的CPU内存上构建模型副本,以极小的内存代价换取传输效率。
- 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原语,这在传输过程中迅速成为瓶颈。
现有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]块量化大小,以适应性能分析配置。
分析结论:
- 性能提升在具有高专家并行度(EP)的大型MoE(混合专家)架构中最为明显。
- 在节点配置较低的情况下(如上述GLM4-MoE 30B示例),当EP较小时,将张量本地加载到CPU模型的成本超过了P2P传输的好处(导致RDMA耗时反而长于NCCL,加速比为0.59x)。
- 随着参与节点的增多,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中参数更新的速度。针对未来的工作,作者提出了以下展望:
- 扩展支持范围:计划为GB200等较新的硬件提供官方支持,并支持SGLang侧的流水线并行(pipeline parallel)。支持更多的量化方式,并将SGLang侧的更改合并到主分支(main)中。
- 实验大页内存分配(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。
多轮轮询映射的具体执行步骤。
- 轮询(Round-Robin):映射
src_rank 0 -> tgt_rank 0,…… 映射src_rank 7 -> tgt_rank 7。 - 所有现有的源具有相同的负载。进行另一轮轮询以分配
src_rank 0 -> tgt_rank 8,……src_rank 7 -> tgt_rank 15。 - 最后,注意到
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并在多线程配置中启用了并行性。
分桶更新中的张量缓冲机制分析。请注意,红色的张量是在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副本上执行。
💬 评论讨论
欢迎在这里分享您的想法和见解!