Prhub

#54682 [ROCm][Perf] Optimize MiniMax-M3 decode indexer and top-k

原始 PR 作者 Fangzhou-Ai 合并时间 2026-09-04 04:20 文件变更 6 提交数 13 评论 38 代码增减 +2010 / -327

执行摘要

优化 gfx950 上 MiniMax-M3 解码索引与 top-k 选择,提升 10-70% 吞吐量。

解决 Issue #54681 提出的问题:在 gfx950 TP4 BF16 路径下,MiniMax-M3 稀疏层的实时解码打分器和通用多阶段选择器存在可测量的启动与工作不均衡开销,尤其是在四个投机查询行场景下,亟需优化。

这是一个高质量的性能优化 PR,值得精读。其核心设计决策(按实际块数映射工作、共享 index-K 瓦片、融合选择与稀疏表构建)和系统化的测试验证方法(策略单测、比特一致性、图重放、微基准与服务端 A/B 测试)具有很高的借鉴价值。建议重点关注 index_topk.py 中的调度策略与内核实现,以及 indexer.py 与 model.py 中如何扩展接口以实现前向路径融合。

讨论亮点
  1. 关于测试冗余性与稳健性:Reviewer AndreasKaratzas 多次质疑新增的多个策略决策单元测试(如 test_amd_decode_score_split_launch_policy, test_amd_balanced_decode_score_budget_by_request_count)是否过于脆弱和冗余。作者 Fangzhou-Ai 解释称这些参数化形状是真实推理场景,且测试策略有助于未来与 AITER 侧核函数集成,最终保留了测试但移除了 test_amd_balanced_decode_score_budget_by_request_count。
  2. 关于测试中的显式同步:AndreasKaratzas 对测试中需要显式设备同步表示疑问,认为 Python 启动后可能隐式完成。作者澄清,核函数启动和图重放是在当前流上顺序执行的,而 assert_exact 产生的布尔结果会等待前置 GPU 工作,因此在确认正确后移除了冗余的 torch.cuda.synchronize() 调用。
  3. 关于随机种子使用:coderabbitai 和 AndreasKaratzas 建议在测试中使用 vLLM 共享的 set_random_seed 而非 torch.manual_seed。作者在后续提交中采纳了此建议,统一了种子设置方式。
  4. 关于完成计数器初始化:Reviewer tjtanaa 指出 topk_completion_counter 在 if 分支外可能未定义。作者回复其默认值为 None,并在分支内才赋值,逻辑正确。

实现拆解

  1. 引入自适应调度策略与优化内核:在 vllm/models/minimax_m3/amd/ops/index_topk.py 中,新增 _decode_score_program_budget 和 _decode_score_split_launch_policy 函数,根据硬件型号、数据类型、请求头维度和请求数量,动态选择优化的平衡内核(_decode_index_score_balanced_kernel)或特化的分块启动策略(_decode_score_split_launch_policy),以替代原有的固定网格启发式调度。同时,新增 _decode_topk_launch_policy 和融合了 top-k 选择、页16稀疏表及上下文长度输出的 _topk_index_partial_kernel 内核。
  2. 扩展模块接口以支持融合输出:修改 vllm/models/minimax_m3/common/indexer.py 中的 MiniMaxM3IndexerImpl 及其 Triton 实现类 MiniMaxM3IndexerTritonImpl 的 forward 方法,新增可选的 attention_block_table、sparse_block_table_out 等参数,允许在索引计算阶段直接生成后续注意力所需的稀疏表,实现前向路径融合。同时为 ROCm 平台添加了用于 CUDA 图原子计数器重置的 topk_completion_counter 缓冲区。
  3. 模型层集成与数据流改造:在 vllm/models/minimax_m3/amd/model.py 的 MiniMaxM3SparseAttention._run_attention 方法中,当使用 AITER 稀疏 PA 且有解码请求时,预先分配稀疏块表并调用新增的融合 indexer 接口,将生成的稀疏表传递给 MiniMaxM3SparseAiterPAImpl。sparse_attention_msa.py 的 forward 方法相应接收并使用传入的预计算稀疏表。
  4. 重构稀疏表构建与导出工具函数:在 vllm/models/minimax_m3/amd/ops/sparse_pa.py 中,将 _alloc_sparse_block_table 和 _block_page_stride 重命名为公共接口 minimax_m3_alloc_sparse_block_table 和 minimax_m3_sparse_block_page_stride,并新增 _write_sparse_block_table_row_from_values Triton 辅助函数以支持从值构建稀疏表行。
  5. 扩展测试覆盖:在 tests/kernels/attention/test_minimax_m3.py 中,新增约 840 行测试,全面覆盖了新的调度策略决策(test_amd_decode_score_split_launch_policy)、平衡映射的精确覆盖(test_amd_balanced_decode_score_mapping_exact_coverage)、优化与通用内核输出的比特级一致性及 CUDA 图重放(test_amd_optimized_decode_score_bitwise_and_graph_replay)。
