TileLang Mamba2 Chunk Scan 前向算子:自动调优实现、H800 实测吞吐与 Triton/Helion 基线对比

TileLang Mamba2 Chunk Scan 前向算子:自动调优实现、H800 实测吞吐与 Triton/Helion 基线对比

【免费下载链接】tilelang Domain-specific language designed to streamline the development of high-performance GPU/CPU/Accelerators kernels 【免费下载链接】tilelang 项目地址: https://gitcode.com/GitHub_Trending/ti/tilelang

本文以仓库中 benchmark/mamba2/README.md 记录的 Mamba2 chunk_scan 前向算子基准测试为主体,完整继承其评测参数、复现命令与实测吞吐数据,并结合 benchmark/mamba2/benchmark_mamba_chunk_scan.py 的源码深入讲解该 TileLang kernel 的分块策略、指数衰减实现与 @autotune 搜索空间设计,帮助读者掌握在 Mamba2 状态空间模型关键算子上编写、调优并跨编译器评测 TileLang kernel 的完整流程。

Mamba2 chunk_scan 在 H100 上的跨编译器性能对比图

一、评测目标与固定参数配置

该基准聚焦 Mamba2(状态空间模型 SSM 的第二代)推理/训练管线中的核心前向算子 chunk scan:给定按 chunk 组织的输入 cb、投影序列 x、时间衰减系数 dt、累加衰减 dA_cumsum、输出投影 C、上一 chunk 的隐状态 prev_states 与跳跃连接系数 D,计算输出序列。

文档固定了如下评测形状(这也是 README 中所有吞吐数据的适用前提):

参数取值含义
batch8批大小
heads80注意力头数(nheads)
groups1头分组数(ngroups,GQA 式共享)
chunk_size256每个 chunk 的时间步数
dim64每头维度(headdim)
dstate128状态空间维度
seq_len1024 ~ 32768扫描的序列长度(nchunks = ceil(seq_len / chunk_size))

计算精度为 T.float16 输入、T.float32 累加(见 kernel 源码中 dtype = T.float16、accum_dtype = T.float32),并开启 TL_ENABLE_FAST_MATH 快速数学 pass。

二、如何复现:完整命令

README 给出的官方复现方式如下(在仓库根目录执行,进入 benchmark/mamba2 后通过 stdin 调用 chunk_scan_fwd):

cd benchmark/mamba2
python - <<'PY'
from benchmark_mamba_chunk_scan import chunk_scan_fwd

batch = 8
heads = 80
groups = 1
chunk_size = 256
dim = 64
dstate = 128
for seq_len in [1024, 2048, 4096, 8192, 16384, 32768]:
    res = chunk_scan_fwd(
            batch,
            seq_len,
            chunk_size,
            groups,
            heads,
            dim,
            dstate)
    tflops = (2 * batch * seq_len * chunk_size * heads * dim * 0.5 + 2 * batch * seq_len * heads * dim * dstate) / res.latency * 1e-9
    print(f"seq_len={seq_len:5d}  latency={res.latency:.6f}ms  TFlops={tflops:.3f}")
PY

几点说明(均可在源码中印证):

  • chunk_scan_fwd 被 @autotune(configs=get_configs(), warmup=10, rep=10) 装饰(见 benchmark_mamba_chunk_scan.py),调用后返回的是自动调优结果对象,其 .latency 属性即最优配置的测得延迟(ms),这正是上式中 res.latency 的来源;脚本 __main__ 分支还会进一步打印 best_config 与 ref_latency。
  • FLOPs 公式为 2*batch*seq_len*chunk_size*heads*dim*0.5 + 2*batch*seq_len*heads*dim*dstate,与 benchmark_mamba_chunk_scan.py 中 total_flops 的定义一致:前一项是 chunk 内 CB 与 x 的因果 matmul(0.5 因子对应下三角因果掩码只有一半有效),后一项是 C 与 prev_states 的跨 chunk 状态 matmul。
  • 若希望与 Triton、Helion 基线同场对比,可直接运行脚本本体,它提供命令行参数 --batch、--heads、--groups、--seq_len、--chunk_size、--dim、--dstate、--tune(见 benchmark_mamba_chunk_scan.py),默认值与 README 的固定参数完全一致。注意该脚本依赖 mamba_ssm(Triton 版 _chunk_scan_fwd)与 helion 两个第三方包,缺失时会抛出带安装提示的 ImportError。

