Prhub

#50534 [XPU] Add tuned Mamba SSU configs for Intel Arc Pro B70

原始 PR 作者 pmanczak 合并时间 2026-08-13 20:36 文件变更 5 提交数 1 评论 13 代码增减 +247 / -12

执行摘要

B70 新增 Mamba SSU 调优配置,修复 XPU 调优脚本

vLLM 的 selective_state_update 配置目录此前只覆盖 AMD 与 NVIDIA 设备,Intel GPU 上运行的 Mamba/hybrid 模型只能使用启发式启动参数,性能远低于调优配置。作者在 PR body 中说明:float16 必须独立调优而非复制 float32 结果,因为在 bfloat16 state 下 headdim=128,dstate=256 有 9/12 的网格点最优解不同,复用 float32 值会导致 kernel 级中位慢 79.6%;此外,原调优脚本无条件调用 torch.cuda.get_device_capability(),在 XPU 构建上会在调优开始前抛出 AssertionError: Torch not compiled with CUDA enabled,torch.cuda.CUDAGraph() 与 torch.cuda.Event 也会让所有候选被跳过,sweep 无结果。

值得精读,重点看三个设计决策:float16 不复制 float32 而是独立调优(用数据说明精度对 kernel 最优解的影响)、以 10% 收益阈值决定是否采纳重调条目(防止 tuner 与模型 ngroups 不一致造成的过拟合)、以及把 torch.cuda.* API 迁移到 current_platform / torch.accelerator 的适配模式。对要在新硬件上做 kernel 调优的贡献者,这是一个完整可复制的流程:工具修复 → 搜索空间设计 → 权衡取舍 → 数值与 E2E 验证。

讨论亮点

review 中最核心的讨论有两点:一是 jikunshang 在 PR 评论中要求删除没有模型覆盖的配置("headdim=64,dstate={16,32,64,256} | float32 only | no model on hand ... we'd better remove unnecessary config."),作者将其采纳,最终只保留 FalconH1 与 Mamba2 实际使用的两种 shape;二是 jikunshang 在 diff hunk 上针对最初的 torch.accelerator.Graph() 写法提问 "is it safe here?",因为该 API 在仓库内没有 CUDA 使用先例,最终 head 版本改为显式平台分支(CUDA 走 CUDAGraph()、XPU 走 XPUGraph())并统一使用 current_platform.graph() 上下文,既保留了 CUDA 的 side-stream 捕获语义,又支持 XPU。之后 yma11 审核通过("LGTM. @jikunshang please take a look."),jikunshang 最终 APPROVED。

实现拆解

  1. 修复调优脚本的 CUDA 硬编码(benchmarks/kernels/benchmark_selective_state_update.py)_make_inputs 的 device 默认值从 "cuda" 改为 current_platform.device_typebenchmark_config 中 graph 创建改为平台分支(current_platform.is_cuda_alike() 时用 torch.cuda.CUDAGraph(),否则用 torch.xpu.XPUGraph()),并以 current_platform.graph() 作为捕获上下文;计时事件从 torch.cuda.Event 改为 torch.Eventmaintorch.cuda.get_device_capability() 改为 current_platform.get_device_capability() 并允许返回 None(打印 n/a)。这样脚本在 CUDA、ROCm、XPU 上都能运行,同时保留 CUDA 原有行为。
  2. 确定搜索空间与调优方法论:按 effective_batch 网格(8~131072)调优 (BLOCK_SIZE_M, num_warps);网格从 8 开始,因为 Mamba2-2.7b 在 concurrency=1 时按 batch 1 x 80 heads 运行,原从 128 开始的网格在该点比启发式慢约 10%;float16 独立调优;headdim=64,dstate=128 保留 incumbent 条目,仅当新调优结果相对启发式有超过 10% 差距时才采纳,因为 tuner 用 ngroups=8 而模型实际跑 ngroups=1,盲目选微基准赢家在 E2E 上反而损失 4.2%。
  3. 新增配置文件:在 vllm/model_executor/layers/mamba/ops/configs/selective_state_update/ 下新增 4 个 JSON,文件名按 (headdim, dstate, device_name, cache_dtype) 编码,运行时由 mamba_ssm 的 get_ssm_config_file_name 自动拾取,产品路径零改动。headdim=64,dstate={16,32,64,256} 虽已调优验证,因无模型可做 E2E 验证,按 review 建议删除,继续使用启发式。
  4. 验证配套:数值验证对比 CPU reference 在模型真实 ngroups/nheads 下 8/8 与 7/7 通过,启发式对照组干净;E2E 用 vllm bench serve 在 Falcon-H1-7B 与 Mamba2-1.3b 上按 10 档 concurrency 测量,并在两台 B70 机器复现高增益结果。本 PR 未新增单元测试,验证依赖调优/基准流程本身。