文件 模块 状态 重要度
vllm/models/minimax_m3/amd/ops/index_topk.py 索引内核 modified 7.91
tests/kernels/attention/test_minimax_m3.py M3 内核测试 modified 7.81
vllm/models/minimax_m3/common/indexer.py 索引器接口 modified 7.38
vllm/models/minimax_m3/amd/model.py M3 模型层 modified 7.09

关键符号

_decode_score_program_budget _decode_score_split_launch_policy _decode_topk_launch_policy _decode_index_score_balanced_kernel _topk_index_partial_kernel minimax_m3_alloc_sparse_block_table minimax_m3_sparse_block_page_stride

关键源码片段

vllm/models/minimax_m3/amd/ops/index_topk.py core-logic

核心变更文件,实现了新的解码打分和 top-k 选择的调度策略(`_decode_score_program_budget`, `_decode_score_split_launch_policy`)与优化的平衡 Triton 内核(`_decode_index_score_balanced_kernel`),是性能提升的关键所在。

# 文件 : vllm/models/minimax_m3/amd/ops/index_topk.py
# 新增的核心调度策略函数
​
def _decode_score_program_budget(
    num_reqs: int,
    head_dim: int,
    query_dtype: torch.dtype,
    cache_dtype: torch.dtype,
    *,
    is_gfx950: bool,
) -> int | None:
    """根据请求硬件条件,决定是否使用平衡内核以及对应的程序预算(网格大小)。
    返回 None 表示回退到通用分块启动路径。"""
    # 仅 gfx950, head_dim 128, BF16 类型才启用优化路径
    if not (
        is_gfx950
        and head_dim == 128
        and query_dtype == torch.bfloat16
        and cache_dtype == torch.bfloat16
    ):
        return None
    # 根据请求量选择不同的预算:1-8 reqs 用 1024, 9-11 reqs 用 768
    if 1 <= num_reqs <= 8:
        return DECODE_SCORE_BALANCED_PROGRAM_BUDGET # 1024
    if 9 <= num_reqs <= MAX_DECODE_SCORE_BALANCED_REQUESTS: # 11
        return DECODE_SCORE_HIGH_BATCH_PROGRAM_BUDGET # 768
    return None # 其他情况回退
​
​
def _decode_score_split_launch_policy(
    num_reqs: int,
    head_dim: int,
    query_dtype: torch.dtype,
    cache_dtype: torch.dtype,
    *,
    is_gfx950: bool,
) -> tuple[int, bool]:
    """为通用分块启动路径选择分块数和高并发配置标志。
    当平衡内核不适用时(budget 为 None),调用此函数。
    返回 (num_kv_chunks, use_high_batch_config)。"""
    # 判断是否启用高并发特化配置
    use_high_batch_config = (
        is_gfx950
        and MIN_DECODE_SCORE_GFX950_HIGH_BATCH_REQUESTS # 20
        <= num_reqs
        <= MAX_DECODE_SCORE_GFX950_HIGH_BATCH_REQUESTS # 64
        and head_dim == 128
        and query_dtype == torch.bfloat16
        and cache_dtype == torch.bfloat16
    )
    target_grid = (
        DECODE_SCORE_GFX950_HIGH_BATCH_TARGET_GRID # 1024
        if use_high_batch_config
        else DECODE_SCORE_DEFAULT_TARGET_GRID # 512
    )
    # 计算每个 KV 块的分块数,向上取整为 2 的幂
    target = max(
        1,
        min(
            MAX_DECODE_SCORE_KV_CHUNKS, # 256
            target_grid // max(1, num_reqs),
        ),
    )
    return 1 << (target.bit_length() - 1), use_high_batch_config