三、实测结果:H800 上随序列长度扩展的吞吐

README 记录的测试环境为:仓库 commit 8a5eb569704bfea64478c29adcfe3a09e3c2b12c,GPU 为 NVIDIA H800 SXM(驱动 560.35.05),使用默认自动调优搜索空间。实测数据如下表:

Seq_lenLatency (ms)Throughput (TFLOPs)
10240.169126.477
20480.329130.195
40960.645133.054
81921.278134.362
163842.531135.711
327685.076135.379

从数据形态看:延迟随 seq_len 近似线性增长(1024→32768 时延迟从 0.169 ms 增至 5.076 ms,约 30 倍,与 32 倍长度基本吻合);吞吐随序列变长从 126.5 TFLOPs 爬升到约 135 TFLOPs 后趋于平稳——短序列下 kernel 启动与访存占比更高,长序列下计算密度摊薄了固定开销。复现结果会因驱动、CUDA 版本与调优随机采样而异,上表应视为该特定环境的参考基线。

四、与 Triton、Helion 基线的对比

README 声明的基线版本为:Triton v3.5.0 + mamba-ssm v2.2.6.post3,以及 Helion v0.2.1。三者在同一组输入上的吞吐对比见下图(图注为 NVIDIA H100):

Mamba2 chunk_scan 在不同编译器间的性能对比(NVIDIA H100)

  • Triton 基线直接调用 mamba_ssm.ops.triton.ssd_chunk_scan._chunk_scan_fwd,计时用 do_bench(..., _n_warmup=10, _n_repeat=10);
  • Helion 基线是在脚本内用 @helion.kernel() 现写的等价 kernel(按 [nheads, chunk_size, headdim, batch, nchunks] 分块,先做 C @ prev_states.T 再循环累加因果衰减后的 cb @ x,见 benchmark_mamba_chunk_scan.py),并通过 run_example 与 PyTorch 参考实现 ref_program 对拍。

五、源码深读:chunk_scan_fwd TileLang Kernel

chunk_scan_fwd 的完整实现位于 benchmark_mamba_chunk_scan.py,核心结构如下。

5.1 张量布局与 Kernel 网格

八个张量签名为:

  • cb: (batch, nchunks, ngroups, chunk_size, chunk_size) —— 每个 chunk 内 CB 预计算结果;
  • x: (batch, seqlen, nheads, headdim)、Output: (batch, seqlen, nheads, headdim);
  • dt, dA_cumsum: (batch, nheads, nchunks, chunk_size) —— 逐时间步衰减系数及其累计;
  • C: (batch, seqlen, ngroups, dstate);
  • prev_states: (batch, nchunks, nheads, headdim, dstate);
  • D: (nheads)。

