Prhub

#33065 [Fix] Honor FlashMLA natural-log LSE in DCP reduction

原始 PR 作者 CyberSecurityErial 合并时间 2026-08-04 10:55 文件变更 4 提交数 1 评论 7 代码增减 +63 / -9

执行摘要

修复 FlashMLA 自然对数 LSE 在 DCP 合并中的误算

关联 Issue #33064 明确指出:FlashMLA 返回的 softmax LSE 是 natural-log 域,而 MLA DCP reduction 按 base-2 合并,"This is not only a numerical-stability or performance issue. It changes the model output." 数值例证:Z=[2, 8]、第二分片输出 10 时正确贡献应为 8,按 exp2 合并得到 7.2330317。PR body 说明目标是在不改变 FlashInfer MLA 行为的前提下修正 FlashMLA + DCP 的输出正确性。

值得精读。三个亮点:一是用 tl.constexpr 把运行时逐元素分支变成编译期特化,兼顾正确性与性能;二是数值例证的测试设计(log(2)、log(8) 与 10 组装出 8.0)极简且直击根因;三是 _is_mla_dcp_lse_base_on_e 这种集中式后端契约函数,可作为同类'后端行为差异'问题的处理样板。建议留意未来新增 MLA attention 后端时同步维护该映射。

讨论亮点

kpham-sgl 审核通过("LGTM. Can you resolve conflicts"),随后作者在 PR 评论区说明 rebase 后的语义漂移检查(git diff --check、py_compile、range-diff),并请求补充 run-ci 标签重跑 CI;H20 节点可用后作者重跑完整回归文件,23 个测试全部通过。CI 路由输出显示 1-gpu-h100 上 23 个测试全部通过。整个审阅没有针对实现本身的反对意见,冲突解决后直接合并。

实现拆解

  1. 新增后端契约函数:在 python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mla.py 定义 _is_mla_dcp_lse_base_on_e(attention_backend),仅当注意力后端为 flashmla 时返回 True(自然对数 LSE),其余后端保持 base-2;该函数将'LSE 对数域是后端能力'这一隐含假设显式化为单一契约点。
  2. Triton 校正 kernel 支持对数底选择:在 python/sglang/kernels/ops/attention/dcp_kernels.py 中给 _correct_attn_cp_out_kernel 增加 IS_LSE_BASE_ON_E: tl.constexpr 编译期常量,让 exp/logexp2/log2 在编译期特化二选一;correct_attn_out 新增同名形参并放入 const_args,避免运行时逐元素分支。
  3. flag 贯穿两条 DCP 合并路径:在 python/sglang/srt/layers/dcp/comm.pycp_lse_ag_out_rs_mla 新增 is_lse_base_on_e=False 参数并透传给 correct_attn_out;在 forward_absorb_core 中,A2A/fi_a2a 路径的 dcp_a2a_lse_reduce 与 AG/RS 路径的 cp_lse_ag_out_rs_mla 都改用统一计算出的 is_lse_base_on_e,删除了原先写死 False 的注释。
  4. 测试配套:在 test/registered/kernels/test_dcp_lse_combine.py 新增 test_flashmla_natural_log_lse_correction(核级回归,复现 issue 的 log(2)、log(8) 与输出 10 合成结果为 8.0 的数值例子)与 test_flashmla_selects_natural_log_lse(路由测试,断言 _is_mla_dcp_lse_base_on_e 对 flashmla 与 flashinfer_mla 的选择);在 H20 上 23 个测试全部通过。
  5. 合入流程:作者 rebase 到最新 main,采用上游 get_parallel().dcp_comm_backend 访问器解决冲突;补充 run-ci 标签后 CI 在 1-gpu-h100 上通过 23 个测试,由 kpham-sgl 合并。
文件 模块 状态 重要度
python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mla.py MLA 前向 modified 6.77
python/sglang/srt/layers/dcp/comm.py DCP 通信 modified 5.45
python/sglang/kernels/ops/attention/dcp_kernels.py Triton 内核 modified 4.88
test/registered/kernels/test_dcp_lse_combine.py DCP 测试 modified 5.64

关键符号

_is_mla_dcp_lse_base_on_e cp_lse_ag_out_rs_mla correct_attn_out _correct_attn_cp_out_kernel test_flashmla_natural_log_lse_correction test_flashmla_selects_natural_log_lse

关键源码片段

python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mla.py data-contract

引入 `_is_mla_dcp_lse_base_on_e` 后端契约函数,决定 DCP 合并时 LSE 对数底,并同时作用于 A2A 与 AG/RS 两条路径,是本次修复的策略中枢。

# python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mla.pydef _is_mla_dcp_lse_base_on_e(attention_backend: Optional[str]) -> bool:
    # FlashMLA 暴露自然对数 softmax LSE;FlashInfer MLA 及其他当前支持的
    # MLA DCP decode 后端暴露 base-2 LSE,所以只有 flashmla 需要 e 为底。
    return attention_backend == "flashmla"