评论区精华

测试冗余性与策略保留价值 测试

Reviewer AndreasKaratzas 质疑 `test_amd_decode_score_split_launch_policy` 和 `test_amd_balanced_decode_score_budget_by_request_count` 等测试是否过于脆弱和冗余。Fangzhou-Ai 解释参数化形状来自真实推理场景,且测试策略有助于未来与 AITER 集成,最终保留了前者,移除了后者。

结论:保留对策略决策边界和内核输出正确性的测试,移除纯粹测试常量值的测试,平衡了测试覆盖与代码冗余。 · 已解决

测试中显式 GPU 同步的必要性 测试

AndreasKaratzas 对测试中是否需要 `torch.cuda.synchronize()` 提出疑问。Fangzhou-Ai 确认 `assert_exact` 等主机端断言会等待 GPU 完成,因此移除了冗余的同步调用,简化了测试代码。

结论:确认测试框架中的主机端操作已隐含等待,移除显式同步。 · 已解决

使用项目共享的随机种子函数 style

coderabbitai 和 AndreasKaratzas 建议将测试中的 `torch.manual_seed(0)` 替换为 vLLM 的 `set_random_seed(0)` 以保持一致性。

结论:作者在后续提交中采纳建议,统一使用 `set_random_seed`。 · 已解决

完成计数器变量初始化的正确性 正确性

tjtanaa 指出 `topk_completion_counter` 变量在条件分支外可能未定义。Fangzhou-Ai 解释其初始值为 `None`,仅在 `if` 分支内被赋值。

结论:代码逻辑正确,变量有明确的默认值 `None`。 · 已解决

风险与影响

  1. GPU 正确性风险:核心变更涉及 Triton 内核的调度策略、工作分配算法及融合计算流程,尽管有详尽的比特级一致性测试,但内核逻辑复杂,对输入形状和边界条件敏感,仍有引入计算错误的风险,可能影响模型精度。
  2. 硬件特异性风险:优化路径(平衡内核、快速选择器)明确限定于 gfx950、BF16、head_dim=128 以及特定的请求数量范围。虽然保留了通用回退路径,但维护两套代码路径增加了测试和维护负担,且未来硬件特性变化可能导致策略失效。
  3. CUDA 图兼容性风险:为支持 CUDA 图捕获引入了原子计数器 topk_completion_counter 和指针区分的图输入。若管理不当(如重置时序错误),可能导致图重放时状态不一致,产生静默数据损坏。
  1. 对用户:直接提升 MiniMax-M3 模型在 ROCm gfx950 硬件上的解码吞吐量和延迟(ITL 改善 10-70%),尤其在较高并发(c10-c15)场景下效果显著,提升用户推理服务性价比。
  2. 对系统:在 vLLM 的 MiniMax-M3 模型 ROCm 后端引入了更精细、更高效的内核调度和数据流路径,为该模型的进一步性能优化(如与 AITER 内核更深集成)奠定了基础。
  3. 对团队:展示了针对特定硬件特性进行深度内核级优化的实践模式,包括调度策略设计、工作负载平衡、融合路径开发及全面的测试方法。但也增加了该模块的复杂度和维护门槛。
核心路径变更 硬件特异性优化 内核逻辑复杂度高 需全面正确性验证

关联 Issue

#54681 [Performance][ROCm] Optimize MiniMax-M3 fresh decode indexer on gfx950

完整报告

参与讨论