mirror of
https://github.com/tile-ai/tilelang.git
synced 2026-10-24 07:04:00 +08:00
221 lines
6.6 KiB
Python
221 lines
6.6 KiB
Python
"""Tests for TMA store (shared -> global).
|
|
|
|
Explicit T.tma_copy(shared_buf, global_buf) emits tma_store + tma_store_arrive
|
|
(no wait). The user must explicitly call T.tma_store_wait() for
|
|
synchronization.
|
|
|
|
Plain T.copy(shared_buf, global_buf) may also auto-lower to tma_store when the
|
|
store-side TMA constraints are satisfied. In that case lowering emits both
|
|
tma_store_arrive and tma_store_wait automatically.
|
|
"""
|
|
|
|
from tilelang import tvm as tvm
|
|
import tilelang.testing
|
|
import tilelang.language as T
|
|
import tilelang
|
|
|
|
|
|
def matmul_tma_store(
|
|
M,
|
|
N,
|
|
K,
|
|
block_M,
|
|
block_N,
|
|
block_K,
|
|
in_dtype,
|
|
out_dtype,
|
|
accum_dtype,
|
|
threads,
|
|
num_stages,
|
|
):
|
|
"""GEMM with T.copy for loads and T.tma_copy for the final store (shared -> global)."""
|
|
A_shape = (M, K)
|
|
B_shape = (K, N)
|
|
|
|
@T.prim_func
|
|
def main(
|
|
A: T.Tensor(A_shape, in_dtype),
|
|
B: T.Tensor(B_shape, in_dtype),
|
|
C: T.Tensor((M, N), out_dtype),
|
|
):
|
|
with T.Kernel(T.ceildiv(N, block_N), T.ceildiv(M, block_M), threads=threads) as (bx, by):
|
|
A_shared = T.alloc_shared((block_M, block_K), in_dtype)
|
|
B_shared = T.alloc_shared((block_K, block_N), in_dtype)
|
|
C_shared = T.alloc_shared((block_M, block_N), out_dtype)
|
|
C_local = T.alloc_fragment((block_M, block_N), accum_dtype)
|
|
T.clear(C_local)
|
|
for k in T.Pipelined(T.ceildiv(K, block_K), num_stages=num_stages):
|
|
T.copy(A[by * block_M, k * block_K], A_shared)
|
|
T.copy(B[k * block_K, bx * block_N], B_shared)
|
|
T.gemm(A_shared, B_shared, C_local)
|
|
# Store: fragment -> shared, then shared -> global via T.tma_copy
|
|
T.copy(C_local, C_shared)
|
|
T.tma_copy(C_shared, C[by * block_M, bx * block_N])
|
|
T.tma_store_wait()
|
|
|
|
return main
|
|
|
|
|
|
def run_gemm_tma_store(num_stages):
|
|
M, N, K = 1024, 1024, 1024
|
|
block_M, block_N, block_K = 128, 128, 32
|
|
in_dtype = T.float16
|
|
out_dtype = T.float16
|
|
accum_dtype = T.float32
|
|
threads = 128
|
|
|
|
program = matmul_tma_store(
|
|
M,
|
|
N,
|
|
K,
|
|
block_M,
|
|
block_N,
|
|
block_K,
|
|
in_dtype,
|
|
out_dtype,
|
|
accum_dtype,
|
|
threads,
|
|
num_stages,
|
|
)
|
|
kernel = tilelang.compile(
|
|
program,
|
|
out_idx=[2],
|
|
pass_configs={tilelang.PassConfigKey.TL_DISABLE_WARP_SPECIALIZED: True},
|
|
)
|
|
kernel_source = kernel.get_kernel_source()
|
|
print(kernel_source)
|
|
# exit()
|
|
# Verify that the generated kernel contains tma_store_arrive but NOT tma_store_wait
|
|
# (the wait is issued separately by the user via T.tma_store_wait)
|
|
assert "tma_store_arrive" in kernel_source, "Expected tma_store_arrive in kernel source"
|
|
|
|
profiler = kernel.get_profiler()
|
|
|
|
def ref_program(A, B):
|
|
import torch
|
|
|
|
C = torch.matmul(A.to(torch.float), B.to(torch.float))
|
|
return C.to(torch.__getattribute__(out_dtype))
|
|
|
|
profiler.assert_allclose(ref_program, atol=1e-2, rtol=1e-2)
|
|
|
|
|
|
def auto_tma_store_copy(M, N, block_M, block_N, dtype, threads):
|
|
@T.prim_func
|
|
def main(
|
|
A: T.Tensor((M, N), dtype),
|
|
C: T.Tensor((M, N), dtype),
|
|
):
|
|
with T.Kernel(T.ceildiv(N, block_N), T.ceildiv(M, block_M), threads=threads) as (bx, by):
|
|
A_shared = T.alloc_shared((block_M, block_N), dtype)
|
|
T.copy(A[by * block_M, bx * block_N], A_shared)
|
|
T.copy(A_shared, C[by * block_M, bx * block_N])
|
|
|
|
return main
|
|
|
|
|
|
def full_shape_tma_store_1d(dtype, threads):
|
|
@T.prim_func
|
|
def main(
|
|
C: T.Tensor((128, 128), dtype),
|
|
):
|
|
with T.Kernel(threads=threads):
|
|
C_shared = T.alloc_shared((128, 128), dtype)
|
|
T.copy(C_shared, C)
|
|
|
|
return main
|
|
|
|
|
|
def tma_store_wait_read_option(read):
|
|
@T.prim_func
|
|
def main():
|
|
with T.Kernel(threads=128):
|
|
T.tma_store_wait(read=read)
|
|
|
|
return main
|
|
|
|
|
|
def run_auto_tma_store_copy():
|
|
M = N = 256
|
|
block_M = block_N = 128
|
|
dtype = T.float16
|
|
threads = 128
|
|
|
|
program = auto_tma_store_copy(M, N, block_M, block_N, dtype, threads)
|
|
kernel = tilelang.compile(
|
|
program,
|
|
out_idx=[1],
|
|
pass_configs={tilelang.PassConfigKey.TL_DISABLE_WARP_SPECIALIZED: True},
|
|
)
|
|
kernel_source = kernel.get_kernel_source()
|
|
assert "tma_store_arrive" in kernel_source, "Expected auto tma_store_arrive in kernel source"
|
|
assert "tma_store_wait" in kernel_source, "Expected auto tma_store_wait in kernel source"
|
|
assert "tl::tma_store_wait<0, true>();" in kernel_source, "Expected auto tma_store_wait to use default read wait"
|
|
|
|
profiler = kernel.get_profiler()
|
|
|
|
def ref_program(A):
|
|
return A
|
|
|
|
profiler.assert_allclose(ref_program, atol=1e-2, rtol=1e-2)
|
|
|
|
|
|
def run_full_shape_tma_store_1d_codegen():
|
|
program = full_shape_tma_store_1d(T.float32, 128)
|
|
kernel = tilelang.compile(
|
|
program,
|
|
pass_configs={tilelang.PassConfigKey.TL_DISABLE_WARP_SPECIALIZED: True},
|
|
)
|
|
kernel_source = kernel.get_kernel_source()
|
|
assert "tl::tma_store" in kernel_source, "Expected TMA store in kernel source"
|
|
assert "CUtensorMap" not in kernel_source, "Expected pointer-based 1D TMA store"
|
|
|
|
|
|
def run_tma_store_wait_read_option_codegen():
|
|
default_kernel = tilelang.compile(
|
|
tma_store_wait_read_option(True),
|
|
pass_configs={tilelang.PassConfigKey.TL_DISABLE_WARP_SPECIALIZED: True},
|
|
)
|
|
no_read_kernel = tilelang.compile(
|
|
tma_store_wait_read_option(False),
|
|
pass_configs={tilelang.PassConfigKey.TL_DISABLE_WARP_SPECIALIZED: True},
|
|
)
|
|
|
|
assert "tl::tma_store_wait<0, true>();" in default_kernel.get_kernel_source()
|
|
assert "tl::tma_store_wait<0, false>();" in no_read_kernel.get_kernel_source()
|
|
|
|
|
|
@tilelang.testing.requires_cuda
|
|
@tilelang.testing.requires_cuda_compute_version_ge(9, 0)
|
|
def test_tma_store_2_stages():
|
|
run_gemm_tma_store(num_stages=2)
|
|
|
|
|
|
@tilelang.testing.requires_cuda
|
|
@tilelang.testing.requires_cuda_compute_version_ge(9, 0)
|
|
def test_tma_store_3_stages():
|
|
run_gemm_tma_store(num_stages=3)
|
|
|
|
|
|
@tilelang.testing.requires_cuda
|
|
@tilelang.testing.requires_cuda_compute_version_ge(9, 0)
|
|
def test_plain_copy_auto_tma_store():
|
|
run_auto_tma_store_copy()
|
|
|
|
|
|
@tilelang.testing.requires_cuda
|
|
@tilelang.testing.requires_cuda_compute_version_ge(9, 0)
|
|
def test_plain_copy_full_shape_tma_store_uses_1d():
|
|
run_full_shape_tma_store_1d_codegen()
|
|
|
|
|
|
@tilelang.testing.requires_cuda
|
|
@tilelang.testing.requires_cuda_compute_version_ge(9, 0)
|
|
def test_tma_store_wait_read_option_codegen():
|
|
run_tma_store_wait_read_option_codegen()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# tilelang.testing.main()
|
|
test_tma_store_2_stages()
|