文件 模块 状态 重要度
benchmarks/kernels/benchmark_selective_state_update.py 基准工具 modified 6.45
vllm/model_executor/layers/mamba/ops/configs/selective_state_update/headdim=64,dstate=128,device_name=Intel(R)_Arc(TM)_Pro_B70_Graphics,cache_dtype=float16.json SSU 配置 added 5.26
vllm/model_executor/layers/mamba/ops/configs/selective_state_update/headdim=64,dstate=128,device_name=Intel(R)_Arc(TM)_Pro_B70_Graphics,cache_dtype=float32.json SSU 配置 added 4.86
vllm/model_executor/layers/mamba/ops/configs/selective_state_update/headdim=128,dstate=256,device_name=Intel(R)_Arc(TM)_Pro_B70_Graphics,cache_dtype=float16.json SSU 配置 added 5.19
vllm/model_executor/layers/mamba/ops/configs/selective_state_update/headdim=128,dstate=256,device_name=Intel(R)_Arc(TM)_Pro_B70_Graphics,cache_dtype=float32.json SSU 配置 added 4.79

关键符号

_make_inputs benchmark_config main

关键源码片段

benchmarks/kernels/benchmark_selective_state_update.py platform-adaptation

调优工具的平台适配核心:移除 torch.cuda 硬编码,引入 current_platform 与 XPUGraph,使脚本可在 XPU-only 构建运行,是本 PR 能够产出调优配置的前提。

def benchmark_config(
    batch: int,
    nheads: int,
    dim: int,
    dstate: int,
    ngroups: int,
    block_size_m: int,
    num_warps_val: int,
    dtype: torch.dtype,
    state_dtype: torch.dtype | None = None,
    num_iters: int = 100,
    num_warmup: int = 20,
    graph_batch_size: int = 10,
) -> float | None:
    """Time one (BLOCK_SIZE_M, num_warps) config for selective_state_update.    返回微秒耗时,失败时返回 None。通过 graph capture 反复执行
    graph_batch_size 次 kernel,隔离 Python 调度与 kwarg 解析开销。
    """
    state, x, dt, A, B, C, D, dt_bias, out = _make_inputs(
        batch, nheads, dim, dstate, ngroups, dtype, state_dtype=state_dtype
    )
​
    def _call_kernel() -> None:
        selective_state_update(
            state, x, dt, A, B, C, D=D, z=None, dt_bias=dt_bias,
            dt_softplus=True, out=out,
        )
​
    try:
        with override_ssm_config((block_size_m, num_warps_val)):
            # Eager 模式 warmup:触发 Triton JIT 与 autotune,预热缓存
            for _ in range(num_warmup):
                _call_kernel()
            torch.accelerator.synchronize()
​
            # CUDA 需要 side-stream 捕获,而 XPU 上 torch.cuda.CUDAGraph()
            # 不可用,因此按平台创建 graph 对象,再统一用 current_platform.graph()
            # 作为捕获上下文,规避 torch.accelerator.Graph() 在 CUDA 未验证的风险
            graph = (
                torch.cuda.CUDAGraph()
                if current_platform.is_cuda_alike()
                else torch.xpu.XPUGraph()
            )
            with current_platform.graph(graph):
                for _ in range(graph_batch_size):
                    _call_kernel()
            torch.accelerator.synchronize()
​
            # 预热 graph 回放,让运行时稳定
            for _ in range(5):
                graph.replay()
            torch.accelerator.synchronize()
