Prhub

#34432 [AMD][DCP 1/N] add dcp support for aiter backend

原始 PR 作者 billishyahao 合并时间 2026-09-12 01:35 文件变更 9 提交数 13 评论 16 代码增减 +549 / -78

执行摘要

aiter 后端接入 DCP,Kimi-K3 在 MI355X 跑通 tp8/dcp8

PR body 明确说明本补丁是 https://github.com/sgl-project/sglang/pull/32796 的 1/N,目标是 “enable dcp support for aiter backend”,并给出 Kimi-K3 + --tp-size 8 --dcp-size 8 --prefill-attention-backend aiter --decode-attention-backend aiter --kv-cache-dtype fp8_e4m3 --context-length 132000 的示例启动命令。长上下文下单卡放不下完整 KV,DCP 把 KV 按 rank 分片,每个 rank 只需持有约 seq_len/dcp_size;而 aiter 后端此前缺少 LSE 返回、分片索引改写与 verify 两段合并能力,无法直接复用已有的 DCP 框架。同时 kimi_k3.py 的 DCP 分支原本只接受 cutedsl_mla / tokenspeed_mla,AMD 平台必须显式放行 aiter。

值得精读,尤其是负责 AMD 注意力后端、DCP/长上下文分片或 Kimi-K3 部署的工程师。建议重点看三处设计决策:(1) _forward_verify_dcp() 把 target verify 拆成「本 rank 前缀 + 仅 rank 0 计算 draft window + 本地 merge_state_triton」的两段式结构,以及为绕开 varlen 索引重复而引入的 2-D token table 与 use_2d_view;(2) aiter DCP 选择自然对数 LSE 并通过 is_mla_dcp_lse_base_on_e() 固化底数约定,直接影响跨 rank 归约的正确性;(3) head 数记账从单 rank 的 num_head_padded 拆成 gather 后的 mla_kernel_num_head_padded,以及随之放宽的断言带来的风险面。同时请把「无自动化测试 + CI 未跑到 DCP 路径」视为后续必须补齐的债务:任何在此之上继续开发的改动,都应先补一个能在 CI 中真正执行 --dcp-size > 1 的注册测试,再动这些路径。

讨论亮点

1) DSPARK 覆盖范围:HaiShaw 在 triton_backend.py:234 追问 “Only dspark covered?”,作者承认目前只验证了 Kimi-K3-DSpark,另提到存在 lightseekorg/kimi-k3-eagle3-mla 但未适配,HaiShaw 回复 “fine, you may do a follow up issue/pr later”,即 EAGLE3 的 DCP draft 处理留作后续 PR。
2) eager_runner.py 的 forward_batch.extend_prefix_lens is not None 追加检查:HaiShaw 质疑 “- bug fix?”,作者确认这是公共路径上的真实 bug(在 NVIDIA DCP 上也能复现),并拆出 https://github.com/sgl-project/sglang/pull/38991 单独跟踪,本 PR 内该改动通过 commit 59613d3 “restore eager_runner fix” 还原,最终 diff 中不再包含 eager_runner.py。
3) CI 覆盖争议:amd-bot 给出 “Merge verdict: ❌ Do not merge”,指出新增的 DCP 代码在 aiter_backend.py 中引入 17 处被禁用的 legacy parallel-getter 调用导致 ratchet 测试失败,同时强调 “PR CI never exercised the DCP feature this PR adds”——新代码全部被 dcp_enabled()(--dcp-size > 1)与 is_hip() 门控,AMD pipeline 的 stage-b/stage-c 模型与注意力任务因 wait-for-stage-a-amd-rocm720 等待约 4 小时后超时被 fast-fail 跳过。作者修复并多次 /rerun-failed-ci 后,HaiShaw 结论为 “CI signal is ok”,review 意见为 “Aiter specific extensions, LGTM.”。
4) LSE 底数约定:从 commit “use natural log” 与 is_mla_dcp_lse_base_on_e() 新增 "aiter" 可以看出,aiter DCP 返回自然对数 LSE,跨 rank 归约必须走 exp/log 而非 exp2/log2;这一约定与 flashmla、cutedsl_mla 保持一致,否则合并结果会出现系统性数值偏差。

