mirror of
https://github.com/tile-ai/tilelang.git
synced 2026-10-24 07:04:00 +08:00
* [Feature] Add Windows support with cross-platform minimal diff * [Feature] Add ccache support for Windows * [Feature] Add parallel autotune support for Windows * [Feature] Add nvrtc support for Windows * [Bugfix] Fix tilelibrary support for Windows * [Bugfix] Fix alignment for Windows * [Feature] Add clang-cl support for Windows * [Bugfix] Fix numeric limit for Windows * [Bugfix] Fix rand for Windows * [Misc] Use dynamic test workload to avoid hang * [Bugfix] Fix TMA alignment for Windows and sm120 * [Bugfix] Add unix-like hex formatter for Windows * [CI] Add Windows wheel job * [Feature] Flexible CUDA loader * fix rebase --------- Co-authored-by: SiriusNEO <chaofan@deepseek.com>
129 lines
3.9 KiB
Python
129 lines
3.9 KiB
Python
import warnings
|
|
|
|
import pytest
|
|
|
|
import tilelang
|
|
import tilelang.language as T
|
|
|
|
|
|
# 2^32 bf16 elements = 8 GiB. This is the upper-bound stress target; the
|
|
# helper below scales down only when the GPU cannot fit it.
|
|
_DEFAULT_TARGET_N = 2**32
|
|
# 2^28 bf16 elements = 512 MiB. Below this the test is no longer a useful
|
|
# fill-stress — skip instead of running a token-sized fill.
|
|
_FLOOR_N = 2**28
|
|
|
|
|
|
def _select_int64_stress_n(
|
|
bytes_per_elem: int = 2,
|
|
target_n: int = _DEFAULT_TARGET_N,
|
|
floor_n: int = _FLOOR_N,
|
|
) -> int:
|
|
"""Pick the largest power-of-two element count that fits in free VRAM.
|
|
|
|
Returns ``target_n`` (8 GiB at bf16) when the GPU has enough free
|
|
memory; otherwise halves until the allocation fits with a 512 MiB / 10%
|
|
headroom margin. Emits a UserWarning whenever the size is scaled below
|
|
``target_n`` so reduced coverage is visible in the test log. Skips the
|
|
test if even ``floor_n`` (~512 MiB) cannot be allocated.
|
|
"""
|
|
import torch
|
|
|
|
if not torch.cuda.is_available():
|
|
pytest.skip("int64 stress test requires a CUDA device")
|
|
|
|
free_bytes, total_bytes = torch.cuda.mem_get_info()
|
|
# Reserve headroom so the kernel launch / Torch caching / cuBLAS
|
|
# workspaces don't push us into OOM at the boundary. On an 8 GiB part
|
|
# only ~7 GiB is typically free even at idle.
|
|
headroom = max(512 * 1024 * 1024, free_bytes // 10)
|
|
usable = max(0, free_bytes - headroom)
|
|
|
|
n = target_n
|
|
while n > floor_n and n * bytes_per_elem > usable:
|
|
n //= 2
|
|
|
|
if n * bytes_per_elem > usable:
|
|
pytest.skip(
|
|
f"int64 stress test needs at least "
|
|
f"{(floor_n * bytes_per_elem) / (1024**3):.2f} GiB of free GPU "
|
|
f"memory (got {free_bytes / (1024**3):.2f} GiB free, "
|
|
f"{total_bytes / (1024**3):.2f} GiB total)"
|
|
)
|
|
|
|
if n < target_n:
|
|
warnings.warn(
|
|
f"int64 stress test scaled down: requested n={target_n} "
|
|
f"({target_n * bytes_per_elem / (1024**3):.2f} GiB) but only "
|
|
f"{free_bytes / (1024**3):.2f} GiB free / "
|
|
f"{total_bytes / (1024**3):.2f} GiB total VRAM available; "
|
|
f"using n={n} ({n * bytes_per_elem / (1024**3):.2f} GiB) instead.",
|
|
stacklevel=2,
|
|
)
|
|
|
|
return n
|
|
|
|
|
|
@tilelang.jit
|
|
def fill_symbolic(value: float, dtype=T.bfloat16):
|
|
n = T.symbolic("n", "int64")
|
|
block_n = 512
|
|
|
|
@T.prim_func
|
|
def main(x: T.Tensor[n, dtype]):
|
|
# Initialize Kernel Context
|
|
with T.Kernel(T.ceildiv(n, block_n), threads=128) as bx:
|
|
# Doesn't yet work with int64-shaped global tensor
|
|
# T.fill(x[bx * block_n : (bx + 1) * block_n], value)
|
|
for i in T.Parallel(block_n):
|
|
x[bx * block_n + i] = value
|
|
|
|
return main
|
|
|
|
|
|
def run_fill_symbolic(n: int):
|
|
import torch
|
|
|
|
x = torch.zeros(n, dtype=torch.bfloat16, device="cuda")
|
|
fill_symbolic(1.0)(x)
|
|
assert x.min() == 1.0 and x.max() == 1.0
|
|
|
|
|
|
def test_fill_symbolic():
|
|
# Targets 8 GiB; falls back in powers of two on smaller GPUs (warns).
|
|
run_fill_symbolic(_select_int64_stress_n())
|
|
|
|
|
|
@tilelang.jit
|
|
def fill_static(n: int, value: float, dtype=T.bfloat16):
|
|
block_n = 512
|
|
|
|
@T.prim_func
|
|
def main(x: T.Tensor[n, dtype]):
|
|
# Initialize Kernel Context
|
|
with T.Kernel(T.ceildiv(n, block_n), threads=128) as bx:
|
|
# Doesn't yet work with int64-shaped global tensor
|
|
# T.fill(x[bx * block_n : (bx + 1) * block_n], value)
|
|
for i in T.Parallel(block_n):
|
|
x[bx * block_n + i] = value
|
|
|
|
return main
|
|
|
|
|
|
def run_fill_static(n: int):
|
|
import torch
|
|
|
|
x = torch.zeros(n, dtype=torch.bfloat16, device="cuda")
|
|
fill_static(n, 1.0)(x)
|
|
assert x.min() == 1.0 and x.max() == 1.0
|
|
|
|
|
|
def test_fill_static():
|
|
# Targets 8 GiB; falls back in powers of two on smaller GPUs (warns).
|
|
run_fill_static(_select_int64_stress_n())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
test_fill_symbolic()
|
|
test_fill_static()
|