​
            # torch.cuda.Event 在 XPU 构建不存在,torch.Event 是跨平台别名
            start = torch.Event(enable_timing=True)
            end = torch.Event(enable_timing=True)
            latencies: list[float] = []
            for _ in range(num_iters):
                start.record()
                graph.replay()
                end.record()
                end.synchronize()
                latencies.append(start.elapsed_time(end))
            graph.reset()
        # elapsed_time 返回 ms;每次回放跑 graph_batch_size 个 kernel,
        # 因此除以 (num_iters * graph_batch_size) 并换算为 us
        return sum(latencies) / (num_iters * graph_batch_size) * 1000
    except Exception as e:
        if "OutOfResources" not in str(e):
            print(
                f"    Warning: config M={block_size_m},w={num_warps_val} "
                f"raised {type(e).__name__}: {e}"
            )
        return None
​
​
# main() 中的设备能力探测:XPU 上 get_device_capability() 返回 None,
# 原 torch.cuda.get_device_capability() 会直接抛 AssertionError
cap = current_platform.get_device_capability()
is_blackwell = cap is not None and cap[0] >= 10 # 仅 CUDA 设备可能为 Blackwell
cap_str = f"sm_{cap[0]}{cap[1]}" if cap is not None else "n/a"
print(f"Device : {device_name}  ({cap_str})")

评论区精华

删除无模型覆盖的调优配置 设计

jikunshang 在 PR 评论中指出 headdim=64,dstate={16,32,64,256} 的 float32 配置没有模型在手验证,建议删除:"we'd better remove unnecessary config."

结论:作者接受,最终 PR 仅保留 FalconH1 与 Mamba2 实际覆盖的两种 shape 共 4 个配置文件,未覆盖 shape 继续使用启发式。 · 已解决

torch.accelerator.Graph() 在 CUDA 上的安全性 设计

jikunshang 在 diff hunk 上针对最初的 torch.accelerator.Graph() 写法提问 "is it safe here?";该 API 在仓库内没有 CUDA 使用先例,body 中也提示需要检查 CUDA。

结论:改为显式平台分支:CUDA 用 CUDAGraph()、XPU 用 XPUGraph(),并以 current_platform.graph() 作为捕获上下文,既保留 CUDA side-stream 语义又支持 XPU。 · 已解决

风险与影响

  1. 覆盖范围有限:四个配置文件均以精确设备名 Intel(R) Arc(TM) Pro B70 Graphics 命名,其他 Intel 显卡(如 Arc A 系列、B580 等)仍回退启发式,不享受本轮收益,但也不会变差。
  2. float16 配置同时服务 bfloat16 模型_SSM_CACHE_DTYPE_MAP 将 bfloat16 映射到 float16 文件,本 PR 已针对 bf16 state 验证,但未来若引入新的 bf16 专属优化需重新审视该映射。
  3. benchmark 脚本的平台分支改动影响 CUDA 路径current_platform.graph()torch.cuda.graph() 的语义一致性依赖仓库平台抽象的正确实现,GPU CI 会覆盖该路径,但本 PR 没有为脚本本身添加单元测试。
  4. 配置依赖 Triton 版本:JSON 内记录 triton_version=3.7.1,Triton 升级后这些参数可能不再是全局最优,需要重新调优。
  5. 轻微工程问题:JSON 文件缺少 trailing newline,且无 schema 校验,不影响解析但容易被后续工具链误报。

对用户而言,在 B70 上运行 FalconH1 系列 hybrid 模型与 Mamba2 系列模型时,decode 延迟与吞吐显著改善(显式 float32 下 TPOT 最高提升 1.455x / 1.316x,默认 float16 路径也有 2.6%~5.7% 收益),且未覆盖场景继续走启发式,行为无损。对系统而言,mamba_ssm 运行时按文件名自动拾取配置,无需修改产品代码,属于 zero-touch 接入,其他平台不受影响。对团队而言,修复后的 benchmark 脚本成为可复用的跨平台调优工具(与 benchmark_moe.py 的方法一致),后续为其他 Intel 设备调优可直接复用同一流程,也为社区贡献了对 CUDA/ROCm 更友好的基准基础设施。

单设备精确匹配,覆盖范围有限 配置无自动化测试覆盖 benchmark 平台分支影响 CUDA 路径 配置依赖 Triton 3.7.1

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论