实现拆解

  1. 参数解析与后端选择(前置门控):arg_groups/model_overrides/kimi_k3.py 新增 _require_kimi_k3_aiter_gluon_dcp_support(),在 _kimi_k3_overrides() 的 dcp_size > 1 分支里加入 decode_backend == "aiter" 分支:先校验 Gluon MLA 内核是否可用,再把 prefill 默认也切成 aiter(仅当用户显式写了 triton 才保留),并把 AssertionError 文案扩展为三种后端。attention_backend_handler.py 的 handle_attention_aiter() 在 DCP 下把 extend 直接路由为 MHA_ONE_SHOT,因为该路径会 all-gather 完整序列 KV,正是 DCP prefill 所需;forward_mla.py 的 is_mla_dcp_lse_base_on_e() 把 aiter 加入集合,声明 aiter DCP 返回自然对数 LSE,跨 rank 归约必须用 exp/log。
  2. aiter 后端的 DCP 元数据层:layers/attention/aiter_backend.py 中 ForwardMetadata 新增 local_kv_lens 与 verify_token_table(每 forward 构建一次而非每层),__init__() 记录 dcp_world_size 与 kv_index_translator;_plan_dcp_decode_metadata() 就地改写 kv_indptr/kv_indices,把索引翻译成本 rank 分片的物理槽位;_build_dcp_local_kv_lens() 与 _build_dcp_verify_token_table() 分别产出每个请求的分片 KV 长度和 target verify 用的 2-D token table;_get_dcp_graph_max_local_kv_len() 给出 cuda graph 可见的静态上界 ceil(max_context_len / dcp_size);init_cuda_graph_state() 预分配 verify token table 与长度缓冲。head 数记账拆成单 rank 的 num_head_padded 与 gather 后的 mla_kernel_num_head_padded,head 数断言在 dcp_world_size > 1 时放宽。
  3. decode 与 target verify 两条计算路径:_forward_decode_dcp() 用 mla_gluon_decode(..., return_lse=True) 只对本 rank 的 KV 分片做注意力并返回自然对数 LSE;_forward_verify_dcp() 分两段执行——stage 1 通过 verify_token_table + use_2d_view=True 读已提交前缀,stage 2 只由 rank 0 执行(draft window k_window 各 rank 相同),最后用 merge_state_triton 本地合并两段 LSE。配套地,aiter_mla_gluon.py 的 mla_gluon_decode() 新增 return_lse 与 use_2d_view 两个默认保持旧行为的参数,并在返回 lse 时保持调用方原有的 flat 输出布局。
  4. 模型前向侧接线:models/deepseek_common/attention_forward_methods/forward_mla_rocm.py 增加 force_rope_for_aiter_dcp_decode——DCP 下 aiter 融合 MLA 内核不生效、Gluon 内核不施加 RoPE,因此 decode / target verify / draft extend v2 必须在 prepare 阶段补 RoPE;forward_absorb_rocm_core() 新增 aiter + DCP 分支:拼接 q、用 set_mla_kv_buffer() 写 KV(该函数自带 owner 过滤与分片除法,故 out_cache_loc 原样传入)、target verify 时把 k_window/v_window 交给 attn_mqa_for_dcp_decode() 取回 (attn_output, lse);extend 分支仅在 attn_dcp_metadata 与 dcp_kv_buffer 都存在时才做 all-gather。
  5. 横切修正与配套:forward_mha_rocm.py 的 _get_mla_kv_buffer_rocm() 删除 filter_dcp_local_kv_indices() 与 translate_dcp_read_ids(),避免过滤器与分片除法被执行第二次(NV CUDA 版本本来就没有这两步);layers/dcp/comm.py 的 all_gather_kv_cache_for_mla_extend() 在 HIP 上前缀长度为 0 时跳过 all-gather,规避 grid size 0 导致的 invalid configuration;layers/attention/triton_backend.py 为 HIP + DSPARK draft worker 强制 dcp_size=1/dcp_rank=0,因为 dspark_kv_inject 用原始 cache_loc 写 draft KV,各 rank 都持有完整副本。测试配套方面本 PR 没有新增任何注册测试(checklist 中 unit tests 与 documentations 两项未勾选),准确率数据来自 8×MI355X 上人工执行的 gsm8k 200 评测。
