TileLang is a Python domain-specific language that compiles high-performance GPU kernels through TVM. This tutorial validates the CUDA environment, establishes benchmarking and numerical-verification utilities, then implements vector addition, tiled tensor-core matrix multiplication, schedule exploration, fused GEMM epilogues, row-wise softmax, and FlashAttention. The process works directly with TileLang’s shared-memory tiles, register fragments, pipelined loops, parallel iteration primitives, reductions, and tensor-core GEMM operators while letting the compiler manage thread mapping, memory layouts, synchronization, vectorization, and low-level CUDA instruction generation. Kernels are compared against PyTorch and cuBLAS baselines, generated CUDA source is inspected, memory and compute throughput are evaluated, and autotuning identifies architecture-dependent configurations.
Setup and environment
import os
import sys
import math
import time
import subprocess
import traceback
def _sh(cmd: str) -> int:
print(f"$ {cmd}", flush=True)
return subprocess.run(cmd, shell=True).returncode
def _bootstrap():
"""Install tilelang if missing. Stable wheel first, nightly as a fallback."""
try:
import tilelang
return
except ImportError:
pass
print(">> installing tilelang (this pulls a bundled TVM, ~1-3 min)\n")
_sh(f"{sys.executable} -m pip install -q tilelang")
try:
import tilelang
return
except ImportError:
print(">> stable wheel unusable, trying nightly channel")
_sh(f"{sys.executable} -m pip install -q tilelang "
f"-f https://tile-ai.github.io/whl/nightly")
import tilelang
if os.path.isdir("/usr/local/cuda"):
os.environ.setdefault("CUDA_HOME", "/usr/local/cuda")
os.environ["PATH"] = os.environ.get("PATH", "") + ":/usr/local/cuda/bin"
_bootstrap()
import torch
import torch.nn.functional as F
import tilelang
import tilelang.language as T
def banner(title: str):
print("\n" + "=" * 78)
print(f" {title}")
print("=" * 78, flush=True)
def bench(fn, warmup: int = 10, rep: int = 50) -> float:
"""Median-ish latency in milliseconds, measured with CUDA events."""
for _ in range(warmup):
fn()
torch.cuda.synchronize()
start, end = torch.cuda.Event(True), torch.cuda.Event(True)
start.record()
for _ in range(rep):
fn()
end.record()
torch.cuda.synchronize()
return start.elapsed_time(end) / rep
def check(actual: torch.Tensor, ref: torch.Tensor, name: str, tol: float = 2e-2) -> bool:
"""Relative-Frobenius-norm check. Far more meaningful than atol for fp16."""
a, r = actual.float(), ref.float()
rel = (a - r).norm() / r.norm().clamp_min(1e-12)
amax = (a - r).abs().max().item()
ok = bool(rel < tol) and math.isfinite(amax)
print(f" [{'PASS' if ok else 'FAIL'}] {name}: rel_err={rel:.3e} max_abs={amax:.3e}")
return ok
banner("0. ENVIRONMENT")
assert torch.cuda.is_available(), "No GPU. Runtime -> Change runtime type -> GPU."
DEV = torch.device("cuda")
PROPS = torch.cuda.get_device_properties(0)
CC = torch.cuda.get_device_capability(0)
SM = CC[0] * 10 + CC[1]
print(f" tilelang : {getattr(tilelang, '__version__', 'unknown')}")
print(f" torch : {torch.__version__}")
print(f" GPU : {PROPS.name} (sm_{SM}, {PROPS.multi_processor_count} SMs, "
f"{PROPS.total_memory/2**30:.1f} GiB)")
SMEM_CAP = 48 * 1024 if SM < 80 else 96 * 1024
DEFAULT_STAGES = 2 if SM < 80 else 3
print(f" smem budget: {SMEM_CAP//1024} KB/block, default num_stages={DEFAULT_STAGES}")
print(" note: tilelang caches compiled kernels in ~/.tilelang/cache, so a")
print(" second run of this cell is dramatically faster.")
@tilelang.jit(out_idx=[-1])
def make_vector_add(N: int, block_N: int = 256, dtype: str = "float32"):
@T.prim_func
def main(A: T.Tensor((N,), dtype),
B: T.Tensor((N,), dtype),
C: T.Tensor((N,), dtype)):
with T.Kernel(T.ceildiv(N, block_N), threads=256) as bx:
for i in T.Parallel(block_N):
C[bx * block_N + i] = A[bx * block_N + i] + B[bx * block_N + i]
return main
def section_1():
banner("1. HELLO, TILE — vector add + generated CUDA")
N = 1 << 22
add = make_vector_add(N, block_N=256)
a = torch.randn(N, device=DEV)
b = torch.randn(N, device=DEV)
c = add(a, b)
check(c, a + b, "vector_add")
ms = bench(lambda: add(a, b))
gbs = 3 * N * 4 / (ms * 1e-3) / 1e9
ref_ms = bench(lambda: a + b)
print(f" tilelang: {ms*1e3:8.1f} us ({gbs:6.1f} GB/s)")
print(f" torch : {ref_ms*1e3:8.1f} us <- both are pure bandwidth, so a tie"
f" is the correct outcome")
src = add.get_kernel_source()
print("\n --- generated device code (first 32 lines) ---")
for line in src.splitlines()[:32]:
print(" " + line)
print(" ---------------------------------------------")
The Google Colab CUDA environment is configured, TileLang is installed with a nightly fallback, and the required PyTorch and TileLang modules are imported. Reusable benchmarking, validation, and reporting utilities are defined to measure kernel latency and compare numerical outputs using relative error. A TileLang vector-add kernel is implemented, executed on the GPU, and its bandwidth is compared with PyTorch. The CUDA source generated by the compiler is inspected.
Tiled tensor-core matrix multiplication
@tilelang.jit(out_idx=[-1])
def make_matmul(M: int, N: int, K: int,
block_M: int = 128, block_N: int = 128, block_K: int = 32,
num_stages: int = 3, threads: int = 128,
use_swizzle: bool = False,
dtype: str = "float16", accum_dtype: str = "float"):
@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(T.ceildiv(N, block_N),
T.ceildiv(M, block_M),
threads=threads) as (bx, by):
A_shared = T.alloc_shared((block_M, block_K), dtype)
B_shared = T.alloc_shared((block_K, block_N), dtype)
C_local = T.alloc_fragment((block_M, block_N), accum_dtype)
if use_swizzle:
T.use_swizzle(panel_size=10, enable=True)
T.clear(C_local)
for ko in T.Pipelined(T.ceildiv(K, block_K), num_stages=num_stages):
T.copy(A[by * block_M, ko * block_K], A_shared)
T.copy(B[ko * block_K, bx * block_N], B_shared)
T.gemm(A_shared, B_shared, C_local)
T.copy(C_local, C[by * block_M, bx * block_N])
return main
def smem_bytes(block_M, block_N, block_K, stages, itemsize=2):
return (block_M * block_K + block_K * block_N) * itemsize * stages
def section_2():
banner("2. MEMORY HIERARCHY — tiled tensor-core GEMM")
M = N = K = 2048
bm, bn, bk, st = 128, 128, 32, DEFAULT_STAGES
while smem_bytes(bm, bn, bk, st) > SMEM_CAP and st > 1:
st -= 1
print(f" config: {bm}x{bn}x{bk}, num_stages={st}, "
f"smem={smem_bytes(bSource Read original →


