mirror of
https://github.com/tile-ai/tilelang.git
synced 2026-10-22 06:50:25 +08:00
Comparison-only snapshot of Ascend at 925bfbb9aff0fb57a4ed9fecc3591b8dcac21c8f.
Use upstream #3174 (62bba8d20d) as the logical synchronization point.
Preserve the complete tree of the previous comparison head d0d2f40be354cc0a4d6ed037b34278c8746fa031,
including submodule references. No source changes or additional integration
resolutions are introduced. This snapshot is for upstream comparison only.
202 lines
6.5 KiB
Python
202 lines
6.5 KiB
Python
import tilelang.language as T
|
|
from tilelang import tvm as tvm
|
|
import tilelang.testing
|
|
import tilelang as tl
|
|
import pytest
|
|
import torch
|
|
|
|
|
|
def view_test(N, M, dtype, new_dtype=None):
|
|
new_shape = [N // M, M]
|
|
if new_dtype:
|
|
from tvm import DataType
|
|
|
|
dtype_src = DataType(dtype)
|
|
dtype_dst = DataType(new_dtype)
|
|
src_bits = dtype_src.bits
|
|
dst_bits = dtype_dst.bits
|
|
scale = src_bits / dst_bits
|
|
new_shape[-1] = int(M * scale)
|
|
|
|
@T.prim_func
|
|
def main(
|
|
A: T.Tensor((N,), dtype),
|
|
B: T.Tensor(new_shape, new_dtype if new_dtype else dtype),
|
|
):
|
|
with T.Kernel(1) as _:
|
|
A_viewed = T.view(A, new_shape, dtype=new_dtype)
|
|
T.copy(A_viewed, B)
|
|
|
|
return main
|
|
|
|
|
|
def run_view(N, M, dtype, new_dtype=None):
|
|
program = view_test(N, M, dtype, new_dtype)
|
|
jit_kernel = tl.compile(program, out_idx=-1)
|
|
profiler = jit_kernel.get_profiler()
|
|
|
|
def ref_program(A):
|
|
if new_dtype:
|
|
torch_dtype = T.dtype(new_dtype).as_torch()
|
|
return A.view(N // M, M).view(dtype=torch_dtype)
|
|
return A.view(N // M, M)
|
|
|
|
profiler.assert_allclose(ref_program, atol=1e-2, rtol=1e-2)
|
|
|
|
|
|
def test_reshape_view():
|
|
# Test view with same dtype
|
|
run_view(1024, 32, T.float32)
|
|
run_view(2048, 64, T.float16)
|
|
|
|
# Test view with dtype conversion
|
|
run_view(1024, 32, T.float32, T.float16)
|
|
run_view(2048, 64, T.float16, T.float32)
|
|
|
|
|
|
def view_shape_mismatch_test(N, M, dtype, new_dtype=None):
|
|
new_shape = [N // M, M + 1]
|
|
if new_dtype:
|
|
from tvm import DataType
|
|
|
|
dtype_src = DataType(dtype)
|
|
dtype_dst = DataType(new_dtype)
|
|
src_bits = dtype_src.bits
|
|
dst_bits = dtype_dst.bits
|
|
scale = src_bits / dst_bits
|
|
new_shape[-1] = int(M * scale)
|
|
|
|
@T.prim_func
|
|
def main(
|
|
A: T.Tensor((N,), dtype),
|
|
B: T.Tensor(new_shape, new_dtype if new_dtype else dtype),
|
|
):
|
|
with T.Kernel(1) as _:
|
|
A_viewed = T.view(A, new_shape, dtype=new_dtype)
|
|
T.copy(A_viewed, B)
|
|
|
|
return main
|
|
|
|
|
|
def test_view_shape_mismatch():
|
|
with pytest.raises(ValueError, match="logical bit count"):
|
|
view_shape_mismatch_test(1024, 32, T.float32)
|
|
|
|
|
|
def test_view_subbyte_dtype_change():
|
|
A = tvm.tirx.decl_buffer((16, 32), "float4_e2m1fn", name="A")
|
|
A_viewed = T.view(A, (16, 16), dtype=T.uint8)
|
|
assert str(A_viewed.dtype) == "uint8"
|
|
assert tuple(int(dim) for dim in A_viewed.shape) == (16, 16)
|
|
assert A_viewed.data.same_as(A.data)
|
|
|
|
|
|
def fp4_to_uint8_view_test(rows_per_cta=16, mask_k=256):
|
|
@T.prim_func
|
|
def main(
|
|
A: T.Tensor((rows_per_cta, mask_k), T.bfloat16),
|
|
B: T.Tensor((rows_per_cta, mask_k // 2), T.uint8),
|
|
):
|
|
with T.Kernel(1, threads=256) as _:
|
|
A_frag = T.alloc_fragment((rows_per_cta, mask_k), T.bfloat16)
|
|
B_shared_fp4 = T.alloc_shared((rows_per_cta, mask_k), T.float4_e2m1fn)
|
|
B_shared_uint8 = T.view(B_shared_fp4, (rows_per_cta, mask_k // 2), dtype=T.uint8)
|
|
|
|
T.copy(A, A_frag)
|
|
for i, j in T.Parallel(rows_per_cta, mask_k):
|
|
B_shared_fp4[i, j] = T.cast(A_frag[i, j], T.float4_e2m1fn)
|
|
T.copy(B_shared_uint8, B)
|
|
|
|
return main
|
|
|
|
|
|
@tilelang.testing.requires_cuda
|
|
@tilelang.testing.requires_cuda_compute_version_ge(10, 0)
|
|
def test_view_shared_fp4_to_uint8_compile():
|
|
program = fp4_to_uint8_view_test()
|
|
kernel = tl.compile(program, out_idx=-1)
|
|
src = kernel.get_kernel_source()
|
|
assert "fp4_e2" in src
|
|
|
|
dummy_input = torch.randn((16, 256), device="cuda", dtype=torch.bfloat16)
|
|
output = kernel(dummy_input)
|
|
assert output.shape == (16, 128)
|
|
assert output.dtype == torch.uint8
|
|
|
|
|
|
def annotated_layout_on_dtype_changing_view_test():
|
|
@T.prim_func
|
|
def main(
|
|
A: T.Tensor((64, 64), T.float16),
|
|
B: T.Tensor((64, 128), T.int8),
|
|
):
|
|
with T.Kernel(1, threads=128) as _:
|
|
A_stage = T.alloc_shared((2, 64, 64), T.float16, scope="shared.dyn")
|
|
A_i8 = T.view(A_stage, (2, 64, 128), dtype=T.int8)
|
|
T.annotate_layout({A_i8: T.Layout((2, 64, 128), lambda s, i, j: [s, i, j])})
|
|
|
|
for i, j in T.Parallel(64, 64):
|
|
A_stage[0, i, j] = A[i, j]
|
|
|
|
for i, j in T.Parallel(64, 128):
|
|
B[i, j] = A_i8[0, i, j]
|
|
|
|
return main
|
|
|
|
|
|
@tilelang.testing.requires_cuda
|
|
def test_annotated_layout_on_dtype_changing_view_compile():
|
|
program = annotated_layout_on_dtype_changing_view_test()
|
|
kernel = tl.compile(program, out_idx=-1)
|
|
assert kernel.get_kernel_source()
|
|
|
|
|
|
def shared_reinterpret_view_test(shape, dtype, new_shape, new_dtype, swizzled):
|
|
"""Write through the shared allocation, read through its reinterpreting
|
|
view: the view's derived layout must induce the same storage-bit map as
|
|
the allocation's. The swizzled variant additionally covers the widening
|
|
contiguity proof and the TMA descriptor encoding of the derived layout."""
|
|
|
|
@T.prim_func
|
|
def main(
|
|
A: T.Tensor(shape, dtype),
|
|
B: T.Tensor(new_shape, new_dtype),
|
|
):
|
|
with T.Kernel(1, threads=128):
|
|
A_shared = T.alloc_shared(shape, dtype)
|
|
A_view = T.view(A_shared, new_shape, dtype=new_dtype)
|
|
if swizzled:
|
|
T.annotate_layout({A_shared: tilelang.layout.make_swizzled_layout(A_shared)})
|
|
T.copy(A, A_shared)
|
|
T.copy(A_view, B)
|
|
|
|
return main
|
|
|
|
|
|
def run_shared_reinterpret_view(shape, dtype, new_shape, new_dtype, swizzled):
|
|
program = shared_reinterpret_view_test(shape, dtype, new_shape, new_dtype, swizzled)
|
|
jit_kernel = tl.compile(program, out_idx=-1)
|
|
|
|
torch_dtype = T.dtype(dtype).as_torch()
|
|
lanes = torch.randn(shape[0], shape[1] * torch_dtype.itemsize // 2, device="cuda", dtype=torch.bfloat16)
|
|
a = lanes.view(dtype=torch_dtype)
|
|
d = jit_kernel(a)
|
|
ref = a.view(dtype=T.dtype(new_dtype).as_torch()).view(new_shape)
|
|
torch.testing.assert_close(d.view(torch.int16), ref.view(torch.int16), rtol=0, atol=0)
|
|
|
|
|
|
@tilelang.testing.requires_cuda
|
|
@pytest.mark.parametrize("swizzled", [False, True], ids=["linear", "swizzled"])
|
|
def test_shared_view_narrowing_numeric(swizzled):
|
|
run_shared_reinterpret_view((64, 64), T.float32, (64, 128), T.bfloat16, swizzled)
|
|
|
|
|
|
@tilelang.testing.requires_cuda
|
|
@pytest.mark.parametrize("swizzled", [False, True], ids=["linear", "swizzled"])
|
|
def test_shared_view_widening_numeric(swizzled):
|
|
run_shared_reinterpret_view((64, 128), T.bfloat16, (64, 64), T.float32, swizzled)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
tilelang.testing.main()
|