文件 模块 状态 重要度
python/sglang/srt/layers/attention/aiter_backend.py 注意力后端 modified 8.84
python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mla_rocm.py 模型前向 modified 7.33
python/sglang/srt/arg_groups/model_overrides/kimi_k3.py 参数覆盖 modified 7.12
python/sglang/srt/layers/attention/aiter_mla_gluon.py 注意力内核 modified 6.52
python/sglang/srt/layers/dcp/comm.py 集合通信 modified 6.45
python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mha_rocm.py 多头前向 modified 6.15
python/sglang/srt/layers/attention/triton_backend.py 后端适配 modified 6.08
python/sglang/srt/models/deepseek_common/attention_backend_handler.py 后端选择 modified 5.1
python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mla.py 后端判定 modified 4.73

关键符号

_forward_decode_dcp _forward_verify_dcp _plan_dcp_decode_metadata _build_dcp_local_kv_lens _build_dcp_verify_token_table _get_dcp_graph_max_local_kv_len mla_gluon_decode all_gather_kv_cache_for_mla_extend forward_absorb_rocm_prepare forward_absorb_rocm_core _get_mla_kv_buffer_rocm _require_kimi_k3_aiter_gluon_dcp_support _kimi_k3_overrides handle_attention_aiter is_mla_dcp_lse_base_on_e TritonAttnBackend.__init__

关键源码片段

python/sglang/srt/layers/attention/aiter_backend.py core-logic

DCP 支持的主体:新增 _forward_decode_dcp/_forward_verify_dcp 两条分片计算路径、DCP 元数据规划(_plan_dcp_decode_metadata/_build_dcp_local_kv_lens/_build_dcp_verify_token_table/_get_dcp_graph_max_local_kv_len)、ForwardMetadata 的 local_kv_lens/verify_token_table 字段,以及 head 数记账与断言放宽,是整条链路的汇聚点。

# aiter_backend.py:DCP 下 decode 与 target verify 的核心实现(重新整理后的完整片段)
​
def _get_dcp_graph_max_local_kv_len(self) -> int:
    """cuda graph 能看到的本 rank 分片长度上界:ceil(max_context_len / dcp_size)。"""
    w = max(self.dcp_world_size, 1)
    return (self.max_context_len + w - 1) // w
​
​
def _forward_decode_dcp(self, q, k_buffer, layer, k_descale):
    """只对本 rank 的 KV 分片做 MLA decode,返回 (out, 自然对数 lse)。
​
    跨 rank 的 LSE 合并由调用方负责;这里刻意不复用非 DCP 的
    mla_decode_fwd 路径,因为那条路径不返回 LSE。
    """
    fm = self.forward_metadata
    bs = fm.kv_indptr.shape[0] - 1
    num_heads = layer.tp_q_head_num # 已 all-gather,等于 local_heads * dcp_size
​
    out, lse = mla_gluon_decode(
        q=q.view(bs, num_heads, layer.qk_head_dim),
        k_buffer=k_buffer,
        layer=layer,
        # kv_indices/kv_indptr 已被 _plan_dcp_decode_metadata 改写成
        # 「本 rank 分片 + 物理槽位」,这里直接使用
        kv_indices=fm.kv_indices,
        kv_indptr=fm.kv_indptr[: bs + 1],
        sm_scale=layer.scaling,
        kv_scale=self._resolve_fp8_kv_scale_float(layer, k_descale),
        min_kv_seq_len=1,
        return_lse=True, # DCP 必须拿回 LSE,否则无法做跨 rank log-sum-exp 合并
    )
    return out, lse.view(bs, num_heads)
