Prhub

#27120 Fix hybrid linear attention dispatch by layer id with draft-worker awareness

原始 PR 作者 ch-wan 合并时间 2026-06-04 05:11 文件变更 3 提交数 2 评论 4 代码增减 +5 / -23

执行摘要

修复混合注意力层调度,替代临时标记方案

HybridLinearAttnBackend._is_full_attn 原先通过 isinstance(layer, RadixLinearAttention) 判断线性层,但像 Bailing / Ring 这类混合模型(见 issue #26623)的线性层封装在普通 RadixAttention 中,会导致误判。此前社区尝试在 Bailing 模型中设置 self.attn._is_linear_attention = True 作为临时标记(PR #26623),但该方案被认定为 workaround 并已回退(#27116)。因此需要更根本的修复方案。

此 PR 属于“有意义的改进”(bugfix + 清理临时方案),值得精读。重点关注 _is_full_attn 的简化过程和草稿 worker 的特例处理。建议后续为混合注意力调度添加专用测试用例,覆盖草稿 worker 和非草稿场景。

讨论亮点

没有 review 评论线程。PR 作者在 body 中详细解释了核心设计权衡:type-based dispatch 不适用于包装了普通 RadixAttention 的线性层(如 Bailing/Ring),而纯 layer-id 方案在草稿 worker 场景下会出错(draft model 的 layer_id 全为 0,但目标模型的 full_attention_layer_ids 可能包含多个非 0 ID),因此需要在 registry 层对 draft worker 做特殊处理。PR 明确标注 FIXME: we assume that MTP/NEXTN always use full-attention.

实现拆解

  1. hybrid_linear_attn_backend.py:重写 _is_full_attn 方法,删除 isinstance(layer, RadixLinearAttention) 短路分支,删除针对 _is_linear_attention 标记的检查,也删除了 isinstance(layer, RadixAttention) 全量默认分支。新逻辑完全依赖 layer_id in self.full_attn_layers 进行调度。
  2. bailing_moe_linear.py:移除 self.attn._is_linear_attention = True 标记行,因为 dispatch 不再依赖该属性。
  3. attention_registry.py:在 attn_backend_wrapper 中,当 runner.is_draft_worker 为真时(MTP/NEXTN 草稿模型),将 full_attn_layers 强制设为 [0],与 draft worker 的 KV 池构建保持一致(full_attention_layer_ids=[0])。非草稿 worker 仍使用配置文件中的 cfg.full_attention_layer_ids
文件 模块 状态 重要度
python/sglang/srt/layers/attention/hybrid_linear_attn_backend.py 注意力调度 modified 6.64
python/sglang/srt/models/bailing_moe_linear.py 模型定义 modified 5.4
python/sglang/srt/layers/attention/attention_registry.py 注意力调度 modified 6.04

关键符号

_is_full_attn attn_backend_wrapper BailingMoeLinear.__init__

关键源码片段

python/sglang/srt/layers/attention/hybrid_linear_attn_backend.py dependency-wiring

核心调度逻辑所在文件,删除 16 行冗余检查,将 _is_full_attn 简化为纯 layer-id 判断。

def _is_full_attn(
    self, layer: Optional[RadixAttention], layer_id: Optional[int] = None
) -> bool:
    # 从 layer 对象中提取 layer_id(如果提供),否则使用传入的 layer_id
    if layer is not None:
        layer_id = layer.layer_id
    # 必须有 layer_id,否则无法调度
    assert layer_id is not None, "either layer or layer_id must be provided"
    # 仅通过 layer_id 是否在 full_attn_layers 列表中判断是否为全量注意力层
    return layer_id in self.full_attn_layers
python/sglang/srt/layers/attention/attention_registry.py core-logic

草稿 worker 特例处理入口,强制设置 full_attn_layers=[0] 以确保 dispatch 与 KV 池构建一致。

# 在 attn_backend_wrapper 函数中,线性注意力后端构造完毕后:
if runner.is_draft_worker:
    # FIXME: 目前假设 MTP/NEXTN 草稿模型总是使用全量注意力
    full_attn_layers = [0]
else:
    full_attn_layers = cfg.full_attention_layer_ids
return HybridLinearAttnBackend(
    full_attn_backend, linear_attn_backend, full_attn_layers
)

评论区精华

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

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

风险与影响

  1. 非混合模型(如纯全量注意力模型)不会走进 HybridLinearAttnBackend 分支,因此不受影响。
  2. 混合模型且非草稿 worker 场景:调度行为与原方案对 layer_id 在配置中的层一致;原先通过 RadixLinearAttention 类型走的层现在是纯 layer-id 匹配,结果相同。
  3. 草稿 worker 场景:强制 full_attn_layers=[0] 是硬编码假设(MTP/NEXTN 总是全量注意力);若未来出现包含线性层的草稿模型,该假设可能失效,届时需调整。
  4. 当前 MR 未新增单元测试,回归风险依赖手动 CI 验证(已通过 ring_2_5_1t.py 和 ling_2_6_flash.py 测试)。

影响范围:仅涉及使用混合线性注意力后端的模型(GPD、KDA、LightningAttention、Mamba2、Bailing/Ring 等)。对用户:修复了 Bailing/Ring 等模型线性层被误路由为全量 Attention 的问题;用户无需修改配置。对系统:调度逻辑简洁化,性能无影响(layer-id 检查仅为列表成员判断,开销可忽略)。对团队:移除了临时标记 workaround,代码可维护性提升。

缺少测试覆盖 草稿模型假设硬编码

关联 Issue

#27116 Revert "Fix hybrid linear attention misrouting plain-RadixAttention linear layers to the full backend (Ring-2.5-1T)"

完整报告

参与讨论