Prhub

#44977 [ROCm][MLA] Fuse MLA q/kv RMSNorm + FP8 per-token quant in the FP8 attention path

原始 PR 作者 xaguilar-amd 合并时间 2026-07-02 13:00 文件变更 4 提交数 4 评论 21 代码增减 +371 / -5

执行摘要

ROCm MLA FP8 路径融合 q/kv RMSNorm 与 per-token 量化

在对MLA注意力进行FP8量化时,RocmAiterRMSNormQuantFusionPass会将q侧的rms_norm→fp8 per-token quant折叠为rocm_aiter_rmsnorm_fused_dynamic_quant,导致q侧不再是裸RMSNorm,破坏MLADualRMSNormFusionPass的对称双RMSNorm模式匹配。这使得FP8路径下无法享受双RMSNorm融合的性能收益。该PR通过匹配这种不对称对(q per-token quant + kv rms_norm),将其降低为单个融合内核,恢复FP8路径的融合能力。

建议精读该PR,特别是模式匹配pass的设计。它展示了如何处理编译pass间的流水线依赖——利用已有pass的规范形式作为匹配目标,而非试图匹配更原始的图结构。此外,关于availability guard取舍的讨论也值得关注。对于仅关心功能的读者,可以直接合并该PR以获得ROCm FP8 MLA路径的性能提升。

讨论亮点

评论中主要围绕三点:

  1. Pass顺序设计:Rohan138建议将MLADualRMSNormFusionPass移至RocmAiterRMSNormQuantFusionPass之前,从而匹配原始的两个RMSNorm+quant模式。但xaguilar-amd解释当前顺序(norm+quant先执行,再将结果与kv norm融合)更简单且安全,因为q侧被归一化为一个标准op后匹配,避免了重复匹配逻辑。该解释被接受。
  2. 代码洁癖:多位审查者要求减少AI生成的冗余注释。xaguilar-amd在第二、三commit中集中精简了注释,获得认可。
  3. 可用性守卫:tjtanaa指出AITER已固定到v0.1.16.post2,应移除动态检查以简化代码并暴露潜在的AITER兼容性问题。xaguilar-amd在最后commit中完全移除了check_aiter_fused_qk_rmsnorm_per_token_quant函数及相关守卫。

实现拆解

该PR的实现分三个步骤:

  1. 在vllm/_aiter_ops.py中注册新自定义算子:添加_fused_mla_dual_rms_norm_per_token_quant_impl(实际调用AITER的fused_qk_rmsnorm_per_token_quant内核)、_fused_mla_dual_rms_norm_per_token_quant_fake(fake实现用于trace)以及通过direct_register_custom_op注册为torch.ops.vllm.fused_mla_dual_rms_norm_per_token_quant。同时添加get_fused_mla_dual_rms_norm_per_token_quant_op访问器。初期包含check_aiter_fused_qk_rmsnorm_per_token_quant可用性检查,在最终commit中随AITER v0.1.16.post2固定而被移除。
  2. 在vllm/compilation/passes/fusion/rocm_aiter_fusion.py中新增MLADualRMSPerTokenQuantPattern类:该类继承VllmPatternReplacement,定义get_inputs提供典型MLA形状的虚拟张量,pattern属性描述q侧rocm_aiter_rmsnorm_fused_dynamic_quant + kv侧vllm_ir.rms_norm的图结构,replacement属性利用新注册的fused_mla_dual_rms_norm_per_token_quant op实现融合替换。在MLADualRMSNormFusionPass的__init__中,当AITER提供该内核时,为两个常用epsilon值(1e-5、1e-6)注册该pattern。
  3. 在tests/compile/passes/test_fuse_mla_dual_rms_norm.py中添加MLADualRMSNormFp8PerTokenTestModel和test_fuse_mla_dual_rms_norm_fp8_per_token测试:模型模拟FP8路径的q norm+quant和kv norm,测试验证融合恰好触发一次且融合前后输出在1e-2误差内一致。
  4. 在docs/design/fusions.md中更新文档,记录FP8 attention变体的融合过程。
    注意:该融合仅在ROCm+AITER启用时生效,默认在优化级别O1及以上自动开启。PR最终版移除了动态可用性检查,依赖AITER固定版本提供内核。