​
​
def _forward_verify_dcp(self, q, k_window, layer, k_descale):
    """把已提交前缀与 verify 窗口分两段计算后本地合并 -> (out, 自然对数 lse)。
​
    拆成两段是为了绕开 decode 内核在 DCP 下唯一做不到的事:
    按全局位置 g(j) = j * W + r 施加掩码。
    """
    fm = self.forward_metadata
    q_len = fm.max_q_len
    num_heads = layer.tp_q_head_num # gathered heads = num_local_heads * dcp
    seqused_k = fm.local_kv_lens # 每个请求在本 rank 上的分片 KV 长度
    n_rows = seqused_k.shape[0]
    bs = n_rows // q_len
​
    # 第一段:本 rank 已提交前缀,用 2-D token table 逐行读取
    out_a, lse_a = mla_gluon_decode(
        q=q.view(n_rows, num_heads, layer.qk_head_dim),
        k_buffer=self.token_to_kv_pool.get_key_buffer(layer.layer_id),
        layer=layer,
        kv_indices=fm.verify_token_table,
        kv_indptr=seqused_k,
        sm_scale=layer.scaling,
        kv_scale=self._resolve_fp8_kv_scale_float(layer, k_descale),
        min_kv_seq_len=1,
        return_lse=True,
        # varlen 形式会对同一请求的每个 query 重复同一份 index 列表,
        # 所以 verify 必须走按行的 2-D 视图
        use_2d_view=True,
    )
    lse_a = lse_a.view(n_rows, num_heads)
​
    # verify 窗口 k_window 由本次 forward 计算且每个 rank 完全相同,
    # 只让 rank 0 参与计算,其余 rank 直接返回前缀部分等待跨 rank 合并
    if get_parallel().attn_dcp_rank != 0:
        return out_a, lse_a
​
    # 窗口 latent 按请求优先且连续,它本身就是 KV 池:请求 b 的第 i 行位于
    # b * q_len + i。mla_gluon 在 seq_len == qlen 时的 MTP mask 恰好就是
    # 这里需要的稠密因果窗口,因此可以直接复用同一内核。
    out_b, lse_b = mla_gluon_decode(
        q=q.view(n_rows, num_heads, layer.qk_head_dim),
        k_buffer=k_window,
        layer=layer,
        kv_indices=torch.arange(n_rows, dtype=torch.int32, device=q.device),
        kv_indptr=torch.arange(bs + 1, dtype=torch.int32, device=q.device) * q_len,
        sm_scale=layer.scaling,
        min_kv_seq_len=1,
        qlen=q_len,
        return_lse=True,
    )
    # 两段结果都在自然对数域,直接用 triton 版 merge 合并,避免 log2/exp2 混用
    return merge_state_triton(out_a, lse_a, out_b, lse_b.view(n_rows, num_heads))
python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mla_rocm.py data-contract

模型前向侧的接线点:新增 force_rope_for_aiter_dcp_decode 让 DCP 下的 decode/target verify/draft extend 在 prepare 阶段补 RoPE,并在 forward_absorb_rocm_core 里新增 aiter + DCP 分支,用 set_mla_kv_buffer 写 KV、把 k_window 交给 attn_mqa_for_dcp_decode,是后端新路径真正被调用的地方。

# forward_mla_rocm.py:aiter + DCP 的写 KV 与 decode/verify 调用路径
​
        fuse_rope_for_trtllm_mla = self._fuse_rope_for_trtllm_mla(forward_batch)
