Prhub

#33928 [Diffusion] Make ring admission a backend capability

原始 PR 作者 mickqian 合并时间 2026-08-07 17:55 文件变更 7 提交数 3 评论 2 代码增减 +87 / -22

执行摘要

Ring 准入改为后端能力自声明,去掉两处白名单

PR body 明确指出:ring 的逐跳 online-softmax 合并需要 kernel 的 softmax LSE,因此应由后端声明能力而不是由调用点命名后端,这与 #33707 的 packed-varlen 准入模式一致。同时 server_args 的字符串级检查因后端类在部分平台不可导入而必须保留,但需集中为镜像常量并用测试防止两个视图漂移;zimage 在 ring_degree > 1 时静默降级到全序列 K/V gather,现在通过一次性 warning 让用户感知该性能损失。

值得精读。改动虽小,但把“能力自声明优于调用点白名单”的模式再次落地(与 #33707 一致),并演示了如何用单元测试锁住类级能力与字符串级配置的一致性。对要新增 diffusion 注意力后端、或想理解 USPAttention gate 演进路径的同学有直接参考价值;同时也提示接入新后端时务必同步覆写 supports_ring_rotation() 并补一致性测试。

讨论亮点

该 PR 没有正式 review 评论(review_comments_count 为 0),主要讨论来自作者在 Issue 中的 CI 说明与 PR body 的设计说明:

作者 mickqian:multimodal-gen-test-2-gpu (0) 失败是 fast-fail 级联,不是本 PR 造成;根因是 main 上 #33923 合并的 masked-path guard 拒绝了合法的单 rank 调用,qwen 的 1-GPU masked 家族在每条分支路径后都会抛 NotImplementedError。修复已由 #33953 提供(将 guard 限定到 sequence-parallel 运行),合并后 rebase 即可转绿。

PR body:ring 逐跳 online-softmax 合并需要 kernel 的 softmax LSE,因此后端声明能力而非调用点命名它们(与 #33707 的 packed-varlen 准入模式相同);server_args 字符串名单保留但集中为镜像常量并用测试固定,避免两边漂移。

实现拆解

实施过程按以下五步拆解:

  1. 定义能力契约:在 python/sglang/multimodal_gen/runtime/layers/attention/backends/attention_backend.pyAttentionBackend 基类新增类方法 supports_ring_rotation(),默认返回 False,docstring 说明 ring 逐跳 online-softmax 合并依赖 kernel 的 softmax LSE。这延续了 #33707 supports_packed_varlen 的能力自声明模式,调用点不再关心具体后端名单。

  2. 后端声明能力flash_attn.pyFlashAttentionBackendsage_attn.pySageAttentionBackend 覆写该方法返回 True(flash_attn 的 kernel 以 return_softmax_lse=True 暴露 LSE)。同时将通过 lint 提交把 AttentionBackend 等符号的 import 提升到 flash_attn 模块顶部,消除循环导入隐患。

  3. 消费能力并 fail-earlypython/sglang/multimodal_gen/runtime/layers/attention/layer.pyUSPAttention.__init__get_ring_parallel_world_size() > 1 时,将原先针对 AttentionBackendEnum.FA / SAGE_ATTN 的枚举白名单校验替换为 if not attn_backend.supports_ring_rotation(): raise RuntimeError,错误信息现在解释需要 kernel 暴露 softmax LSE,便于定位。

  4. 镜像字符串名单python/sglang/multimodal_gen/runtime/server_args/server_args.py 新增模块级常量 RING_CAPABLE_ATTENTION_BACKENDS = ("fa", "sage_attn")_adjust_attention_backend 的 ring 分支改为引用该常量并增强错误消息(列出允许后端),自动选择默认后端由硬编码 "fa" 改为 RING_CAPABLE_ATTENTION_BACKENDS[0],日志改为参数化输出。

  5. 降级可见性与测试保障python/sglang/multimodal_gen/runtime/models/dits/zimage.pyuse_full_unified_sequencesp>1 且 ring>1)分支新增 logger.warning_once,提示 fallback 到全序列 K/V gather 放弃 ring 的显存与重叠收益;新增 test/unit/test_ring_admission.py,用三个用例分别固定默认/SDPA 不声明、FA 声明、以及 RING_CAPABLE_ATTENTION_BACKENDS 与后端类能力互相一致,防止双轨漂移。

测试配套共新增 39 行单测;无配置文件、schema 或部署脚本改动,server_args 仅内部常量集中。

文件 模块 状态 重要度
python/sglang/multimodal_gen/runtime/layers/attention/backends/attention_backend.py 注意力后端 modified 6.39
python/sglang/multimodal_gen/runtime/layers/attention/layer.py 注意力层 modified 6.28
python/sglang/multimodal_gen/runtime/server_args/server_args.py 服务参数 modified 6.14
python/sglang/multimodal_gen/runtime/layers/attention/backends/flash_attn.py 注意力后端 modified 5.93
python/sglang/multimodal_gen/runtime/layers/attention/backends/sage_attn.py 注意力后端 modified 5.36
python/sglang/multimodal_gen/runtime/models/dits/zimage.py 模型管线 modified 5.08
python/sglang/multimodal_gen/test/unit/test_ring_admission.py 单元测试 added 6.84

关键符号

AttentionBackend.supports_ring_rotation FlashAttentionBackend.supports_ring_rotation SageAttentionBackend.supports_ring_rotation USPAttention.__init__ ServerArgs._adjust_attention_backend

关键源码片段

python/sglang/multimodal_gen/runtime/layers/attention/backends/attention_backend.py core-logic

定义本次重构的能力契约 supports_ring_rotation(),是后续所有后端声明支持的统一入口。

class AttentionBackend(ABC):
    """Abstract class for attention backends."""
​
    # 部分后端会在调用自定义 op 前预先分配输出张量,
    # 在 piecewise cudagraph 场景下可保证输出分配发生在 cudagraph 内部。
    accept_output_buffer: bool = False
​
    @classmethod
    def supports_packed_varlen(cls) -> bool:
        # 判断 impl 是否覆写了 varlen 前向,供能力准入使用。
        return cls.get_impl_cls().forward_varlen is not AttentionImpl.forward_varlen
​
    @classmethod
    def supports_ring_rotation(cls) -> bool:
        """后端能否承担 Ring Attention 的 kernel 角色。        ring 的逐跳 online-softmax 合并需要 kernel 的 softmax LSE,
        因此“是否支持”由后端自行声明,而非调用方枚举后端名字。
        默认不声明(返回 `False`);提供 LSE 的后端覆写为 `True` 即可。
        """
        return False
python/sglang/multimodal_gen/runtime/layers/attention/layer.py core-logic

USPAttention 初始化 gate 从枚举白名单切换为能力查询,是消费端核心改动,决定 ring 参数下后端准入行为。

        dtype = get_compute_dtype()
        attn_backend = get_attn_backend(
            head_size, dtype, supported_attention_backends=supported_attention_backends
        )
        if get_ring_parallel_world_size() > 1:
            # 逐跳 online-softmax 合并依赖 kernel 的 softmax LSE,
            # 因此不再枚举 FA / SAGE_ATTN 白名单,改为查询能力声明;
            # 未声明的后端在初始化时直接 fail early,便于定位。
            if not attn_backend.supports_ring_rotation():
                raise RuntimeError(
                    "Ring Attention requires a backend whose kernel exposes the "
                    "softmax LSE for the per-hop merge; "
                    f"{attn_backend.get_enum().name} does not declare support "
                    "(see AttentionBackend.supports_ring_rotation)."
                )
        impl_cls = attn_backend.get_impl_cls()
python/sglang/multimodal_gen/runtime/server_args/server_args.py configuration

字符串级 ring 准入名单集中为 RING_CAPABLE_ATTENTION_BACKENDS 镜像常量,增强自动选择与报错逻辑,是双轨一致性的另一端。

# 镜像 AttentionBackend.supports_ring_rotation 的字符串级名单。
# 字符串级检查早于后端类的可导入时机(部分平台无法 import 后端类),
# 因此保留在 server_args;由单元测试锁定它与类能力声明的一致性。
RING_CAPABLE_ATTENTION_BACKENDS = ("fa", "sage_attn")
​
​
def _adjust_attention_backend(self) -> None:
    # ... 其他 backend 归一化逻辑 ...
​
    if self.ring_degree > 1:
        if (
            self.attention_backend is not None
            and self.attention_backend not in RING_CAPABLE_ATTENTION_BACKENDS
        ):
            raise ValueError(
                "Ring Attention requires one of the ring-capable backends "
                f"({', '.join(RING_CAPABLE_ATTENTION_BACKENDS)}), got "
                f"{self.attention_backend!r}"
            )
        if self.attention_backend is None:
            # 自动选择名单首项(当前为 "fa"),不再硬编码字符串。
            self.attention_backend = RING_CAPABLE_ATTENTION_BACKENDS[0]
            logger.info(
                "Ring Attention requires a ring-capable backend; "
                "attention_backend has been automatically set to %s",
                self.attention_backend,
            )

评论区精华

CI 失败与 main 上 #33923 的级联问题 测试

作者在 Issue 评论中指出 multimodal-gen-test-2-gpu (0) 失败是 fast-fail 级联,根因是 #33923 合并的 masked-path guard 误拒绝合法单 rank 调用,qwen 的 1-GPU masked 家族在每条分支路径后都会抛 NotImplementedError,与本 PR 无关。

结论:修复由 #33953(把 guard 限定到 sequence-parallel 运行)提供;合并后 rebase/rerun 即可转绿,本 PR 无需改动。 · 已解决

ring 准入采用后端能力自声明而非调用点白名单 设计

PR body 说明 ring 逐跳 online-softmax 合并需要 kernel 的 softmax LSE,因此后端声明能力而不是调用点命名它们,沿用 #33707 packed-varlen 准入的模式;server_args 字符串名单保留但集中为镜像常量并配测试防漂移。

结论:采用 AttentionBackend.supports_ring_rotation() 能力契约,字符串层名单用测试固定一致,新后端只需覆写一个类方法即可接入。 · 已解决

风险与影响

  • 双轨漂移风险:能力方法(类级)与 RING_CAPABLE_ATTENTION_BACKENDS(字符串级)两处维护,新增测试只固定了 FA 在名单内、SDPA 不在名单内,未显式固定 sage_attn;若 SageAttentionBackend 未来改变声明,测试不会立即暴露。
  • fail-early 行为变化layer.py 的 gate 从枚举白名单改为默认 False 的能力查询,任何未覆写 supports_ring_rotation() 但实际具备 LSE 的后端都会在初始化时抛 RuntimeError。这是更安全的失败方向,但属于行为变更,第三方后端作者需同步覆写。
  • 默认值依赖常量首项server_args 自动回退从硬编码 "fa" 改为 RING_CAPABLE_ATTENTION_BACKENDS[0],若未来常量顺序调整(如把 sage_attn 放前面),自动启用的默认后端会改变,测试未断言顺序。
  • 无回归风险的表现:SDPA 本来就不是 ring 后端,默认 False 与旧行为一致;FA/Sage 行为不变;zimage 仅新增日志,无张量路径变化。
  • 用户侧:配置 ring_degree > 1 且后端非法时,错误信息现在明确列出允许的后端列表,更易排查;zimage 在 ring 模式下出现一次性 warning,用户能感知到实际走的是全序列 K/V gather 降级路径。
  • 系统侧:改动全部集中在 multimodal_gen 子模块的注意力后端与 server_args,SRT 主推理路径不受影响;新后端接入 ring 只需覆写一个类方法,不再需要修改 layer.py 或 server_args 白名单。
  • 团队侧:与 #33707 的能力自声明模式形成统一惯例,后续新增 packed-varlen、ring 等能力时按同一方式扩展;双轨一致性由单测兜底,降低维护两套名单的心理负担。影响范围小但模式示范意义强。
能力与字符串名单双轨一致性依赖测试 sage_attn 未纳入一致性测试 gate 改为 fail-early 能力检查 默认 ring 后端取自常量首项

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论