Prhub

#30113 [KDA] Add FlashInfer SM100 KDA decode + MTP (target_verify) backend

原始 PR 作者 yuan-luo 合并时间 2026-07-15 15:04 文件变更 6 提交数 3 评论 10 代码增减 +1077 / -39

执行摘要

为 KDA 添加 FlashInfer SM100 解码和 MTP 验证后端

KDA had no target_verify path, so KDA models could not use speculative decoding at all. GDN already has the full decode + prefill + MTP story; this PR brings KDA to parity for decode + MTP on Blackwell by wrapping FlashInfer's recurrent_kda.

此 PR 设计思路清晰,验证严谨,适合以下读者精读:

  1. 对线性注意力推测解码实现感兴趣者,可学习其数值验证方法(per-step checkpoint 匹配)和硬件特定后端集成模式。
  2. KDA 模型使用者,应了解配置选项和限制(topk=1,SM100)。
  3. 关注卷积窗口布局问题的开发者,可参考 memory_pool.py 中的条件处理。
    值得关注的设计决策:将 FlashInfer 定位为纯 decode+verify 后端,prefill 保留 Triton,避免依赖 FlashInfer 不支持的 chunk kernel。
讨论亮点
  • 性能基准详情(kaixih 评论):kaixih 要求提供完整基准输出和 wrapper 外开销分解,yuan-luo 补充了 CUDA graph 模式的结果(decode 持平,MTP verify 仍稍慢)。设计团队认为这为后续优化(如融合周围操作)提供了明确目标。
  • topk 限制提前检查(kaixih 评论):kaixih 建议将 FlashInfer 仅支持 topk=1 的限制检查从内核文件移到后端初始化中,以便及早失败。yuan-luo 在 KDAAttnBackend.__init__ 中添加了 speculative_topk > 1 的检查。
  • dtype 硬编码问题(gemini-code-assist 评论):机器人审查建议将 _beta_logit_to_prob 和后续 cast 中的硬编码 torch.bfloat16 改为动态使用 q.dtype,以避免 float16 模型时的类型错误。该建议未被采纳,最终代码仍为 bfloat16 硬编码,成为潜在的兼容性风险。

实现拆解

  1. 创建 FlashInfer KDA 内核包装kda_flashinfer.py):定义 FlashInferKDAKernel 类,封装 flashinfer.kda_decode.recurrent_kda,提供 decodetarget_verify 方法。通过延迟导入和 SM100 守卫确保版本兼容。decode 方法将 raw per-K gate a 和 beta logit b 预处理后传递给内核;target_verify 利用 num_spec_tokensssm_state_indices 参数写入 speculative 状态 scratch 缓冲区。Prefill(extend)因 FlashInfer 没有 KDA chunk kernel 而保持 Triton / CuTe DSL。

  2. 扩展 KDA 调度器与验证逻辑kda_backend.py):在 KDAKernelDispatcher.__init__ 中添加 is_flashinfer() 分支,选择 FlashInferKDAKernel 作为 decode_kernel,并将 verify_kernel 设为该内核(Triton / CuTe DSL 回退到 Triton 内核)。新增 target_verify 方法路由到 verify_kernel.target_verify。在 KDAAttnBackend 中新增 _forward_target_verify 方法,处理 Conv1d 逐 draft token 计算、中间状态检查点以及 SSM 状态回滚,与 GDN 后端模式对齐。

  3. Triton 参考验证kda_triton.py):为 TritonKDAKernel 添加 target_verify 方法,通过 fused_sigmoid_gating_delta_rule_updatedisable_state_update=True)实现。其每个 draft token 的注意力输出作为单元测试的参考,但端到端回滚尚未正确,因此不用于生产。

  4. 卷积窗口布局修正memory_pool.py):修改 conv_window_dedup_enabled 函数,增加 is_kda 参数。KDA 的卷积窗口需要密集布局(不能去重重叠),因为 KDA 的转置操作会破坏重叠视图的物理列对应关系。添加该条件后,KDA 使用密集窗口,确保状态正确。

  5. 测试与基准:新增 test_kda_decode_flashinfer.py(11 个单元测试)验证 FlashInfer decode 和 target_verify 输出与 Triton 参考匹配,并增加 checkpoint 状态匹配测试。新增 bench_kda_flashinfer_mtp.py 提供 latency 对比和正确性检查。

文件 模块 状态 重要度
python/sglang/srt/layers/attention/linear/kernels/kda_flashinfer.py KDA 内核 added 9.08
python/sglang/srt/layers/attention/linear/kda_backend.py KDA 调度 modified 8.12
test/registered/attention/test_kda_decode_flashinfer.py 测试套件 added 7.76
benchmark/bench_linear_attention/bench_kda_flashinfer_mtp.py 基准测试 added 8.78
python/sglang/srt/layers/attention/linear/kernels/kda_triton.py Triton 内核 modified 6.65
python/sglang/srt/mem_cache/memory_pool.py 内存池 modified 5.49

关键符号

FlashInferKDAKernel.decode FlashInferKDAKernel.target_verify KDAKernelDispatcher.target_verify KDAAttnBackend._forward_target_verify TritonKDAKernel.target_verify conv_window_dedup_enabled

关键源码片段

python/sglang/srt/layers/attention/linear/kernels/kda_flashinfer.py core-logic