​
        # DCP 下 aiter 融合 MLA 内核不生效,而 Gluon 内核不做 RoPE,
        # 因此 decode / target verify / draft extend v2 必须在这里补上 RoPE,
        # 否则 q_pe/k_pe 是未旋转的原始值,注意力分数整体失真。
        force_rope_for_aiter_dcp_decode = (
            get_parallel().dcp_enabled
            and (
                forward_batch.forward_mode.is_decode()
                or forward_batch.forward_mode.is_target_verify()
                or forward_batch.forward_mode.is_draft_extend_v2()
            )
            and _use_aiter_gfx95
            and self.current_attention_backend
            not in FORWARD_ABSORB_CORE_ATTENTION_BACKENDS
        )
        if self.rotary_emb is not None and (
            force_rope_for_aiter_dcp_decode
            or (
                (not fuse_rope_for_trtllm_mla)
                and (not self._skip_rope_for_dsa_tilelang_fused())
                and (not self._skip_rope_for_aiter_fused_mla())
                and (
                    not _use_aiter
                    or not _is_gfx95_supported
                    or self.use_dsa
                    or (
                        self.current_attention_backend
                        not in FORWARD_ABSORB_CORE_ATTENTION_BACKENDS
                        and self.current_attention_backend != "aiter"
                    )
                )
            )
        ):
            q_pe, k_pe = self.rotary_emb(positions, q_pe, k_pe)
​
        # ... 省略中间逻辑 ...
​
        elif (
            _use_aiter
            and (
                forward_batch.forward_mode.is_decode()
                or forward_batch.forward_mode.is_target_verify()
                or forward_batch.forward_mode.is_draft_extend_v2()
            )
            and get_parallel().dcp_enabled
        ):
            q = torch.cat([q_nope_out, q_pe], dim=-1)
            if llama_4_scaling is not None:
                q[..., : self.kv_lora_rank] *= llama_4_scaling
            # set_mla_kv_buffer 内部自己做 owner 过滤与分片除法,
            # 所以这里传原始 out_cache_loc,不做任何 DCP 改写
            get_token_to_kv_pool().set_mla_kv_buffer(
                self.attn_mqa,
                forward_batch.out_cache_loc,
                k_nope,
                k_pe,
            )
            if forward_batch.forward_mode.is_target_verify():
                # verify 窗口各 rank 内容相同,作为 k_window/v_window 传入,
                # 由 backend 的 _forward_verify_dcp 决定只让 rank 0 计算
                k_window = torch.cat([k_nope, k_pe], dim=-1)
                v_window = k_nope
            else:
                k_window = None
                v_window = None
            attn_output, lse = self.attn_mqa_for_dcp_decode(
                q,
                k_window,
                v_window,
                forward_batch,
                save_kv_cache=False, # KV 已由上面的 set_mla_kv_buffer 写入
                **(dict(topk_indices=topk_indices) if topk_indices is not None else {}),
            )
python/sglang/srt/arg_groups/model_overrides/kimi_k3.py configuration

把 aiter 接入 Kimi-K3 DCP 的参数解析白名单,并新增 _require_kimi_k3_aiter_gluon_dcp_support() 在参数解析阶段就校验 Gluon MLA 内核是否可用,避免服务器跑到 kernel launch 才崩溃。

# kimi_k3.py:aiter + DCP 的参数解析门控(在 server 启动前就失败,而不是 kernel launch 时)
​
def _require_kimi_k3_aiter_gluon_dcp_support() -> None:
    from sglang.srt.layers.attention.aiter_mla_gluon import _gluon_fn
​
    # _gluon_fn() 为 None 表示 Gluon MLA 内核不可用(环境变量关闭、import 失败,
    # 或 Triton < 3.7 缺少 cga_layout),具体原因已由 _gluon_fn 内部打日志。
    if _gluon_fn() is None:
        raise RuntimeError(
            "Kimi-K3 DCP with decode_attention_backend='aiter' requires the aiter "
            "gluon mla kernel, which is unavailable. See above aborting reasons."
        )
​
​
@_register_for("KimiK3ForConditionalGeneration")
def _kimi_k3_overrides(server_args: Any, hf_config: Any) -> dict:
    cfg = resolving_view(server_args)
    if cfg.dcp_size > 1:
        overrides = {}
        # ... 省略 symm_mem / DSPARK ragged verify 处理 ...