​
​
# forward_absorb_core 中,DCP decode 合并路径的两条分支共用同一个 flag:
if _is_dcp_mla_decode_phase(forward_batch):
    attn_output = attn_output.view(
        -1,
        self.num_local_heads * get_parallel().attn_dcp_size,
        self.kv_lora_rank,
    )
    dcp_comm_backend = get_parallel().dcp_comm_backend
    # 根据当前 attention 后端决定 LSE 对数域,替换原先写死的 False
    is_lse_base_on_e = _is_mla_dcp_lse_base_on_e(self.current_attention_backend)
    if dcp_comm_backend in ("a2a", "fi_a2a"):
        # A2A 交换 head 部分和 + LSE,再本地 Triton 合并
        attn_output = dcp_a2a_lse_reduce(
            attn_output.contiguous(),
            lse.contiguous(),
            get_parallel().dcp_group,
            is_lse_base_on_e=is_lse_base_on_e,
            comm_backend=dcp_comm_backend,
        )
    else:
        # all-gather LSE + reduce-scatter 输出,同样传递该 flag
        attn_output = cp_lse_ag_out_rs_mla(
            attn_output,
            lse,
            get_parallel().dcp_group,
            is_lse_base_on_e=is_lse_base_on_e,
        )
        attn_output = attn_output.transpose(0, 1)
python/sglang/kernels/ops/attention/dcp_kernels.py infrastructure

Triton 校正 kernel 的核心修改:用编译期常量选择 exp/log 与 exp2/log2,保证自然对数 LSE 权重计算正确且无运行时开销。

# python/sglang/kernels/ops/attention/dcp_kernels.py# 校正 kernel 新增编译期常量 IS_LSE_BASE_ON_E,让 exp/log 与 exp2/log2 的选择
# 发生在编译期(Triton 特化),不会引入任何运行时逐元素分支开销。
@triton.jit
def _correct_attn_cp_out_kernel(
    ...,
    HEAD_DIM: tl.constexpr,
    N_ROUNDED: tl.constexpr,
    IS_LSE_BASE_ON_E: tl.constexpr,
):
    ...
    lse = lse - lse_max
    # 自然对数 LSE 用 exp/log;base-2 LSE 用 exp2/log2
    lse_exp = tl.exp(lse) if IS_LSE_BASE_ON_E else tl.exp2(lse)
    lse_acc = tl.sum(lse_exp, axis=0)
    final_lse = (tl.log(lse_acc) if IS_LSE_BASE_ON_E else tl.log2(lse_acc)) + lse_max
​
    # 每个 head 的加权因子同样按同一底计算
    lse_diff = tl.where(
        lse_idx...,
        lse - final_lse,
        neg_inf,
    )
    factor = tl.exp(lse_diff) if IS_LSE_BASE_ON_E else tl.exp2(lse_diff)
​
​
# 入口 correct_attn_out 把调用方传入的 is_lse_base_on_e 放进 const_args
def correct_attn_out(..., is_lse_base_on_e: bool = False):
    ...
    const_args = {
        "HEAD_DIM": D,
        "N_ROUNDED": N,
        "IS_LSE_BASE_ON_E": is_lse_base_on_e,
    }
    ctx.call_kernel(_correct_attn_cp_out_kernel, grid, *regular_args, **const_args)
    return new_output, lse

评论区精华

跨 rank LSE 合并时对数域不一致 正确性

Issue #33064 用数值例子证明:两分片 partition function Z=[2, 8]、第二分片局部输出为 10 时,正确贡献应为 8;把 ln(2)、ln(8) 按 exp2 合并会得到 7.2330317。受影响路径包括 AG/RS 的 correct_attn_out(kernel 无条件使用 tl.exp2/tl.log2)和 A2A 的 dcp_a2a_lse_reduce(对 FlashInfer 与 FlashMLA 都传 is_lse_base_on_e=False)。

结论:通过新增编译期 IS_LSE_BASE_ON_E 常量与后端路由函数 _is_mla_dcp_lse_base_on_e,仅 flashmla 使用自然对数,默认保持 base-2 兼容 FlashInfer。 · 已解决

冲突解决与 run-ci 门禁 other

kpham-sgl 批准但要求解决冲突;作者 rebase 到 main 并采用上游 get_parallel().dcp_comm_backend 访问器,仅保留 flag 传播,随后请求补充 run-ci 标签重新触发 CI。

结论:rebase 后在 H20 重跑 23 个测试全部通过,CI 在 1-gpu-h100 上通过,PR 由 kpham-sgl 合并。 · 已解决

风险与影响

  1. 后端契约硬编码_is_mla_dcp_lse_base_on_e 通过字符串匹配 flashmla 选择自然对数,未来新增返回自然对数 LSE 的 MLA 后端而忘记更新该函数时,会再次静默算错;建议后续以 attention backend 的能力元数据形式维护该契约。
  2. 默认值依赖correct_attn_outcp_lse_ag_out_rs_mladcp_a2a_lse_reduce 的默认值均为 base-2(False),其他调用点若未显式传参且后端为自然对数域,则仍会误算;本 PR 只覆盖了 MLA DCP 的已知两条路径。
  3. 验证范围有限:GPU 回归仅在单张 H20(SM90)上执行,未覆盖多卡 DCP 真实集合通信场景与其他 FlashMLA 运行架构(如 SM100/SM120),也未见覆盖 A2A 合并非的独立回归。

用户影响:修复 FlashMLA + DCP decode 场景下的静默输出错误,属于正确性修复;性能无影响,因为 log-base 分支是 tl.constexpr 编译期特化,不产生运行时开销。系统影响:默认路径(FlashInfer MLA)行为完全不变;仓库新增一个集中式 backend-to-LSE-base 映射,为后续 MLA 后端接入提供契约点。团队影响:将'LSE 对数域是后端能力'的隐含假设显式化,测试文件为后续变更提供回归基线。

后端契约硬编码 默认 base-2 隐式假设 未覆盖多卡 DCP

关联 Issue

#33064 [Bug] FlashMLA DCP combines natural-log LSE with base-2 correction

完整报告

参与讨论