Prhub

#33844 [diffusion] simplify disaggregation transport hygiene

原始 PR 作者 mickqian 合并时间 2026-08-06 21:50 文件变更 5 提交数 3 评论 2 代码增减 +29 / -205

执行摘要

简化扩散解耦传输层,清理过期同步 API 与重复 schema

PR body 指出传输路径存在重复的 schema 组装,以及 diffusion runtime 根本不调用的 API 造成的双份维护面:“The transfer path had duplicate schema assembly and two maintenance surfaces for APIs that are not called by the diffusion runtime. Consolidating the live path makes RDMA staging and prefetch ownership easier to reason about.” 同时明确 “No public API changes are intended”,说明这是一次内部卫生清理。

值得快速翻阅,不必精读:作为 refactor-only 清理,它展示了在活跃子系统中收敛 schema、删除 dead API、复用已有工具函数的实践。重点看 manager.py 中同步/异步 API 的去留判断、protocol.pyTransferStagedMsg 的 schema 化,以及 _prefetch_transfer_ready 返回元组的收窄方式。若要在生产长期维护,建议补一个覆盖 async stage/load 的轻量单测,并说明 extra CI 失败的原因。

讨论亮点

本 PR 没有任何 review 评论。唯一交互是作者在 issue 评论中两次触发 /tag-and-rerun-ci 重跑 CI,说明关注点主要在回归验证而非设计讨论。最终基础 PR Test(Run #31085927775)通过,extra CI(Run #31085927225)失败但未阻止合并;由于没有同行评审意见,TransferStagedMsg schema 化与 API 删除的取舍只能从 PR body 推断。

实现拆解

  1. schema 收敛到 transport/protocol.pyprotocol.py 移除无用的 logging__post_init__TransferStagedMsg.manifest 改用 field(default_factory=dict),并新增 scalar_fields 字段。scheduler_mixin.py_disagg_encoder_transfer_stage 从手工 json.dumps(staged_data) 改为 encode_transfer_msg(TransferStagedMsg(...)),使 protocol 模块成为 staged 消息的唯一 schema。
  2. 复用默认值判断逻辑:删除 scheduler_mixin.py_BASE_SP_DEFAULTS 缓存与长注释,改为对每个 SamplingParams 字段调用已有 _is_default(value, f) 判断是否跳过默认值字段,避免两套“字段是否等于默认值”的实现。
  3. 精简 prefetch 状态_prefetch_transfer_ready 返回值从六元组收窄为四元组 (req, load_event, request_id, prealloc_slot_id)_disagg_prefetch_event_loop_handle_transfer_ready 同步调整解包;_disagg_denoiser_compute / _disagg_decoder_compute 移除 role_name 形参;多 rank 判断收敛为 _is_multi_rank()
  4. 删除传输层死代码manager.py 删除同步 stage_tensorsload_tensorsget_receive_slot_addr / get_receive_slot_offset / get_staged_info / free_slots_countbuffer.pywrite_tensor 去掉未使用的 name 参数,并删除 free_slots_count / get_stats 包装;allocator.py 删除 count_free_slots
  5. 测试与 CI 配套:没有新增测试文件;作者按 SGLang-Diffusion 重构指导未做本地验证。CI 中基础 PR Test 通过、extra CI 失败后仍合并,缺少对删除 API 的回归保护。
文件 模块 状态 重要度
python/sglang/multimodal_gen/runtime/disaggregation/transport/manager.py 传输管理 modified 8.11
python/sglang/multimodal_gen/runtime/disaggregation/scheduler_mixin.py 解耦调度 modified 7.42
python/sglang/multimodal_gen/runtime/disaggregation/transport/buffer.py 传输缓冲 modified 6.12
python/sglang/multimodal_gen/runtime/disaggregation/transport/allocator.py 内存分配 modified 5.94
python/sglang/multimodal_gen/runtime/disaggregation/transport/protocol.py 传输协议 modified 6.31

关键符号

stage_tensors load_tensors get_receive_slot_addr get_receive_slot_offset get_staged_info free_slots_count count_free_slots get_stats _disagg_denoiser_compute _disagg_decoder_compute _prefetch_transfer_ready _disagg_encoder_transfer_stage write_tensor

关键源码片段

python/sglang/multimodal_gen/runtime/disaggregation/scheduler_mixin.py core-logic

prefetch 状态简化与 `TransferStagedMsg` 落地的主战场,涉及默认值判断逻辑、compute 函数签名与消息编码多处修改。

    def _prefetch_transfer_ready(self: Scheduler, msg: dict) -> tuple:
        """Load tensors from transfer buffer and build Req for a transfer_ready message.        Called from the recv prefetch thread. Loads on _transfer_stream and builds the Req,
        so the main thread can start compute immediately. Returns (req, load_event, request_id, prealloc_slot_id).
        """
        request_id = msg['request_id']
        manifest = msg.get('manifest', {})
        scalar_fields = msg.get('scalar_fields', {})
​
        if self._disagg_metrics:
            self._disagg_metrics.record_request_start(request_id)
​
        # 处理预分配 slot:若发送方已分配好接收槽,直接注册进 transfer manager
        prealloc_slot_id = scalar_fields.pop('_prealloc_slot_id', None)
        if (prealloc_slot_id is not None and prealloc_slot_id in self._preallocated_slots):
            slot = self._preallocated_slots[prealloc_slot_id]
            self._transfer_manager.register_prealloc_as_receive(request_id, slot)
​
        # 在 transfer_stream 上非阻塞加载张量,CPU 侧可同时构建 Req
        local_device = f'{current_platform.device_type}:{self.worker.local_rank}'
        tensors, load_event = self._transfer_manager.load_tensors_async(
            request_id, manifest, device=local_device, stream=self._transfer_stream,
        )
​
        # NOTE: 不要在这里释放 receive slot——异步 load 仍在进行,
        # slot 必须保持有效直到主线程等待 load_event;释放发生在 _disagg_prefetch_event_loop。
​
        # CPU 工作与 GPU load 重叠:先构建 Req 再交给主线程 compute
        req = self._build_disagg_req(scalar_fields, tensors)
​
        # NOTE: 不要在这里调用 scheduler_mod.set_timesteps()!
        # 该调用修改共享 scheduler 状态(self.sigmas),会破坏主线程正在运行的
        # denoising loop,应推迟到主线程的 _disagg_prefetch_event_loop 中执行。
​
        # 返回四元组:role_name 与 scalar_fields 已无消费者,不再向下游传递
        return req, load_event, request_id, prealloc_slot_id
python/sglang/multimodal_gen/runtime/disaggregation/transport/protocol.py core-logic

`TransferStagedMsg` 成为 staged 消息唯一 schema,新增 `scalar_fields` 并移除 `__post_init__`,是本次 schema 收敛的落点。

# SPDX-License-Identifier: Apache-2.0
"""Transfer protocol messages for disaggregated diffusion.All messages are sent as ZMQ multipart with a b'__transfer__' discriminator
in frame[0] and JSON payload in frame[1].
"""import json
from dataclasses import asdict, dataclass, field
from typing import AnyTRANSFER_MAGIC = b'__transfer__'
​
​
class TransferMsgType:
    # Instance -> DiffusionServer
    STAGED = 'transfer_staged'
    ALLOCATED = 'transfer_allocated'
    PUSHED = 'transfer_pushed'
    DONE = 'transfer_done'
​
    # DiffusionServer -> Instance
    ALLOC = 'transfer_alloc'
    PUSH = 'transfer_push'
    READY = 'transfer_ready'
​
    # Registration
    REGISTER = 'transfer_register'
    REGISTER_ACK = 'transfer_register_ack'
​
​
@dataclass
class TransferStagedMsg:
    # 使用 field(default_factory=dict) 而不是可变默认值 None,
    # 避免共享同一 dict 实例,也省掉了 __post_init__ 的判空补丁
    msg_type: str = TransferMsgType.STAGED
    request_id: str = ''
    data_size: int = 0
    manifest: dict = field(default_factory=dict)
    session_id: str = ''
    pool_ptr: int = 0
    slot_offset: int = 0
    # scalar_fields 承载 encoder 阶段消息的标量载荷(如 seed、采样参数),
    # 现在由 protocol schema 统一接管,调用方不再手工拼 dict
    scalar_fields: dict = field(default_factory=dict)

评论区精华

CI 重跑与 extra CI 失败 other

作者在 issue 评论中两次使用 /tag-and-rerun-ci 触发 CI;基础 PR Test 通过,extra CI 失败。没有实质技术讨论。

结论:无设计争议;extra CI 失败原因未公开说明,PR 仍被合并。 · closed

风险与影响

  • manager.py 等删除的同步 API 虽在仓库内无调用方,但对下游分支或插件属于可见接口,若被直接引用会出现 ImportError / AttributeError
  • _prefetch_transfer_ready 返回值是跨线程隐式契约,与旧版主线程逻辑混跑会导致解包失败,存在部署/回滚期间的兼容风险。
  • TransferStagedMsg 新增 scalar_fields 字段改变 dataclass 结构;虽然 sender 此前已手工发送该字段,但任何直接反序列化旧 schema 的外部调试工具可能不兼容。
  • 无本地验证且无新增测试,基础 CI 通过但 extra CI 失败原因未说明,存在未被发现的回归可能。
  • 改动集中在 disaggregation 传输热路径(prefetch 线程、RDMA 消息发送),异步事件处理若有疏忽可能影响 pipeline 并行下的请求调度。

对用户无感知,无公开 API 变更;对系统而言显著降低 transport 层维护面,消除重复 schema 组装,使 RDMA stage/load 与 prefetch 的所有权更易推理,同时减少 _BASE_SP_DEFAULTS 这类缓存的启动开销(幅度很小)。对团队而言,这为后续 diffusion disaggregation 的 prefetch/alloc 演进(如 capture-safe 传输、DP 扩展)提供了更干净的地基,但缺少测试覆盖是主要薄弱点。

无本地验证 缺少测试覆盖 extra CI 失败 删除传输层 API 存在外部调用风险 prefetch 线程返回值变更

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论