​
        prefill_backend, decode_backend = attention_backends_of(cfg)
        if decode_backend == "cutedsl_mla" or decode_backend is None:
            # ... 省略 cutedsl_mla 分支 ...
            pass
        elif decode_backend == "tokenspeed_mla":
            # ... 省略 tokenspeed_mla 分支 ...
            pass
        elif decode_backend == "aiter":
            # 先做能力校验,再决定 prefill 后端
            _require_kimi_k3_aiter_gluon_dcp_support()
            # 默认把 prefill 也切到 aiter;只有用户显式指定 triton 时才保留 triton
            prefill_ab = "triton" if prefill_backend == "triton" else "aiter"
            logger.info(
                "Kimi-K3 DCP uses aiter MLA decode: "
                f"prefill={prefill_backend!r} -> {prefill_ab!r}, "
                f"decode={decode_backend!r} -> 'aiter'."
            )
            overrides.update(
                prefill_attention_backend=prefill_ab,
                decode_attention_backend="aiter",
            )
        else:
            raise AssertionError(
                f"Decode attention backend for Kimi-K3 DCP must be 'cutedsl_mla', 'tokenspeed_mla' or 'aiter', got {decode_backend!r}."
            )
        # ... 省略 dcp_replicate_q_proj / dcp_comm_backend 处理 ...
        return overrides

评论区精华

triton_backend 的 DCP draft 特例目前只覆盖 DSPARK 设计

HaiShaw 在 triton_backend.py:234 提问 “Only dspark covered?”;billishyahao 回复目前只测试了 Kimi-K3-DSpark(HuggingFace RadixArk/Kimi-K3-DSpark),并提到还存在 lightseekorg/kimi-k3-eagle3-mla 但未适配,会做后续改动;HaiShaw 最终回复 “@billishyahao fine, you may do a follow up issue/pr later. Thanks.”。

结论:当前把 HIP + DSPARK draft worker 的 dcp_size/dcp_rank 强制为 1/0 的修正只覆盖 DSPARK,EAGLE3 draft 在 HIP + DCP 下的正确性留待后续 PR/Issue;代码注释也标出 TODO(该逻辑应在非 HIP 平台统一修复)。 · unresolved

eager_runner 的 extend_prefix_lens None 检查是公共路径 bug 修复 正确性

HaiShaw 就 `and forward_batch.extend_prefix_lens is not None` 追问 “- bug fix?”;billishyahao 确认 “It is true bug in common path. I also checked on nvidia dcp and observe this issue.”,并拆出 https://github.com/sgl-project/sglang/pull/38991 单独跟踪,随后说明 “Restore this through .../commits/59613d3”。

结论:确认是公共路径上的真实缺陷(NVIDIA DCP 同样可复现),已从本 PR 拆出为独立 PR#38991,本 PR 最终 diff 不再包含 eager_runner.py。 · 已解决

PR CI 未真正覆盖新增 DCP 路径,且 ratchet 测试被新代码触发失败 测试

amd-bot 给出 “Merge verdict: ❌ Do not merge”,指出 aiter_backend.py 新增 17 处被禁用的 legacy parallel-getter 调用导致 parallel-adoption ratchet 测试失败,并强调 “The core value of this PR (aiter MLA decode-context-parallel) is NOT exercised by any PR-CI test that ran.”——代码被 dcp_enabled() 与 is_hip() 双重门控,AMD pipeline 的 stage-b/stage-c 因 wait-for-stage-a-amd-rocm720 约 4 小时超时被 fast-fail 跳过;作者修复后多次 /rerun-failed-ci,HaiShaw 最终确认 “CI signal is ok”。

结论:通过修复与重跑恢复了绿灯,但 DCP 新路径在自动化 CI 中仍缺执行覆盖,只看 CI 绿灯无法证明该功能可用;作者仅报告了一个 H20 case 因超时失败。 · resolved-with-remaining-gap

aiter DCP 的 LSE 底数约定改为自然对数 正确性

