跳到主要内容
TileLang 系统教程

tilelang-tutorial.huandu.me

第 14 章 算子实战 II:GEMM 全家族

/code/14-operators-gemm/

05-ascend-gemm/ascend_gemm.py4302 字节
#!/usr/bin/env python3
"""第 14 章第 6 节:昇腾上的 GEMM 与 batched GEMM。

对照 `_vendor/tilelang-ascend-v0.1.2.000/examples/gemm/example_gemm.py` 与
`examples/batch_gemm/batch_gemm.py`,以及主干
`_vendor/tilelang-v0.1.15/examples/ascend/example_gemm.py`。

三个实现刚好是本章阶梯在昇腾上的翻版:

  ascend_gemm_basic       L1 + L0C + T.gemm_v0 + 显式 barrier(≈ 第 ② 级)
  ascend_gemm_auto        同上,但把同步/多缓冲/内存规划交给 pass_configs(≈ 第 ③ 级)
  ascend_batch_gemm       一维 grid 展平 (batch, m_block, n_block)(≈ 4.4 节)

运行前提:昇腾 NPU + CANN + torch_npu + TileLang-Ascend
(`_vendor/tilelang-ascend-v0.1.2.000`);PyTorch with torch_npu。
写作机上没有 NPU,本文件只做过静态核对(py_compile + API 比对),**未运行**。
"""

from __future__ import annotations

import argparse

import tilelang
import tilelang.language as T
import torch


@tilelang.jit(out_idx=[-1])
def ascend_gemm_basic(M, N, K, block_M, block_N, K_L1, dtype="float16", accum_dtype="float"):
    """最朴素的昇腾 GEMM:显式 barrier,不做流水。"""
    m_num = M // block_M
    n_num = N // block_N

    @T.prim_func
    def main(
        A: T.Tensor((M, K), dtype),
        B: T.Tensor((K, N), dtype),
        C: T.Tensor((M, N), dtype),
    ):
        with T.Kernel(m_num * n_num, is_npu=True) as (cid, _):
            bx = cid // n_num
            by = cid % n_num

            A_L1 = T.alloc_L1((block_M, K_L1), dtype)
            B_L1 = T.alloc_L1((K_L1, block_N), dtype)
            C_L0 = T.alloc_L0C((block_M, block_N), accum_dtype)

            with T.Scope("C"):
                for k in T.serial(T.ceildiv(K, K_L1)):
                    T.copy(A[bx * block_M, k * K_L1], A_L1)
                    T.copy(B[k * K_L1, by * block_N], B_L1)
                    T.barrier_all()
                    T.gemm_v0(A_L1, B_L1, C_L0, init=(k == 0))
                    T.barrier_all()
                T.copy(C_L0, C[bx * block_M, by * block_N])

    return main


ASCEND_PASS_CONFIGS = {
    tilelang.PassConfigKey.TL_ASCEND_AUTO_CV_COMBINE: True,
    tilelang.PassConfigKey.TL_ASCEND_AUTO_SYNC: True,
    tilelang.PassConfigKey.TL_ASCEND_MEMORY_PLANNING: True,
}


@tilelang.jit(out_idx=[-1], pass_configs=ASCEND_PASS_CONFIGS)
def ascend_batch_gemm(batch, M, N, K, block_M, block_N, K_L1,
                      dtype="float16", accum_dtype="float"):
    """batched GEMM:一维 grid 展平 (batch, m_block, n_block)。"""
    m_num = M // block_M
    n_num = N // block_N

    @T.prim_func
    def main(
        A_mat: T.Tensor((batch, M, K), dtype),
        B_mat: T.Tensor((batch, K, N), dtype),
        C_mat: T.Tensor((batch, M, N), dtype),
    ):
        total = batch * m_num * n_num
        with T.Kernel(total, is_npu=True) as (cid, _):
            bid = cid // (m_num * n_num)
            rem = cid % (m_num * n_num)
            bx = rem // n_num
            by = rem % n_num

            A_L1 = T.alloc_shared((block_M, K_L1), dtype)
            B_L1 = T.alloc_shared((K_L1, block_N), dtype)
            C_L0 = T.alloc_fragment((block_M, block_N), accum_dtype)

            for k in T.serial(T.ceildiv(K, K_L1)):
                T.copy(A_mat[bid, bx * block_M, k * K_L1], A_L1)
                T.copy(B_mat[bid, k * K_L1, by * block_N], B_L1)
                T.gemm_v0(A_L1, B_L1, C_L0, init=(k == 0))

            T.copy(C_L0, C_mat[bid, bx * block_M, by * block_N])

    return main


def main() -> int:
    parser = argparse.ArgumentParser(description="昇腾 GEMM / batched GEMM")
    parser.add_argument("--m", type=int, default=1024)
    parser.add_argument("--n", type=int, default=1024)
    parser.add_argument("--k", type=int, default=1024)
    parser.add_argument("--batch", type=int, default=8)
    args = parser.parse_args()

    m, n, k = args.m, args.n, args.k

    # 基础 GEMM
    gemm = ascend_gemm_basic(m, n, k, 128, 256, 64)
    a = torch.randn(m, k).half().npu()
    b = torch.randn(k, n).half().npu()
    c = gemm(a, b)
    torch.testing.assert_close(c, a @ b, rtol=1e-2, atol=1e-2)
    print("ascend_gemm_basic: 正确性通过")

    # batched GEMM
    batched = ascend_batch_gemm(args.batch, m, n, k, 128, 256, 64)
    ab = torch.randn(args.batch, m, k).half().npu()
    bb = torch.randn(args.batch, k, n).half().npu()
    cb = batched(ab, bb)
    torch.testing.assert_close(cb, torch.matmul(ab, bb), rtol=1e-2, atol=1e-2)
    print("ascend_batch_gemm: 正确性通过")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

← 浏览本章全部代码