跳到主要内容
TileLang 系统教程

tilelang-tutorial.huandu.me

第 09 章 核心原语详解

/code/09-core-primitives/

04-copy-gemm-sketches/gemm_primitive_variants.py4145 字节
"""T.gemm 的四种参数组合:transpose_A / transpose_B / policy / clear_accum。

静态核对来源:
  tilelang/language/gemm_op.py:38-122,160-210
    - M/N/K 推导与静态性检查 :77-92
    - clear_accum 改 access region :103
    - 签名(无 accum_dtype):160-169
  tilelang/tileop/base.py:5-12                (GemmWarpPolicy 三个取值)
  src/cuda/op/gemm.cc:336-395                 (指令选择与 warp 划分)

运行前提:NVIDIA GPU + CUDA + PyTorch(CUDA) + TileLang v0.1.15。
作者验证级别:仅静态核对(py_compile + API 逐条比对),未在硬件上运行。
"""

from __future__ import annotations

import torch
import tilelang
import tilelang.language as T


def make_kernel(M: int, N: int, K: int, *, transpose_a: bool, transpose_b: bool,
                policy: T.GemmWarpPolicy, clear_by_mask: bool,
                block_M: int = 128, block_N: int = 128, block_K: int = 64,
                threads: int = 256):
    """A 的形状由 transpose_a 决定;B 的形状由 transpose_b 决定。

    transpose_a=False -> A 是 [M, K];True -> A 是 [K, M]
    transpose_b=False -> B 是 [K, N];True -> B 是 [N, K]
    """
    a_shape = (K, M) if transpose_a else (M, K)
    b_shape = (N, K) if transpose_b else (K, N)

    @tilelang.jit(out_idx=-1)
    def build():
        @T.prim_func
        def main(
            A: T.Tensor(a_shape, "float16"),
            B: T.Tensor(b_shape, "float16"),
            C: T.Tensor((M, N), "float32"),
        ):
            with T.Kernel(T.ceildiv(N, block_N), T.ceildiv(M, block_M), threads=threads) as (bx, by):
                A_shared = T.alloc_shared(
                    (block_K, block_M) if transpose_a else (block_M, block_K), "float16"
                )
                B_shared = T.alloc_shared(
                    (block_N, block_K) if transpose_b else (block_K, block_N), "float16"
                )
                C_local = T.alloc_fragment((block_M, block_N), "float32")

                # 不想依赖 clear_accum 时,显式清零一次。
                if not clear_by_mask:
                    T.clear(C_local)

                for ko in T.Pipelined(K // block_K, num_stages=2):
                    if transpose_a:
                        T.copy(A[ko * block_K, by * block_M], A_shared)
                    else:
                        T.copy(A[by * block_M, ko * block_K], A_shared)
                    if transpose_b:
                        T.copy(B[bx * block_N, ko * block_K], B_shared)
                    else:
                        T.copy(B[ko * block_K, bx * block_N], B_shared)

                    # clear_accum 是逐次调用的参数:第一次调用时清零,之后累加。
                    # 注意它在 IR 层把 C 的 access region 从 "rw" 改成 "w"
                    # (gemm_op.py:103),因此不是纯运行期开关。
                    T.gemm(
                        A_shared,
                        B_shared,
                        C_local,
                        transpose_A=transpose_a,
                        transpose_B=transpose_b,
                        policy=policy,
                        clear_accum=clear_by_mask and (ko == 0),
                    )

                T.copy(C_local, C[by * block_M, bx * block_N])

        return main

    return build


def main() -> None:
    M = N = K = 512
    variants = [
        # (transpose_a, transpose_b, policy, clear_by_mask, 名字)
        (False, False, T.GemmWarpPolicy.Square, False, "nn-square"),
        (False, True, T.GemmWarpPolicy.FullRow, True, "nt-fullrow"),
        (True, False, T.GemmWarpPolicy.FullCol, True, "tn-fullcol"),
        (True, True, T.GemmWarpPolicy.Square, True, "tt-square"),
    ]
    for transpose_a, transpose_b, policy, clear_by_mask, name in variants:
        kernel = make_kernel(
            M, N, K,
            transpose_a=transpose_a,
            transpose_b=transpose_b,
            policy=policy,
            clear_by_mask=clear_by_mask,
        )()
        a = torch.randn((K, M) if transpose_a else (M, K), device="cuda", dtype=torch.float16)
        b = torch.randn((N, K) if transpose_b else (K, N), device="cuda", dtype=torch.float16)
        c = kernel(a, b)
        ref = (a.T if transpose_a else a).float() @ (b.T if transpose_b else b).float()
        torch.testing.assert_close(c, ref, rtol=1e-2, atol=1e-2)
        print(f"{name}: passed")


if __name__ == "__main__":
    main()

← 浏览本章全部代码