提交历史中有独立的 “use natural log” 提交,且 forward_mla.py 的 is_mla_dcp_lse_base_on_e 把 aiter 加入集合(原为 flashmla、cutedsl_mla);说明 aiter DCP 返回自然对数 LSE,跨 rank 归约必须使用 exp/log 而不是 exp2/log2。

结论:采用与既有两个后端一致的自然对数约定,避免 log 底数不匹配导致的系统性数值偏差;新后端接入时必须遵守该判定集合。 · 已解决

风险与影响

1) head 数断言被放宽:aiter_backend.py 中 assert self.dcp_world_size > 1 or _valid_heads or _pad_heads_to_16 or not may_run_mla_decode 让 DCP 分支无条件通过断言,实际 head 支持依赖 mla_kernel_num_head_padded(gather 后 head 数)与 Gluon 内核自身上限。参数解析期的检查只覆盖 Kimi-K3 override,其他 MLA 模型在 aiter + DCP 下可能延迟到 kernel launch 才失败。
2) 两段 verify 合并的数值路径:_forward_verify_dcp() 依赖 rank 0 之外的 rank 只返回前缀部分,再由跨 rank LSE 合并补齐;一旦 k_window 在各 rank 不一致(例如 draft KV 未按预期复制),会静默产生错误输出而非报错。
3) 重复过滤被删除后的契约依赖:forward_mha_rocm.py 移除 filter_dcp_local_kv_indices() 与 translate_dcp_read_ids() 后,正确性完全依赖三个既有调用方已经预过滤/预翻译;未来新增调用方若忘记预处理,会读错 KV 槽位且无任何断言保护。
4) HIP 专属分支造成平台语义分叉:dcp/comm.py 中 if not _is_hip or dcp_extend_prefix_lens_sum > 0 只在 HIP 上跳过空前缀 all-gather,CUDA 仍会发起空 collective,两条路径的可见行为不同且 HIP 侧缺少自动化测试。
5) DSPARK-only 的 draft 特例:triton_backend.py 中 is_hip_dspark_draft 把 dcp_size 强制为 1/dcp_rank 为 0,代码自带 “TODO: this logic should be fixed in non-hip platform”;EAGLE3 draft 在 HIP + DCP 下仍会把各 rank 的完整 draft KV 副本当作分片合并,属于已知未修复缺陷。
6) 长上下文 prefill 的内存压力:handle_attention_aiter() 在 DCP 下把 extend 切到 MHA_ONE_SHOT,该路径会 all-gather 完整序列 KV,132k context 下显存占用与通信量都会显著上升。
7) 无自动化测试守护:本 PR 未新增单测/注册测试,新增路径又在 CI 中被门控且被 fast-fail 跳过,回归只能靠人工 gsm8k 评测发现。

对用户:Kimi-K3 在 8×MI355X(gfx950)上可选用 aiter 作为 prefill 与 decode 后端并开启 --dcp-size 8,PR 给出 5 类场景的 gsm8k 0.980–0.990、accept len 4.68–4.87,覆盖 dspark、radix cache 与 tp8dcp1 对照。对其他 MLA 模型与 NVIDIA 平台无行为影响(全部被 dcp_enabled() 与 is_hip() 门控);但对 HIP + DCP + EAGLE3 draft 的组合仍是未支持状态。对系统:DCP 的分片索引改写与 LSE 合并语义开始进入 aiter 后端,ForwardMetadata 增加了每 forward 构建一次的 DCP 字段,属于注意力子系统的结构性扩展;LSE 底数约定(is_mla_dcp_lse_base_on_e)成为跨后端共享契约,后续新后端接入必须遵守。对团队:作为 1/N 系列的开篇,后续至少还有 EAGLE3 draft 适配、非 HIP 平台通用化、以及拆出的 PR#38991(eager_runner extend_prefix_lens bug)三条明确跟进线,维护者需持续关注 CI 是否真正覆盖 DCP 路径。

核心注意力路径变更 缺少测试覆盖 DCP 路径 CI 未执行 head 数断言放宽 平台语义分叉(HIP 专属分支) EAGLE3 draft 未适配 重复过滤删除后的契约依赖

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论