Prhub

#35509 [Diffusion] Fix multi-group layerwise offload startup memory

原始 PR 作者 mickqian 合并时间 2026-08-19 20:32 文件变更 2 提交数 2 评论 1 代码增减 +104 / -10

执行摘要

修复多组 layerwise offload 启动内存峰值问题

PR body 明确指出:Model can expose multiple entries in layer_names. Previously, each manager moved the entire model immediately after replacing only its own layer group. Earlier managers therefore treated later groups as non-layer parameters and transiently moved them to the accelerator, making startup peak memory depend on layer_names ordering. 即多个层组时,先初始化的 manager 会把尚未卸载的后续层组临时当作非层参数搬到加速器,导致峰值内存随声明顺序波动。

值得精读。该 PR 展示了一个典型的多组件共享模型的初始化协调问题:通过延迟初始化 + 按资源占用排序,消除了执行顺序对内存峰值的影响。对类似场景(如多组参数需要分批卸载/加载)有很好的借鉴价值。

讨论亮点

该 PR 没有产生代码评审讨论(review_comments_count = 0)。唯一活动是作者在 Issue 中评论 /tag-and-rerun-ci 触发 CI 重跑;最终 Extra CI 有一次运行失败,但 PR 已合并。

实现拆解

  1. 新增 _managed_parameter_bytes 方法(layerwise_offload.py):遍历模型命名参数,仅统计能匹配到 layer_idx 且小于 num_layers 的本地张量字节数,作为后续按大小排序初始化的度量。
  2. 将原 _initialize 拆分为 _initialize_layer_weights_finalize_initialization:前者的职责是收集参数、合并同 dtype 权重、替换为 CPU 缓存视图与占位符;后者则在所有层组替换完成后统一调用 model.to(device) 移动真正的非层参数,并触发 prepare_for_next_req 预取与 forward hook 挂载。这样避免了早期 manager 把其他层组误当非层参数搬上设备。
  3. 修改 configure_layerwise_offload:创建 manager 时改为 initialize=False 延迟初始化;在非 MPS 分支中,先对 enabled 的 manager 按 _managed_parameter_bytes() 降序排序,逐个调用 manager.initialize(),保证最大层组最先卸载、尽早释放设备内存;同时保持 layerwise_offload_managers 中的声明顺序不变,以维护运行期行为一致性。
  4. 测试配套(test_layerwise_offload.py):新增 _MultiGroupComponent(含 small_blockslarge_blocksnon_layer 参数)和 test_configure_offloads_all_layer_groups_before_moving_non_layers,通过 monkeypatch 记录 _initialize_layer_weights 的调用顺序,断言大组先初始化、manager 声明顺序不变、model.to 只调用一次且执行时所有层参数已被替换为 (1,) 的占位符。
文件 模块 状态 重要度
python/sglang/multimodal_gen/runtime/managers/memory_managers/layerwise_offload.py 层卸载 modified 7.67
python/sglang/multimodal_gen/test/unit/test_layerwise_offload.py 单元测试 modified 6.28

关键符号

_managed_parameter_bytes _initialize_layer_weights _finalize_initialization configure_layerwise_offload

关键源码片段

python/sglang/multimodal_gen/runtime/managers/memory_managers/layerwise_offload.py core-logic

核心逻辑变更:拆分初始化函数、新增参数字节统计、调整 configure_layerwise_offload 的初始化顺序,直接修复多组启动内存峰值问题。

@torch.compiler.disable
def _initialize(self) -> None:
    if not self.enabled:
        return
​
    if self._synchronous_mps:
        # MPS 同步模式下权重由 shared memory 托管,直接建立映射即可
        self._named_parameters = dict(self.model.named_parameters())
        self._named_buffers = dict(self.model.named_buffers())
        self._initialize_mps_cpu_weights()
        return
​
    # 第一步:只替换所有受管 layer 的权重为 CPU 缓存上的视图,
    # 不在这里移动任何参数,避免把其它 manager 的 layer 当作非层参数。
    self._initialize_layer_weights()
​
    # 第二步:此时 layer 权重已被占位符替换,把剩余非层参数(如
    # 全局 norm、embedding 等)统一搬到 device,不会重新加载已卸载的层。
    if not self._has_dtensor_weights:
        self.model.to(self.device)
​
    # 第三步:逐组完成 prefetch 预热与 forward hook 挂载,
    # 保持每个 manager 独立的运行期生命周期。
    self._finalize_initialization()
def _managed_parameter_bytes(self) -> int:
    # 统计本 manager 自己管理的 layer 参数占用的本地字节数,
    # 用于 configure_layerwise_offload 时决定大组优先初始化。
    total_bytes = 0
    for name, tensor in self.model.named_parameters():
        layer_idx = self._match_layer_idx(name)
        if layer_idx is None or layer_idx >= self.num_layers:
            continue
        local_tensor = self._to_local_tensor(tensor)
        total_bytes += local_tensor.numel() * local_tensor.element_size()
    return total_bytes

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

  1. 初始化路径被拆分重构,属于核心路径变更,若 _initialize_layer_weights_finalize_initialization 拆分有误可能引发回归。
  2. 排序依赖 _managed_parameter_bytes 的统计,若参数名匹配规则有遗漏,排序可能不准确,但不影响正确性,只会降低内存优化收益。
  3. _has_dtensor_weights 为 True 时不会调用 model.to,新增测试未覆盖 DTensor 权重场景。
  4. MPS 分支仍保留原有 _initialize_mps_cpu_weights 路径,改动风险较低,但需关注后续平台差异。

对用户:修复了存在多个 layer_names 条目的 diffusion 模型在启动阶段可能出现的峰值内存过高的 OOM 风险,提升启动稳定性。对系统:初始化逻辑职责更清晰,层组替换、非层参数迁移、最终化三个阶段分离,便于后续扩展。对团队:为 layerwise offload 后续支持更多层组与更复杂的策略提供了更稳健的初始化框架。

核心初始化路径变更 多组协调初始化回归风险 DTensor 场景未覆盖

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论