TileLang
Trace the internal phase timeline of a TileLang kernel by adding device-side scope markers.
G-Watch can trace the internal phases of a TileLang kernel. You mark regions
inside the T.prim_func with device-side scope markers, run the kernel under
G-Watch, and get back a per-thread timeline of when each phase ran — useful for
spotting pipeline bubbles, synchronization overhead, and warp-role scheduling.
Example
A runnable example is available at
examples/cuda/trace/trace_tilelang_matmul.py.
pip install tilelang
# for humanized visualization
python3 examples/cuda/trace/trace_tilelang_matmul.py --report trace.html
# for agentic visualization
python3 examples/cuda/trace/trace_tilelang_matmul.py --report trace.json
gwatch show trace.json
Mark scopes in the kernel
Import the TileLang trace helper and wrap each region with scope_start /
scope_end. The markers are TIR statements, so emit them through T.evaluate(...)
inside the kernel body; each region uses a unique integer id.
import gwatch.cuda.trace.tilelang as gw_trace
import tilelang
import tilelang.language as T
@tilelang.jit(out_idx=[-1])
def make_mul(n, block):
@T.prim_func
def main(A: T.Tensor((n,), "float32"),
B: T.Tensor((n,), "float32"),
C: T.Tensor((n,), "float32")):
with T.Kernel(T.ceildiv(n, block), threads=block) as bx:
for i in T.Parallel(block):
idx = bx * block + i
T.evaluate(gw_trace.scope_start(1)) # load
a = A[idx]
b = B[idx]
T.evaluate(gw_trace.scope_end(1))
T.evaluate(gw_trace.scope_start(2)) # store
C[idx] = a * b
T.evaluate(gw_trace.scope_end(2))
return main
Build and trace
TileLang compiles to a single-arch .cubin (pure SASS, no embedded PTX), so
CUPTI's module capture has no PTX to hand to the tracer. Export the kernel's PTX
explicitly with kernel.export_ptx(...) and point the TileLang PTX-cache loader
(TILELANG_CACHE_PATH) at it so tracing can find and instrument it.
import os, tempfile
import gwatch.libpygwatch as pygwatch
from gwatch.cuda.trace import do_trace
from gwatch.common.format import File
from gwatch.cuda.trace.format import Section_IntraKernelTrace
pygwatch.init_cupti_hooks() # install CUPTI hooks before the first module load
kernel = make_mul(N, BLOCK) # the @tilelang.jit kernel above, with scope markers
# TileLang's cubin is SASS-only, so export the PTX and point the loader at it.
ptx_dir = os.path.join(tempfile.gettempdir(), "gw_tilelang_ptx")
os.makedirs(ptx_dir, exist_ok=True)
kernel.export_ptx(os.path.join(ptx_dir, "main_kernel.ptx"))
os.environ["TILELANG_CACHE_PATH"] = ptx_dir
result = do_trace(
fn=lambda: kernel(x, y), # first execution of the kernel
kernel_name_pattern=r".*main.*", # regex on the mangled prototype
dsl="tilelang", # TileLang — trace uses the exported PTX
scope_name_map={1: "load", 2: "store"},
instrumentation_tier="ptx",
)
# Render the trace to an interactive HTML report (use a .json path for the
# machine-readable archive).
section = Section_IntraKernelTrace()
section.add_run(result)
report = File(title="Intra-kernel trace")
report.add_section(section)
report.render("trace.html")
A few things to note:
instrumentation_tier="ptx"as current OSS G-Watch supports PTX-based instrumentation. SASS-level instrumentation would be open-sourced once ready.- Export PTX. TileLang's cubin has no embedded PTX, so
kernel.export_ptx(...)plusTILELANG_CACHE_PATHare required for tracing to find the kernel's PTX. dsl="tilelang"tells G-Watch to recover PTX from the exported cache.scope_name_map(optional) turns the integer ids into the labels shown in the report.Section_IntraKernelTracerenders to interactive HTML (.html) or a machine-readable JSON (.json) archive, picked from the output extension.