文件 模块 状态 重要度
vllm/compilation/passes/fusion/rocm_aiter_fusion.py 融合模式 modified 8.27
vllm/_aiter_ops.py AITER 接口 modified 7.69
tests/compile/passes/test_fuse_mla_dual_rms_norm.py 测试 modified 7.05
docs/design/fusions.md 文档 modified 2.69

关键符号

_fused_mla_dual_rms_norm_per_token_quant_impl _fused_mla_dual_rms_norm_per_token_quant_fake get_fused_mla_dual_rms_norm_per_token_quant_op MLADualRMSPerTokenQuantPattern.__init__ MLADualRMSPerTokenQuantPattern.get_inputs MLADualRMSPerTokenQuantPattern.pattern MLADualRMSPerTokenQuantPattern.replacement

关键源码片段

vllm/compilation/passes/fusion/rocm_aiter_fusion.py core-logic

定义了新的融合模式 MLADualRMSPerTokenQuantPattern,实现 FP8 路径下的双 RMSNorm+per-token 量化融合匹配与替换,是 PR 的核心逻辑所在。

class MLADualRMSPerTokenQuantPattern(
    VllmPatternReplacement[
        ..., tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]
    ]
):
    # 引用先前注册的动态量化 op 和本模式要使用的 fused op
    DYNAMIC_QUANT_OP = rocm_aiter_ops.get_rmsnorm_fused_dynamic_quant_op()
    FUSED_OP = rocm_aiter_ops.get_fused_mla_dual_rms_norm_per_token_quant_op()
​
    def __init__(self, epsilon: float) -> None:
        self._epsilon = epsilon
​
    def get_inputs(self) -> list[torch.Tensor]:
        # 典型 MLA 几何尺寸:q_dim=256, kv_c_dim=128, k_pe_dim=64
        q_dim, kv_c_dim, k_pe_dim = 256, 128, 64
        return [
            self.empty_bf16(5, q_dim + kv_c_dim + k_pe_dim), # projected
            self.empty_bf16(q_dim), # q_weight
            self.empty_bf16(kv_c_dim), # kv_weight
        ]
​
    @property
    def pattern(self) -> Callable[..., tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]]:
        eps = self._epsilon
        dynamic_quant_op = self.DYNAMIC_QUANT_OP
​
        def _pattern(projected, q_weight, kv_weight):
            # 拆分 q 和 kv 隐变量
            q_dim = q_weight.shape[0]
            kv_dim = projected.shape[-1] - q_dim
            kv_c_dim = kv_weight.shape[0]
            k_pe_dim = kv_dim - kv_c_dim
            q_c, kv_lora = projected.split([q_dim, kv_dim], dim=-1)
            kv_c, k_pe = kv_lora.split([kv_c_dim, k_pe_dim], dim=-1)
            # q 侧:RMSNorm + FP8 per-token 量化
            q_quant = dynamic_quant_op(x=q_c, weight=q_weight,
                                        epsilon=eps, quant_dtype=FP8_DTYPE)
            # kv 侧:仅 RMSNorm (bf16)
            kv_normed = vllm.ir.ops.rms_norm(kv_c, kv_weight, eps)
            return q_quant[0], q_quant[1], kv_normed, k_pe
​
        return _pattern
​
    @property
    def replacement(self) -> Callable[..., tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]]:
        eps = self._epsilon
        fused_op = self.FUSED_OP
​
        def _replacement(projected, q_weight, kv_weight):
            # 与 pattern 相同的拆分逻辑
            q_dim = q_weight.shape[0]
            kv_dim = projected.shape[-1] - q_dim
            kv_c_dim = kv_weight.shape[0]
            k_pe_dim = kv_dim - kv_c_dim
            q_c, kv_lora = projected.split([q_dim, kv_dim], dim=-1)
            kv_c, k_pe = kv_lora.split([kv_c_dim, k_pe_dim], dim=-1)
            # 使用 fused op 一次计算 q_norm+quant 和 kv_norm
            at = fused_op(q_c, q_weight, kv_c, kv_weight, eps, eps)
            # 返回 (q_fp8, q_scale, kv_normed, k_pe)
            return at[0], at[1], at[2], k_pe
​
        return _replacement
vllm/_aiter_ops.py core-logic

注册新的 fused_mla_dual_rms_norm_per_token_quant 自定义算子,包含实际实现、fake 实现和访问器,是融合的内核调用入口。

