fix: lazy load TileLang MHC kernels (#30580)
This commit is contained in:
@@ -9,27 +9,8 @@ import triton.language as tl
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# tilelang isn't shipped on every platform (e.g. Ascend NPU images) and the
|
# This module is imported during model-registry discovery. Keep it free of
|
||||||
# only tilelang artifacts in this file are pass_configs that downstream
|
# TileLang imports so discovery does not load TileLang's native CUDA stubs.
|
||||||
# tilelang.jit decorators would consume — the kernels actually defined here
|
|
||||||
# are Triton. Keep the import optional so this module loads on NPU.
|
|
||||||
try:
|
|
||||||
import tilelang
|
|
||||||
|
|
||||||
tilelang.set_log_level("WARNING")
|
|
||||||
|
|
||||||
pass_configs = {
|
|
||||||
tilelang.PassConfigKey.TL_DISABLE_WARP_SPECIALIZED: True,
|
|
||||||
tilelang.PassConfigKey.TL_DISABLE_TMA_LOWER: True,
|
|
||||||
}
|
|
||||||
except ImportError:
|
|
||||||
logger.info(
|
|
||||||
"tilelang not installed; deepseek_v4_rope pass_configs unset. "
|
|
||||||
"Triton kernels in this module still run; only downstream tilelang.jit "
|
|
||||||
"consumers of pass_configs will need to handle the None."
|
|
||||||
)
|
|
||||||
tilelang = None
|
|
||||||
pass_configs = None
|
|
||||||
|
|
||||||
FP8 = "float8_e4m3"
|
FP8 = "float8_e4m3"
|
||||||
BF16 = "bfloat16"
|
BF16 = "bfloat16"
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import functools
|
import functools
|
||||||
|
import importlib
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
|
import threading
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
@@ -12,52 +14,102 @@ from sglang.srt.layers.utils.common import strict_contiguous
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Tilelang isn't packaged on every platform (notably Ascend NPU images) but
|
# This module is imported during model-registry discovery. Do not import the real
|
||||||
# this module is imported transitively from deepseek_v4.py — module-load
|
# TileLang package here: it loads native CUDA stubs. The proxy below lets
|
||||||
# must succeed even when tilelang is missing. The kernels themselves still
|
# module-level @tilelang.jit declarations parse, then imports and applies real
|
||||||
# require tilelang at runtime; we replace the package with a stub that lets
|
# TileLang only when a TileLang MHC kernel is actually called.
|
||||||
# `@tilelang.jit` decorations and `tilelang.PassConfigKey.*` references parse
|
_real_tilelang = None
|
||||||
# without ImportError, and any actual call into the kernels raises a clear
|
_real_T = None
|
||||||
# message at execution time instead of crashing on import.
|
_tilelang_load_lock = threading.Lock()
|
||||||
try:
|
|
||||||
import tilelang
|
|
||||||
import tilelang.language as T
|
|
||||||
|
|
||||||
tilelang.set_log_level("WARNING")
|
|
||||||
|
|
||||||
pass_configs = {
|
class _LazyTilelangAttr:
|
||||||
tilelang.PassConfigKey.TL_DISABLE_WARP_SPECIALIZED: True,
|
def __init__(self, path: Tuple[str, ...] = ()):
|
||||||
tilelang.PassConfigKey.TL_DISABLE_TMA_LOWER: True,
|
self.path = path
|
||||||
}
|
|
||||||
except ImportError:
|
|
||||||
|
|
||||||
class _TilelangMissing:
|
def __getattr__(self, name):
|
||||||
"""Stub so module-level @tilelang.jit and PassConfigKey accesses parse."""
|
return _LazyTilelangAttr((*self.path, name))
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __call__(self, *_args, **_kwargs):
|
||||||
if name == "jit":
|
return _LazyTilelangAttr(self.path)
|
||||||
|
|
||||||
def _jit(*_args, **_kwargs):
|
|
||||||
def _wrap(fn):
|
|
||||||
def _raise(*a, **k):
|
|
||||||
raise RuntimeError(
|
|
||||||
"tilelang is not installed; this kernel cannot run "
|
|
||||||
"on the current platform"
|
|
||||||
)
|
|
||||||
|
|
||||||
return _raise
|
def _resolve_lazy_tilelang_value(value):
|
||||||
|
if isinstance(value, _LazyTilelangAttr):
|
||||||
|
obj = _load_tilelang()
|
||||||
|
for name in value.path:
|
||||||
|
obj = getattr(obj, name)
|
||||||
|
return obj
|
||||||
|
if isinstance(value, dict):
|
||||||
|
return {
|
||||||
|
_resolve_lazy_tilelang_value(k): _resolve_lazy_tilelang_value(v)
|
||||||
|
for k, v in value.items()
|
||||||
|
}
|
||||||
|
# Keep list/tuple support so future TileLang jit kwargs such as out_idx=[...]
|
||||||
|
# can use lazy TileLang enum values without changing the proxy.
|
||||||
|
if isinstance(value, list):
|
||||||
|
return [_resolve_lazy_tilelang_value(v) for v in value]
|
||||||
|
if isinstance(value, tuple):
|
||||||
|
return tuple(_resolve_lazy_tilelang_value(v) for v in value)
|
||||||
|
return value
|
||||||
|
|
||||||
return _wrap
|
|
||||||
|
|
||||||
return _jit
|
def _load_tilelang():
|
||||||
return _TilelangMissing()
|
global _real_tilelang, _real_T, tilelang, T
|
||||||
|
if _real_tilelang is None:
|
||||||
|
with _tilelang_load_lock:
|
||||||
|
if _real_tilelang is None:
|
||||||
|
try:
|
||||||
|
new_tilelang = importlib.import_module("tilelang")
|
||||||
|
new_T = importlib.import_module("tilelang.language")
|
||||||
|
except ImportError as exc:
|
||||||
|
raise RuntimeError(
|
||||||
|
"tilelang is not installed; this kernel cannot run on the current platform"
|
||||||
|
) from exc
|
||||||
|
new_tilelang.set_log_level("WARNING")
|
||||||
|
tilelang = new_tilelang
|
||||||
|
T = new_T
|
||||||
|
_real_T = new_T
|
||||||
|
_real_tilelang = new_tilelang
|
||||||
|
return _real_tilelang
|
||||||
|
|
||||||
def __call__(self, *_args, **_kwargs):
|
|
||||||
return _TilelangMissing()
|
|
||||||
|
|
||||||
tilelang = _TilelangMissing()
|
class _LazyTilelang:
|
||||||
T = _TilelangMissing()
|
PassConfigKey = _LazyTilelangAttr(("PassConfigKey",))
|
||||||
pass_configs = None
|
layout = _LazyTilelangAttr(("layout",))
|
||||||
|
|
||||||
|
def jit(self, func=None, **jit_kwargs):
|
||||||
|
def decorate(fn):
|
||||||
|
compiled = None
|
||||||
|
compile_lock = threading.Lock()
|
||||||
|
|
||||||
|
@functools.wraps(fn)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
nonlocal compiled
|
||||||
|
if compiled is None:
|
||||||
|
with compile_lock:
|
||||||
|
if compiled is None:
|
||||||
|
real_tilelang = _load_tilelang()
|
||||||
|
real_kwargs = _resolve_lazy_tilelang_value(jit_kwargs)
|
||||||
|
compiled = real_tilelang.jit(**real_kwargs)(fn)
|
||||||
|
return compiled(*args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
if callable(func):
|
||||||
|
return decorate(func)
|
||||||
|
return decorate
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return _LazyTilelangAttr((name,))
|
||||||
|
|
||||||
|
|
||||||
|
tilelang = _LazyTilelang()
|
||||||
|
T = _LazyTilelangAttr()
|
||||||
|
pass_configs = {
|
||||||
|
tilelang.PassConfigKey.TL_DISABLE_WARP_SPECIALIZED: True,
|
||||||
|
tilelang.PassConfigKey.TL_DISABLE_TMA_LOWER: True,
|
||||||
|
}
|
||||||
|
|
||||||
FP8 = "float8_e4m3"
|
FP8 = "float8_e4m3"
|
||||||
BF16 = "bfloat16"
|
BF16 = "bfloat16"
|
||||||
@@ -298,7 +350,7 @@ def mhc_pre_gemm_sqrsum_tilelang(
|
|||||||
hc_hidden_size: int,
|
hc_hidden_size: int,
|
||||||
token_block: int = 32,
|
token_block: int = 32,
|
||||||
hidden_block: int = 256,
|
hidden_block: int = 256,
|
||||||
) -> tilelang.JITKernel:
|
):
|
||||||
assert hc_mult3 <= 32
|
assert hc_mult3 <= 32
|
||||||
num_tokens = T.dynamic("num_tokens")
|
num_tokens = T.dynamic("num_tokens")
|
||||||
assert hc_hidden_size % hidden_block == 0
|
assert hc_hidden_size % hidden_block == 0
|
||||||
@@ -363,7 +415,8 @@ def mhc_pre_gemm_sqrsum_splitk_kernel(
|
|||||||
token_block: int = 32,
|
token_block: int = 32,
|
||||||
hidden_block: int = 256,
|
hidden_block: int = 256,
|
||||||
threads: int = 128,
|
threads: int = 128,
|
||||||
) -> Tuple[tilelang.JITKernel, tilelang.JITKernel]:
|
):
|
||||||
|
_load_tilelang()
|
||||||
assert hc_mult3 <= 32
|
assert hc_mult3 <= 32
|
||||||
assert hc_hidden_size % hidden_block == 0
|
assert hc_hidden_size % hidden_block == 0
|
||||||
assert hc_hidden_size % split_k == 0
|
assert hc_hidden_size % split_k == 0
|
||||||
@@ -925,7 +978,7 @@ def mhc_pre(
|
|||||||
)
|
)
|
||||||
def mhc_post_tilelang(
|
def mhc_post_tilelang(
|
||||||
a, b, c, d, x, hc: int, hidden: int, n_thr: int = 128, h_blk: int = 1024
|
a, b, c, d, x, hc: int, hidden: int, n_thr: int = 128, h_blk: int = 1024
|
||||||
) -> tilelang.JITKernel:
|
):
|
||||||
n = T.dynamic("num_tokens")
|
n = T.dynamic("num_tokens")
|
||||||
h = hidden
|
h = hidden
|
||||||
|
|
||||||
@@ -1018,7 +1071,7 @@ def mhc_fused_post_pre_fma_tilelang(
|
|||||||
n_thr: int = 256,
|
n_thr: int = 256,
|
||||||
tile_mix_outputs: int = 1,
|
tile_mix_outputs: int = 1,
|
||||||
split_k: int = 1,
|
split_k: int = 1,
|
||||||
) -> tilelang.JITKernel:
|
):
|
||||||
num_tokens = T.dynamic("num_tokens")
|
num_tokens = T.dynamic("num_tokens")
|
||||||
split_k = T.dynamic("split_k")
|
split_k = T.dynamic("split_k")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user