网格划分为 T.Kernel(nheads, ceildiv(chunk_size, block_M) * ceildiv(headdim, block_N), batch * nchunks),即:bz 遍历头(头到组的映射用 bz // (nheads // ngroups) 实现 GQA 共享,groups=1 时退化为每头独立),by 遍历 (batch, chunk),bx 把单个 chunk 的 [chunk_size, headdim] 输出块按 m_idx(时间维)/n_idx(维度维)二维切分(见 benchmark_mamba_chunk_scan.py)。

5.2 两阶段累加:状态贡献 + chunk 内扫描

  1. 跨 chunk 状态项:将 C 块与 prev_states 块载入 shared(C_shared: (block_M, block_Dstate)、prev_state_shared: (block_N, block_Dstate)),执行 T.gemm(C_shared, prev_state_shared, acc_o, transpose_B=True),再按行乘上 scale_m_local[i] = exp2(dA_cumsum_m * p)(p = 1.44269504 是 log2(e) 常数,把自然指数转换为 exp2 以利用硬件快速指令)。
  2. chunk 内因果扫描:for k in T.Pipelined(loop_range, num_stages=num_stages) 流水化 K 维循环。每一拍内:
    • 载入 cb 块并逐元素乘 exp2(dA_m * p - dA_k * p) 得到跨时间步的相对衰减;
    • 再乘 dt 列向量;
    • 用 T.if_then_else(m 起始行 >= k 起始列) 施加下三角因果掩码;
    • 载入 x 块并执行 T.gemm(cb_local, x_shared, acc_o) 累加。 这一整段与 PyTorch 参考实现 ref_program 中 scores_decay = cb * decay; masked_fill(~tril) 再 einsum 的逻辑一一对应(见 benchmark_mamba_chunk_scan.py 与 benchmark_mamba_chunk_scan.py)。
  3. 跳跃连接与写回:累加 x_residual * D[bz],经 acc_o_shared 写回 Output 对应切片。cb_shared 与 x_residual_shared 通过 T.annotate_layout 绑定 tilelang.layout.make_swizzled_layout 以减少 shared memory bank conflict。

5.3 自动调优搜索空间

get_configs() 定义的默认搜索空间为(见 benchmark_mamba_chunk_scan.py):

  • block_M ∈ {64, 128, 256}(chunk 时间维切块)
  • block_N ∈ {32, 64}(headdim 维切块)
  • block_K ∈ {64, 128, 256}(K 维流水步长)
  • block_Dstate ∈ {128}(固定为 dstate)
  • num_stages ∈ {1, 2, 3, 4, 5}(T.Pipelined 流水级数)

笛卡尔积共 3×2×3×1×5 = 90 个候选配置。@autotune 装饰器(实现在 tilelang/autotuner/tuner.py)会把每个配置映射为 chunk_scan_fwd 的同名形参,多线程并行编译后逐个做正确性校验与计时(warmup=10, rep=10),计时底层走 tilelang/profiler/bench.py 的 do_bench(基于 CUDA event,含缓存刷新与多种回读模式)。调优结果带缓存:以函数源码、形状参数、配置集合与编译/剖析参数哈希为 key 落盘,同形状重复调用可直接命中缓存。若不想走调优流程,也可按 autotune 文档提示直接在调用时显式传入 block_M/block_N/... 等可整参数跳过搜索。

六、适用前提与限制

  • 本 kernel 为 fp16 输入 / fp32 累加 的 CUDA 实现,评测数据仅对 H800 SXM(图注 H100)环境有效,其他 GPU 需重新实测;
  • 复现依赖 mamba_ssm 与 helion(对比基线时)及 einops(参考实现中 rearrange/repeat),安装方式见脚本头部的 ImportError 提示;
  • seq_len 需能被 chunk_size 整除(参考实现中有 assert seqlen == nchunks * chunk_size);
  • 文中所有吞吐数字仅反映该 commit 与该环境下的测量,不应外推为跨平台的性能承诺。

小结

这份基准展示了 TileLang 在 Mamba2 这类"chunk 内矩阵乘 + 状态递推"混合算子上的标准工作流:用 T.prim_func 显式描述 shared/fragment 数据流与 T.Pipelined 流水,用 @autotune 对分块尺寸与流水级数做穷举搜索,再与 Triton、Helion 生态的同类 kernel 在同一 do_bench 计时口径下对拍。完整的可运行入口是 benchmark/mamba2/benchmark_mamba_chunk_scan.py,评测记录与数据表见 benchmark/mamba2/README.md,调优器机制可继续深入 tilelang/autotuner/tuner.py。

【免费下载链接】tilelang Domain-specific language designed to streamline the development of high-performance GPU/CPU/Accelerators kernels 【免费下载链接】tilelang 项目地址: https://gitcode.com/GitHub_Trending/ti/tilelang

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值