此前 770GB 权重平均需要约 36 秒完成同步;现在只需 15 秒,请求暂停不到一秒,比此前的最优水平快 7.5 倍。
作者
Jerry HongParas Stefanopoulos发布于
2026 年 10 月 30 日
分享
发布于
2026 年 10 月 30 日
在我们上一篇 技术博客中,我们展示了针对约 770 GB 的 GLM-5.3 检查点的 增量权重同步 ,耗时 36 秒,请求暂停 6 秒 。同样的同步现在只需 15 秒,暂停 0.8 秒,吞吐量提升最高可达 3.2 倍。
我们围绕 GPU 上已有的权重重建了权重同步路径。这使我们不再需要在 CPU 内存中维护第二份完整副本。这一新设计让我们能够:
仅读取发生变化的 GPU 位置来校验更新
提前准备 GPU 写入,从而将暂停时间降到最低
将释放出的主机内存用于 KV cache,提升吞吐量
解锁此前因 CPU 内存不足而受阻的 Kimi K3 在 8:GB300 上的 RL rollout
增量权重同步简要回顾
客户环境中的训练器将完整检查点和稀疏增量发布到对象存储。每个增量张量都是 zstd(parent_bytes XOR new_bytes)。快照上传后,训练器会发送一个增量权重同步请求,指明它希望对外提供服务的策略。我们各集群中的 rollout 集群通过 Baseten Delivery Network(BDN)将每个对象拉取一次,重建新策略,并就地更新其 GPU。同样重要的是,训练器和 rollout GPU 不共享任何基础设施。
这部分没有变化。
✕
图 1:训练器在其自身环境中将每个检查点和增量各发布一次到对象存储;每个副本通过 Baseten Delivery Network 拉取相同的对象,并各自完成同步。CPU 副本是瓶颈
我们的第一版设计将完整的父检查点保存在主机内存中。解码器需要它来将 XOR 字节转换为新值,并且在每次更新后都必须对它进行修补,以便下一个增量能针对正确的父副本进行解码。
这份副本在三个方面给我们带来了代价:
内存。 每个副本都在主机 RAM 中保存其完整父副本:GLM-5.3 约为 770 GB。在 8×B300 节点上,该副本与 1.1 TB 的主机 KV cache 无法共存;缓存不得不缩减到 275 GB。
时间。 将 CPU 副本提升为新策略每次更新大约需要 45 秒 ,并会阻塞下一个增量。
两个事实来源。 每次更新都必须同时落到 GPU 和 CPU 副本上,否则下一个增量就会针对错误的父副本进行解码。
GPU 已经保存了策略 N 的每一个字节。我们只需要一种方法来读取发生变化的那几个字节。
✕
图 2:以前,每个增量都针对约 770 GB 的 CPU 副本进行解码,随后该副本需要 45 秒才能提升;现在 GPU 权重就是父副本,同样的更新在 15 秒内上线,暂停仅 0.8 秒。GPU 就是父副本
启动时,我们从本地磁盘副本加载模型,通过引擎实际的 GPU 布局对照该副本校验每一个对外提供的张量,记录每个张量的 Adler-32 校验和,然后删除该副本。从那时起,GPU 内存中保存着唯一的父副本。
当增量到达时,我们的原生解码器直接遍历 zstd 的命令,只保留非零的 XOR 字节:对于每个发生变化的元素,保留其位置和 XOR 掩码。它从不实际生成稠密的 XOR 张量,也从不读取模型权重。
然后,在旧策略继续提供服务的同时,每个 GPU worker 会:
仅在发生变化的位置上 收集旧值.
应用 XOR 掩码以得到 绝对新值.
对照训练器的校验和校验结果。
按照引擎的原生张量布局,将新值封装进写入计划。
图 3:直到写入之前的所有操作都在旧策略继续提供服务时进行;在暂停期间,每个 rank 写入绝对值,并且所有 rank 必须达成一致后才会发布新策略。以 O(1) 更新实现增量校验和
训练器会发布每个发生变化张量的新字节的 Adler-32。朴素地校验意味着要对整个张量重新计算哈希,这效率很低。
每个字节都独立地对 Adler-32 做出贡献,其贡献量仅取决于该字节的(位置,值)。因此,一个发生变化的字节对校验和的影响,可以根据其位置、旧值和新值在常数时间内计算出来。我们从启动时记录的校验和出发,为每个发生变化的字节添加一次修正,然后与训练器的校验和进行比较。未发生变化的字节永远不会被读取。
这一优化使整个更新最多快了 2.3× (在 30 GB 增量上的 Kimi K3 从约 230 秒降至约 100 秒),同时确保了正确性。损坏的下载或错误的父副本会在任何 GPU 写入之前被拒绝。
✕
图 4:只读取发生变化的字节:其旧值和新值在常数时间内修正已存储的校验和,修正后的校验和必须与训练器的一致。不到一秒的暂停
在暂停之前进行如此大量准备的目的,是尽量减少我们在暂停期间执行的操作。现在暂停变为:
关闭准入,并就地暂停正在运行的请求。
就地写入新值 + 刷新派生权重。
发布新策略版本,重新开放准入。
在 GLM-5.3 上,暂停现在为 0.8 秒,低于此前的 6 秒。此外,45 秒的 CPU 提升已经消失,因此下一个增量一到达就可以立即应用。
进一步优化
避免 CPU 检查点是一个巨大的突破。然而,在没有 CPU 副本的情况下,更新工作会与提供流量服务的 GPU 和 CPU 共享资源。这些优化可防止它挤占服务吞吐量。
感知并行性的增量解码器
在张量并行和专家并行下,每个 GPU 服务于自己的分片。我们的解码器按拥有每处变更的 GPU 拆分每个增量,每个 GPU 将其变更直接映射到自身打包布局中的地址,直至 swizzled FP8 和 FP4 缩放因子。
避免增量权重同步与服务之间的争用
SGLang 的调度器是一个 Python 事件循环,因此其进程内任何 CPU 密集型工作都会争抢 GIL 并阻塞批调度。我们的更新规划改为在一个独立的、不依赖 CUDA 的辅助进程中运行,通过共享的固定内存与 GPU 工作进程交换批量数据。
解码在下载完成之前就已开始
增量以多个分片的形式到达,每个分片一落地就立即被解码。下载与解码是流水线式并行进行的,而非顺序执行。解码器运行在持久的低优先级原生线程上,线程数量经过调整,以便留出 16 个核心用于服务。
Kimi K3 在两个 GB300 节点上运行
Kimi K3 在服务所用的 MXFP4 布局下为 1.7 TB。若使用 CPU 检查点,每个 GB300 节点仅为其那一半权重就需要约 838 GB 主机内存,几乎相当于一个服务容器的全部内存上限。这是一个硬性约束,因为 GB300 出厂即带有预配置的硬件。现在它运行在两个 GB300 节点上,每个节点配备四块 GPU。每个节点只解码、校验并暂存自己拥有的张量,绝不持有另一节点的权重。两个节点都必须成功写入,新策略才会发布。 1.7 TB 在其服务所用的 MXFP4 布局下。若使用 CPU 检查点,每个 GB300 节点仅为其那一半权重就需要约 838 GB 主机内存,几乎相当于一个服务容器的全部内存上限。这是一个硬性约束,因为 GB300 出厂即带有预配置的硬件。
现在它运行在 两个 每个节点配备四块 GPU。每个节点只解码、校验并暂存 它只拥有那些张量,绝不持有另一节点的权重。两个节点都必须成功写入,新策略才会发布,请求才会恢复。
在 8.4 GB 增量下,Kimi K3 从增量权重同步请求到权重生效仅需 75 秒,暂停 4.1 秒。 75 秒,仅为其一半权重就需要几乎相当于一个服务容器的全部内存上限。 4.1 秒 暂停。
✕
图 5:每个 GB300 节点只解码、校验并写入自己拥有的张量;主机内存不保存权重副本,只保留有界的增量预算和主机 KV 层级。将内存用于 KV cache
在本博客全文中,我们多次提到,不将检查点保留在主机内存中带来的最大收益,是额外释放出的内存开销,使我们得以进行 KV cache 卸载。其工作原理如下,并附上一些测量数据:
被驱逐的 GPU KV 页会写回主机内存而不是被丢弃,之后具有相同前缀的请求会重新加载这些页,而不是重新运行预填充。
RL rollout 是多轮对话,且共享前缀很长:从 CPU 内存重新加载一个已被驱逐的 150k token 前缀需要 0.48 秒,而 4.9 秒 的全新预填充。
我们还在 AgentX 基准测试中运行了 Kimi K3,对比了新设计下启用卸载与旧的 CPU 检查点方案的表现。
在并发数为 16 时,KV cache 已经能完全放入 GPU 显存,因此没有任何差别。
在并发数为 48 时,智能体的前缀已无法放入 GPU 显存,但仍能放入主机内存,因此卸载将未命中转为命中:命中率从 2% 升到 92% , 单 GPU 输出吞吐提升 3.2 倍。
图 6:在并发数为 16 时,两种方案都能把所有内容放在 GPU 上;在并发数为 48 时,只有 GPU 检查点副本还有空闲的主机内存可用于卸载,缓存命中率从 2% 升至 92%。结果
所有运行均为单副本,使用与训练器形状相同的固定增量,计时从训练器的增量权重同步请求开始,到副本开始服务新策略为止。计时从 S3 标记开始,到第一个新响应为止。
如果你希望将其用于你的训练栈,请阅读我们的 RL rollout 文档 ,或 联系我们的团队如果这类系统工作听起来有趣, 我们正在招聘。.
订阅我们的新闻通讯
及时了解模型性能、推理基础设施等最新动态。
$?/$