Delta 权重同步#
Delta 权重同步只发送两次同步之间发生变化的字节,而不是每次都写一份完整 checkpoint,以此让非 colocate 的 rollout engine 保持最新。它面向大模型、跨集群或跨数据中心的训推解耦场景——这种场景下每次都写整份 actor 权重是主要开销。
它只支持 disk transport。训练端把每次同步发布为一份 canonical HF checkpoint 目录;engine 的 /pull_weights 端点(随 slime 的 sglang patch 提供)把 apply 扇出到 engine 覆盖的每一个 host 并校验,随后 engine 通过原生的 update_weights_from_disk 端点 reload 打过补丁的本地 checkpoint。slime 对每个 engine 只与一个端点通信,所以多节点 serving 和外部 rollout engine 在 slime 侧都不需要任何额外支持。
配置#
--update-weight-mode delta
--update-weight-transport disk
--update-weight-disk-dir /shared/fs/delta-updates
--update-weight-local-checkpoint-dir /local/nvme/rollout-ckpt
--update-weight-delta-encoding xor # 或: overwrite
--update-weight-delta-checksum xxh3-128 # 或: blake3, adler32
参数 |
作用 |
|---|---|
|
训练端发布 delta、rollout host 读取 delta 的共享文件系统目录。 |
|
host 本地(如 NVMe)的完整 HF checkpoint,由 |
|
磁盘上的 delta 编码: |
|
逐 tensor 完整性 checksum: |
delta 始终用 zstd(level 1)压缩;profiling 显示对这类数据它在 wire 大小和解压速度上都优于 lz4 / gzip / snappy / brotli,所以不做成可配置项。
工作原理#
Seed。 首次同步,以及训练端恢复或替换 engine 后的首次同步,会从训练端当前权重发布完整 HF checkpoint,并从同一批 tensor 捕获 CPU snapshot。所有 engine 加载成功后才恢复生成。版本号高于目录中所有已有版本,包括已经写盘但丢失应答的更新;重启不会删除旧版本流。
Publish。 之后每次同步,训练端把每个 gather 出的 HF tensor 与 snapshot 做 diff,编码、压缩,写到
--update-weight-disk-dir下的新版本目录weight_v{N:06d}/。该目录是一份 canonical HF checkpoint——model-NNNNN.safetensors文件装着压缩后的 diff tensor,外加model.safetensors.index.json(tensor 名 → 文件)承载 apply 元数据——所以这个产物是可移植的,不绑定训练端的并行 layout。随后 snapshot 推进到新值,供下次 diff。Pull。 训练端对每个 engine 调用
/pull_weights。engine 内部把请求广播到每个节点的每个 rank;每个 host 把新版本的 delta 原地 apply 进它的本地 checkpoint(host 级文件锁把同 host 的多个 rank 合并成一次 apply)。apply 在 tensor 之间并行,并逐 tensor 校验(见"完整性");只有每一个 host 都持有校验通过的 checkpoint,该调用才报告成功。/pull_weights并不绑定 delta:每个发布的版本是自描述的。若某个版本是一份普通的完整 HF checkpoint(index 中没有 delta 元数据),pull 就直接整份拷贝——同时重置链条,因此晚加入的 新 host 从最近的完整版本 seed,而不必回放全部 delta,旧的 delta 也可以被清理。slime 的 full 模式 disk 同步在设置了--update-weight-local-checkpoint-dir时正是走这条路径。Reload。 engine 通过原生
update_weights_from_disk路径 reload 打过补丁的本地 checkpoint——权重加载代码从不接触 delta 格式。
snapshot 与 engine 都以同一份已发布的 HF 字节为基准,即使训练端恢复的是比 --hf-checkpoint 更新的模型也成立。因此,Megatron→HF 转换中的差异(例如 embedding / LM head 被裁掉的 vocab padding 行)不会破坏下一次 delta。
编码#
两种编码都是字节级、与 dtype 无关的,所以量化 checkpoint 也走同一条路径。engine 从每个版本的 index 元数据读取所用编码。
xor(默认):写new ^ old。wire 最小、apply 最快(顺序访问、对 cache 友好;未变化的字节是 0,被压缩器压到极小)。它是一个对合(involution),所以必须恰好对正确的 base apply 一次——apply 两次会还原。overwrite:写变化的位置及其新的绝对值。wire 更大、apply 是对 cache 不友好的分散写,但幂等:重复 apply(或把部分 apply 的 delta 补完)无论执行多少次都收敛到同一状态。当“可重复 apply”比 wire 大小更重要时用它。
完整性#
训练端把每个 tensor 新状态的逐 tensor checksum 存进版本里。apply 之后每个 host 重新计算 checksum,任何不匹配都会 raise——失败会通过 /pull_weights 的响应传回,所以损坏的 delta 或错误的 base 会直接报错失败,而不会把坏权重提供出去。apply 还拒绝乱序执行:一个版本只会在它声明的 base 版本之上 apply。
--update-weight-delta-checksum 选择算法。checksum 不是 apply 的瓶颈(apply 受解压 + XOR 限制),所以这是一个 digest 属性的选择,而非速度选择:xxh3-128(默认)是最宽的快速非加密 digest;blake3 是加密 digest,用于不可信存储;adler32 用于与期望它的系统互操作。
共享文件系统可见性 hook#
在 POSIX 共享文件系统(NFS、Lustre……)上不需要额外步骤。对于需要显式 commit/refresh 才能让写入跨 host 可见的对象存储挂载,可以提供两个可选 hook(通过 import 路径加载——slime 和 sglang 里都不存在任何厂商特定代码):
--custom-update-weight-post-write-path(slime,训练端):在一个版本的文件写完之后、通知 engine 读取之前调用(例如把待写入数据上传到底层对象存储)。签名:hook(args, version_dir, rollout_engines)。--sglang-custom-pull-weights-pre-read-hook(sglang server 参数,engine 端):在每个 host 上、/pull_weights读取 delta 目录之前于 engine 内部调用(例如刷新挂载视图)。签名:hook(delta_dir, target_version)。