mirror of
https://github.com/tile-ai/tilelang.git
synced 2026-10-21 06:40:23 +08:00
* [Refactor] Update KernelParam integration across modules - Replaced instances of TensorType with KernelParam in various modules to standardize parameter handling. - Updated JITKernel, BaseKernelAdapter, and CythonKernelAdapter to utilize KernelParam for improved type consistency. - Enhanced Profiler class to include KernelParam in its parameters, ensuring better integration with the new parameter structure. - Adjusted tensor handling in utility functions to accommodate the new KernelParam type, improving overall code clarity and maintainability. - Updated copyright headers to reflect the correct organization. * [Refactor] Clean up whitespace in kernel, profiler, and tensor modules - Added blank lines for improved readability in kernel.py, __init__.py, and tensor.py. - Enhanced code clarity by ensuring consistent formatting across these modules. * [Enhancement] Add detailed docstrings to KernelParam and Profiler classes - Enhanced KernelParam class with comprehensive docstrings for better understanding of its purpose and methods. - Updated Profiler class to include detailed docstrings for its attributes and methods, improving code documentation and usability. - Removed unused do_bench function to streamline the profiler module and improve clarity. * [Refactor] Update type hints in do_bench function and clean up whitespace in profiler module - Changed type hints for grad_to_none and quantiles parameters in do_bench function to use Optional for better clarity. - Added a blank line in __init__.py for improved readability and consistency in the profiler module. * [Refactor] Update type hint in do_bench function for consistency - Changed the return type hint in the do_bench function from a union type to a more explicit List type for better clarity and consistency in type annotations. * [Refactor] Update return type hint in do_bench function for clarity - Changed the return type hint in the do_bench function from a union type to Union[float, List[float]] for improved clarity and consistency in type annotations. * [Enhancement] Add func property to Profiler class for adapter access - Introduced a new property `func` in the Profiler class to provide access to the adapter, ensuring that the adapter is set before retrieval. This enhancement improves the usability of the Profiler class by allowing easier access to the adapter functionality. * [Refactor] Update kernel compilation and profiling in tests - Replaced instances of `TL.lower` and `TL.Profiler` with `tilelang.compile` and the new profiler interface across multiple test files. - Enhanced the kernel compilation process to utilize the updated API, improving consistency and maintainability in the testing framework. - Updated assertions to use the new profiler methods for better clarity and functionality in performance testing. * [Refactor] Simplify kernel invocation and remove unused parameters in tests - Updated the kernel invocation in `test_tilelang_dynamic_symbolic.py` to directly assign the result to `C`, improving clarity. - Removed the `execution_backend` parameter from `tilelang.compile` calls in `test_tilelang_jit_callback.py` and `test_tilelang_jit_gemm.py` for consistency with the updated API. - Commented out the call to `tilelang.testing.main()` in `test_tilelang_jit_callback.py` and replaced it with a direct call to `test_gemm_jit_kernel()` to streamline test execution. - Adjusted the dtype mapping in `TorchDLPackKernelAdapter` to use the parameter's dtype directly, enhancing code simplicity. * [Refactor] Remove unused imports in test files for cleaner code - Eliminated unnecessary imports of `tilelang` as `TL` in various test files to enhance code clarity and maintainability. - Updated multiple test files to streamline the codebase and reduce potential confusion from unused references. * [Refactor] Simplify kernel invocation in tilelang kernel test - Updated the kernel invocation in `test_tilelang_kernel_bf16_gemm_mma.py` to directly assign the result to `C`, enhancing code clarity and consistency with recent changes in the API. * [Refactor] Simplify kernel invocation in tilelang kernel tests - Updated kernel invocations in multiple test files to directly assign the result to `C`, improving code clarity and consistency with the updated API. - Removed unnecessary initialization of `C` as a zero tensor, streamlining the code further. * [Refactor] Update kernel invocation in tilelang transform tests - Replaced the use of `TL.Profiler` with `tilelang.compile` in `test_tilelang_transform_simplify.py`, enhancing code clarity and consistency with the updated API. - Streamlined the kernel invocation process by directly assigning the result to `C`, improving readability and maintainability of the test code.
178 lines
6.0 KiB
Python
178 lines
6.0 KiB
Python
import torch
|
|
import torch.backends
|
|
import tilelang.testing
|
|
from tilelang import tvm as tvm
|
|
from tvm import DataType
|
|
import tilelang.language as T
|
|
from tilelang.intrinsics import get_swizzle_layout
|
|
from tilelang.transform import simplify_prim_func
|
|
|
|
tilelang.testing.set_random_seed(0)
|
|
|
|
|
|
def make_swizzle_layout(shared_buf):
|
|
dtype = shared_buf.dtype
|
|
shape = shared_buf.shape
|
|
|
|
can_swizzle = shape[-1] * DataType(dtype).bits == 512
|
|
if not can_swizzle:
|
|
return T.Layout(shape, lambda *args: args)
|
|
|
|
def transform_func(i, j):
|
|
new_warp_i, new_warp_j = get_swizzle_layout(i, j, shape[-1], dtype)
|
|
return [new_warp_i, new_warp_j]
|
|
|
|
return T.Layout(shape, transform_func)
|
|
|
|
|
|
@simplify_prim_func
|
|
def tl_matmul_simt(
|
|
M,
|
|
N,
|
|
K,
|
|
in_dtype,
|
|
out_dtype,
|
|
accum_dtype,
|
|
):
|
|
assert in_dtype in [
|
|
"float16",
|
|
"int8",
|
|
], "Currently only float16 and int8 are supported"
|
|
assert out_dtype in [
|
|
"float16",
|
|
"float32",
|
|
"int32",
|
|
], "Currently only float16, float32 and int32 are supported"
|
|
|
|
# This is a debug config
|
|
block_size_x = 8
|
|
block_size_y = 8
|
|
thread_row_tiles = 16
|
|
thread_col_tiles = 16
|
|
chunk = 16
|
|
|
|
shared_scope = "shared"
|
|
|
|
block_M = block_size_x * thread_row_tiles
|
|
block_N = block_size_y * thread_col_tiles
|
|
block_K = chunk
|
|
|
|
# Pipeline Stage
|
|
|
|
A_shape = (M, K)
|
|
B_shape = (N, K)
|
|
C_shape = (M, N)
|
|
A_shared_shape = (block_M, block_K)
|
|
B_shared_shape = (block_N, block_K)
|
|
|
|
threads = thread_row_tiles * thread_col_tiles
|
|
local_size_a = block_M // thread_row_tiles
|
|
local_size_b = block_N // thread_col_tiles
|
|
local_size_c = (block_M // thread_row_tiles) * (block_N // thread_col_tiles)
|
|
|
|
micro_size_k = 128 // DataType(in_dtype).bits
|
|
dp4a_size = 4
|
|
use_dp4a = in_dtype == "int8" and accum_dtype == "int32"
|
|
|
|
@T.prim_func
|
|
def main(
|
|
A: T.Buffer(A_shape, in_dtype),
|
|
B: T.Buffer(B_shape, in_dtype),
|
|
C: T.Buffer(C_shape, 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(A_shared_shape, in_dtype, scope=shared_scope)
|
|
B_shared = T.alloc_shared(B_shared_shape, in_dtype, scope=shared_scope)
|
|
|
|
A_local = T.alloc_local((local_size_a, micro_size_k), in_dtype)
|
|
B_local = T.alloc_local((local_size_b, micro_size_k), in_dtype)
|
|
C_local = T.alloc_local((local_size_c,), accum_dtype)
|
|
|
|
tid = T.get_thread_binding()
|
|
|
|
warp_m = tid % thread_row_tiles
|
|
warp_n = tid // thread_row_tiles
|
|
|
|
T.clear(C_local)
|
|
|
|
for ko in T.serial(K // block_K):
|
|
|
|
# Load A into shared memory
|
|
for i, k in T.Parallel(block_M, block_K):
|
|
A_shared[i, k] = A[by * block_M + i, ko * block_K + k]
|
|
|
|
# Load B into shared memory
|
|
for j, k in T.Parallel(block_N, block_K):
|
|
B_shared[j, k] = B[bx * block_N + j, ko * block_K + k]
|
|
|
|
for ki in T.serial((block_K // micro_size_k)):
|
|
for i in T.serial(local_size_a):
|
|
for mk in T.vectorized(micro_size_k):
|
|
A_local[i, mk] = A_shared[warp_m * local_size_a + i,
|
|
ki * micro_size_k + mk]
|
|
|
|
for i in T.serial(local_size_b):
|
|
for mk in T.vectorized(micro_size_k):
|
|
B_local[i, mk] = B_shared[warp_n * local_size_b + i,
|
|
ki * micro_size_k + mk]
|
|
|
|
for i, j in T.grid(local_size_a, local_size_b):
|
|
for mk in T.serial(micro_size_k // dp4a_size):
|
|
if use_dp4a:
|
|
T.dp4a(A_local[i, mk * dp4a_size], B_local[j, mk * dp4a_size],
|
|
C_local[i * local_size_b + j])
|
|
else:
|
|
for dp4a_idx in T.serial(dp4a_size):
|
|
C_local[i * local_size_b +
|
|
j] += A_local[i, mk * dp4a_size +
|
|
dp4a_idx] * B_local[j, mk * dp4a_size +
|
|
dp4a_idx]
|
|
|
|
for i, j in T.grid(local_size_a, local_size_b):
|
|
C[by * block_M + warp_m * local_size_a + i,
|
|
bx * block_N + warp_n * local_size_b + j] = C_local[i * local_size_b + j]
|
|
|
|
return main
|
|
|
|
|
|
def assert_tl_matmul_correctness(M, N, K, in_dtype, out_dtype, accum_dtype):
|
|
matmul = tl_matmul_simt(M, N, K, in_dtype, out_dtype, accum_dtype)
|
|
kernel = tilelang.compile(matmul, out_idx=[2])
|
|
profiler = kernel.get_profiler()
|
|
|
|
src_code = kernel.get_kernel_source()
|
|
print(src_code)
|
|
# src_code is the generated cuda source
|
|
assert src_code is not None
|
|
|
|
if in_dtype == "int8":
|
|
A = torch.randint(-128, 127, (M, K), device="cuda", dtype=torch.int8)
|
|
B = torch.randint(-128, 127, (N, K), device="cuda", dtype=torch.int8)
|
|
else:
|
|
A = torch.rand(M, K, device="cuda", dtype=getattr(torch, in_dtype))
|
|
B = torch.rand(N, K, device="cuda", dtype=getattr(torch, in_dtype))
|
|
|
|
C = kernel(A, B)
|
|
|
|
latency = profiler.do_bench()
|
|
|
|
# Ensure that the latency is not None
|
|
assert latency is not None
|
|
|
|
# Get Reference Result
|
|
ref_c = torch.matmul(A.to(torch.float32), B.T.to(torch.float32)).to(getattr(torch, accum_dtype))
|
|
print(C)
|
|
print(ref_c)
|
|
torch.testing.assert_close(C, ref_c, rtol=1e-2, atol=1e-2)
|
|
|
|
|
|
def test_assert_tl_matmul():
|
|
assert_tl_matmul_correctness(128, 128, 128, "float16", "float16", "float16")
|
|
assert_tl_matmul_correctness(128, 256, 256, "float16", "float32", "float32")
|
|
assert_tl_matmul_correctness(128, 256, 256, "int8", "int32", "int32")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
tilelang.testing.main()
|