def _fused_mla_dual_rms_norm_per_token_quant_impl(
    q: torch.Tensor,
    q_weight: torch.Tensor,
    kv: torch.Tensor,
    kv_weight: torch.Tensor,
    q_epsilon: float,
    kv_epsilon: float,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
    # 调用 AITER 的 fused_qk_rmsnorm_per_token_quant 内核
    from aiter.ops.fused_qk_rmsnorm_group_quant import fused_qk_rmsnorm_per_token_quant
​
    mq, nq = q.shape
    q_out = torch.empty((mq, nq), dtype=FP8_DTYPE, device=q.device)
    q_scale = torch.empty((mq, 1), dtype=torch.float32, device=q.device)
    kv_normed = torch.empty(kv.shape, dtype=kv.dtype, device=kv.device)
​
    fused_qk_rmsnorm_per_token_quant(
        q_out_quantized=q_out,
        q_out_scale=q_scale,
        q=q,
        q_weight=q_weight,
        q_epsilon=q_epsilon,
        k_out=kv_normed,
        k=kv,
        k_weight=kv_weight,
        k_epsilon=kv_epsilon,
        gemma_norm=False,
    )
    return q_out, q_scale, kv_normed
​
# 注册片段(位于 register_ops_once 内)
direct_register_custom_op(
    op_name="fused_mla_dual_rms_norm_per_token_quant",
    op_func=_fused_mla_dual_rms_norm_per_token_quant_impl,
    mutates_args=[],
    fake_impl=_fused_mla_dual_rms_norm_per_token_quant_fake,
)
​
@staticmethod
def get_fused_mla_dual_rms_norm_per_token_quant_op() -> OpOverload:
    # 外界通过此方法获取 op,用于 pattern replacement
    return torch.ops.vllm.fused_mla_dual_rms_norm_per_token_quant.default

评论区精华

Pass ordering: dual fusion after norm+quant 设计

Rohan138 建议将 MLADualRMSNormFusionPass 移至 RocmAiterRMSNormQuantFusionPass 之前,以匹配原始的两个 norm+quant 模式。xaguilar-amd 解释当前顺序更简单安全:先让 norm+quant pass 将 q 侧归一化为一个标准 op,再被 dual pass 匹配,避免了重复匹配逻辑。

结论:当前顺序被保留,并在代码中添加了说明注释。 · 已解决

Verbose comments should be condensed style

Rohan138 多次要求减少 AI 生成的冗长注释,特别是测试文件和文档中的逐行解释。xaguilar-amd 在第二、三 commit 中集中精简了注释。

结论:注释被精简,Rohan138 随后 approve。 · 已解决

Remove availability guard after AITER pin other

tjtanaa 指出 AITER 已固定到 v0.1.16.post2,建议移除动态检查以简化代码并暴露潜在问题。xaguilar-amd 最终 commit 完全移除了 check_aiter_fused_qk_rmsnorm_per_token_quant 函数及相关守卫。

结论:guard 被移除,依赖硬编码的 AITER 版本。 · 已解决

风险与影响

风险包括:

  1. 依赖AITER特定内核版本:该融合依赖AITER的fused_qk_rmsnorm_per_token_quant内核,若未来AITER修改API或移除该内核,将导致编译pass失败或静默不触发。由于PR最终移除了动态检查,无法优雅降级。
  2. 数值精度:融合后的输出与分离计算的输出在测试中验证了1e-2误差,但不同模型或长度下可能存在更大偏差,特别是当per-token scale出现极端值时。
  3. 平台限定:该融合仅针对ROCm,其他平台不受影响。但需确保在CI中覆盖ROCm测试。
  4. pass生命周期:评论提到fusion passes可能很快被整体移除(#pr-merge-requests),若移除此pass则相关代码变为死代码,需及时清理。

影响范围:仅限使用ROCm+AITER且启用FP8 MLA注意力路径的模型(如DeepSeek-V3、Kimi-K2)。性能影响:根据测试,在Kimi-K2 MXFP4-AttnFP8上吞吐提升约1-2%,TPOT降低1-2%。无功能影响,融合结果与分离计算在容忍误差内一致。对团队:增加了少量维护负担,但代码已最小化且集中。无用户可见的配置变更,融合自动在O1及以上启用。

ROCm 限定 AITER 内核依赖 精度敏感 pass 生命周期短

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论