核心新增文件,封装 FlashInfer recurrent_kda 内核实现 decode 和 target_verify,是 KDA 推测解码的基础。

class FlashInferKDAKernel(LinearAttnKernelBase):
    """FlashInfer KDA 内核(SM100),提供 decode 和 target_verify。"""
​
    def __init__(self):
        available, self._recurrent_kda = _get_flashinfer_kda_kernel()
        if not available or self._recurrent_kda is None:
            raise RuntimeError("FlashInfer KDA kernel not available (require SM100).")
        # 缓存每层 gate 参数(A_log/dt_bias 的 float/reshape 结果)
        self._gate_cache: dict = {}
        # 缓存验证时 ssm_state_indices(基于 batch 和 spec 长度)
        self._verify_idx_cache: dict = {}
​
    def _prep_gate_params(self, A_log, dt_bias):
        """预处理 gate 参数:返回 [HV] fp32 和 [HV*K] fp32。"""
        key = (id(A_log), id(dt_bias))
        cached = self._gate_cache.get(key)
        if cached is not None:
            return cached
        A_log_fi = A_log.reshape(-1).float().contiguous()
        dt_bias_fi = dt_bias.reshape(-1).float().contiguous() if dt_bias is not None else None
        self._gate_cache[key] = (A_log_fi, dt_bias_fi)
        return A_log_fi, dt_bias_fi
​
    @staticmethod
    def _beta_logit_to_prob(b):
        """beta logit -> 概率(sigmoid + bf16)。"""
        return torch.sigmoid(b).to(torch.bfloat16)
​
    def decode(self, q, k, v, a, b, *, A_log, dt_bias,
               ssm_states, cache_indices, query_start_loc, **kwargs):
        """单 token decode(T=1),就地更新 SSM 状态池。"""
        batch_size = cache_indices.shape[0]
        num_v_heads, head_k_dim = v.shape[2], q.shape[3]
        A_log_fi, dt_bias_fi = self._prep_gate_params(A_log, dt_bias)
        g_fi = a.reshape(1, batch_size, num_v_heads, head_k_dim).to(torch.bfloat16)
        beta_fi = self._beta_logit_to_prob(b).reshape(1, batch_size, num_v_heads)
        out, _ = self._recurrent_kda(
            q, k, v, g_fi, beta_fi,
            A_log=A_log_fi, dt_bias=dt_bias_fi,
            ssm_states=ssm_states,
            cache_indices=cache_indices,
            query_start_loc=query_start_loc,
            num_spec_tokens=0,
            use_gate_in_kernel=True,
        )
        return out.to(dtype=q.dtype)

评论区精华

硬编码 bfloat16 的 dtype 兼容性 正确性

gemini-code-assist 指出 _beta_logit_to_prob 和 decode/target_verify 中硬编码为 torch.bfloat16,若模型使用 float16 会出错,建议改为动态 dtype。

结论:未采纳,最终代码仍保持 bfloat16 硬编码。 · unresolved

提前检查 topk 限制 设计

kaixih 建议将 topk=1 的检查移到 kda_backend.py 的初始化中,以便及早失败。

结论:采纳,yuan-luo 在 KDAAttnBackend.__init__ 中添加了早期检查。 · 已解决

性能基准详情请求 性能

kaixih 要求增加基准完整输出和每个 wrapper 额外开销的分解,yuan-luo 补充了 CUDA graph 模式结果。

结论:已提供,PR 描述更新了性能数据。 · 已解决

风险与影响

  • 硬件限制:后端仅能在 SM100(Blackwell)GPU 上工作,kda_flashinfer.py 中使用 torch.cuda.get_device_capability()[0] >= 10 防护,但依赖硬编码版本检查。
  • 推测解码限制:仅支持线性链(topk=1),不支持树状猜测。kda_backend.py 中在初始化时检查并抛出错误,但生产环境可能在配置后才发现不可用。
  • 数据类型硬编码kda_flashinfer.py_beta_logit_to_prob 和 gate 参数 cast 硬编码为 torch.bfloat16,若模型使用 float16 将导致 dtype 不匹配。gemini 审查已指出但未修复。
  • 卷积窗口布局条件memory_pool.py 中增加 is_kda 参数,仅 KDA 使用密集布局,非 KDA 模型行为不变。但需确保该条件分支在所有路径中正确触发,否则可能导致记忆状态损坏。
  • 数值正确性依赖:target_verify 的正确性基于 per-step checkpoint 状态匹配测试(test_kda_decode_flashinfer.py),但端到端仅测试了 n-gram 推测(无 MTP 头)。实际 MTP 场景可能有额外数值误差。
  • 用户影响:KDA 模型(如 Kimi-Linear)现在可以使用推测解码(EAGLE/MTP/n-gram),显著提升推理吞吐。但需要配置 --linear-attn-decode-backend flashinfer--mamba-ssm-dtype bfloat16,仅限 SM100 GPU。
  • 系统影响:新增一个后端选择,不影响现有 Triton/CuTe DSL 后端。内存池增加了条件分支但无性能退化。
  • 团队影响:代码新增约 1077 行,测试和基准完善,贡献者可复现。维护者需关注 Blackwell CI 测试配置变化。
  • 影响程度:对 KDA 用户是高价值功能,对非 KDA 用户无影响。
仅 SM100 仅线性链 topk=1 dtype 硬编码风险 卷积布局条件分支

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论