Files
wehub-resource-sync 59a0a3844c
PR Test AMD / cancel-on-close (push) Has been skipped
PR Test NVIDIA ARM / scan (push) Has been skipped
PR Test NVIDIA / cancel-on-close (push) Has been skipped
PR Test AMD / scan (push) Has been skipped
PR Test NVIDIA ARM / cancel-on-close (push) Has been skipped
PR Test NVIDIA / scan (push) Has been skipped
Release Docker Images / build (cu129-torch-2.11.0) (push) Has been skipped
Release Docker Images / build (cu130-torch-2.11.0) (push) Has been skipped
Release PyPI / publish (push) Has been skipped
Scheduler Python Test / test (push) Successful in 27m19s
Docs / build (push) Successful in 28m8s
Scheduler C++ Test / test (push) Successful in 28m19s
Scheduler C++ Test / test-flat (push) Successful in 28m18s
Docs / deploy (push) Has been cancelled
PR Test AMD / finish (push) Has been cancelled
PR Test NVIDIA / finish (push) Has been cancelled
PR Test NVIDIA ARM / finish (push) Has been cancelled
PR Test NVIDIA ARM / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test NVIDIA / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:31 +08:00

501 lines
19 KiB
Python

# Copyright (c) 2026 LightSeek Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from __future__ import annotations
import logging
from collections import defaultdict
from dataclasses import dataclass, field
from enum import IntEnum
from typing import TYPE_CHECKING, Any, Callable, Iterable
if TYPE_CHECKING:
import torch
from tokenspeed_kernel.selection import SelectedKernel
from tokenspeed_kernel.platform import CapabilityRequirement, PlatformInfo
from tokenspeed_kernel.signature import FormatSignature
logger = logging.getLogger(__name__)
__all__ = [
"KernelSpec",
"KernelRegistry",
"Priority",
"load_builtin_kernels",
"register_kernel",
"describe_kernel",
]
def _normalize_roles(roles: str | Iterable[str]) -> tuple[str, ...]:
if isinstance(roles, str):
role_names = (roles,)
else:
role_names = tuple(roles)
if not role_names:
raise ValueError("at least one dtype filter role is required")
return role_names
def _validate_weight_preprocessor(
weight_preprocessor: Callable | None,
) -> Callable | None:
if weight_preprocessor is not None and not callable(weight_preprocessor):
raise TypeError("weight preprocessor must be callable")
return weight_preprocessor
def _callable_name(fn: Callable) -> str:
return getattr(fn, "__name__", repr(fn))
# Hard upper bound on priority values; selection scoring clamps to this range.
_PRIORITY_MAX = 20
class Priority(IntEnum):
"""Selection-priority bands for registered kernels.
Priority is the tiebreaker among kernels that already match the request's
capability and trait gates. A kernel that does not satisfy the platform's
capability requirement is filtered out before priority is consulted.
Bands group kernels by their portability/performance contract so that an
out-of-tree plugin author can predict what offset they need to win without
auditing every in-tree registration. Use band members directly, or add a
small intra-band offset for relative preference within the band:
priority=Priority.PERFORMANT # band start (8)
priority=Priority.PERFORMANT + 2 # +2 within the band (10)
Band layout (each occupies a contiguous range of ints in [0, 20).
ints in 1..3 are unused — that range previously held a separate FALLBACK
band that was folded into PORTABLE):
+--------------+--------+----------------------------------------------------+
| Band | Range | When to use |
+==============+========+====================================================+
| REFERENCE | 0 | Correctness reference. Never auto-selected when a |
| | | real implementation is available; useful as a |
| | | numeric ground truth in tests. |
+--------------+--------+----------------------------------------------------+
| PORTABLE | 4..7 | In-tree generic implementation with no arch or |
| | | shape gating beyond the family contract — e.g. |
| | | default Triton, or PyTorch reference patsh used as |
| | | last-resort coverage. |
+--------------+--------+----------------------------------------------------+
| PERFORMANT | 8..11 | In-tree generally optimized kernel, covering a |
| | | broad arch range — e.g. optimizied Triton for |
| | | Hopper+. The default winner on supported vendor. |
+--------------+--------+----------------------------------------------------+
| SPECIALIZED | 12..15 | In-tree highly optimized kernel, narrowly gated on |
| | | arch + shape, e.g., Gluon/CuTe DSL fp8 attention |
| | | for Blackwell with specific head_dim. |
+--------------+--------+----------------------------------------------------+
| PLUGIN | 16..19 | Reserved for out-of-tree plugins to override the |
| | | in-tree default. In-tree kernels should not use |
| | | this band so plugins always have headroom. |
+--------------+--------+----------------------------------------------------+
Notes:
* Keep offsets within the band's width — band+offset returns a plain int,
so crossing into the next band cannot be flagged.
* Pick the lowest band that fits. Inflating a kernel's band to win on one
platform makes it win everywhere it isn't actually specialized.
"""
REFERENCE = 0
PORTABLE = 4
PERFORMANT = 8
SPECIALIZED = 12
PLUGIN = 16
def _band_for(value: int) -> Priority:
"""Return the band that contains ``value`` (the largest band start ≤ value)."""
return max((b for b in Priority if int(b) <= value), key=int)
def _validate_priority(value: int | Priority) -> int:
"""Validate a priority value and return it as a plain ``int``.
Accepts a :class:`Priority` band, a band plus offset (e.g.
``Priority.PERFORMANT + 2``), or a raw int. Raises ``ValueError`` if the
final value falls outside ``[0, 20)``.
"""
ivalue = int(value)
if not 0 <= ivalue < _PRIORITY_MAX:
bands = ", ".join(f"{b.name}={int(b)}" for b in Priority)
raise ValueError(
f"priority must be in [0, {_PRIORITY_MAX}), got {ivalue}. "
f"Use a Priority band ({bands}) optionally with a small +offset."
)
return ivalue
@dataclass(frozen=True)
class KernelSpec:
"""Complete specification of a registered kernel."""
# Identity
name: str # Unique name, e.g., "flashinfer_decode_sm90"
family: str # "attention", "gemm", "moe", etc.
mode: str # "decode", "mm", "experts", etc.
features: frozenset[str] = (
frozenset()
) # Orthogonal features, e.g., {"paged", "mla"}
solution: str = "" # "triton", "flashinfer", "cutlass", "reference", etc.
# Capabilities
capability: CapabilityRequirement = field(default_factory=CapabilityRequirement)
format_signatures: frozenset[FormatSignature] = frozenset()
# Op-specific traits, e.g. {"head_dim": frozenset({64, 128, 256}), "persistent": frozenset({True})}
traits: dict[str, frozenset[Any]] = field(default_factory=dict)
# Selection metadata
# Higher = preferred. See :class:`Priority` for the band layout. The default
# places unannotated kernels in PERFORMANT so they win against PORTABLE but
# lose to SPECIALIZED. Selection scoring clamps out-of-range values.
priority: int = int(Priority.PERFORMANT) + 2
tags: frozenset[str] = (
frozenset()
) # Standard tags: "throughput", "latency", "determinism", "portability"
weight_preprocessor: Callable | None = None
def __post_init__(self) -> None:
object.__setattr__(
self,
"weight_preprocessor",
_validate_weight_preprocessor(self.weight_preprocessor),
)
def supports_format_signature(self, format_signature: FormatSignature) -> bool:
return format_signature in self.format_signatures
def format_signatures_for_storage_dtype(
self,
storage_dtype: torch.dtype,
roles: str | Iterable[str],
) -> tuple[FormatSignature, ...]:
"""Return signatures whose selected role has storage_dtype.
``roles`` is explicit because the meaningful dtype role is an operator
property, not a property of ``FormatSignature`` itself. Multiple roles
are treated as alternatives, which is useful for operators whose dtype
filter role depends on the concrete signature.
"""
role_names = _normalize_roles(roles)
return tuple(
signature
for signature in sorted(self.format_signatures, key=str)
if any(
signature.storage_dtype_for(role) == storage_dtype
for role in role_names
)
)
def format_signature_for_storage_dtype(
self,
storage_dtype: torch.dtype,
roles: str | Iterable[str],
) -> FormatSignature | None:
"""Return the single matching signature, or raise if ambiguous."""
matches = self.format_signatures_for_storage_dtype(storage_dtype, roles)
if len(matches) > 1:
role_list = ", ".join(_normalize_roles(roles)) or "none"
raise ValueError(
f"Kernel {self.name!r} has multiple format signatures for "
f"storage dtype={storage_dtype} on role(s) {role_list}; "
"use a full format signature"
)
return matches[0] if matches else None
def storage_dtypes_for_role(
self,
roles: str | Iterable[str],
) -> frozenset[torch.dtype]:
role_names = _normalize_roles(roles)
return frozenset(
dtype
for dtype in (
signature.storage_dtype_for(role)
for signature in self.format_signatures
for role in role_names
)
if dtype is not None
)
class KernelRegistry:
"""Central registry for all kernel implementations."""
_instance: KernelRegistry | None = None
def __init__(self) -> None:
self._by_operator: dict[tuple[str, str], list[KernelSpec]] = defaultdict(list)
self._by_name: dict[str, KernelSpec] = {}
self._impls: dict[str, Callable] = {} # name -> callable
self._selection_cache: dict[tuple, SelectedKernel] = {}
@classmethod
def get(cls) -> KernelRegistry:
"""Get singleton registry instance."""
if cls._instance is None:
cls._instance = KernelRegistry()
return cls._instance
@classmethod
def reset(cls) -> None:
"""Reset singleton (for testing)."""
cls._instance = None
# ---- Registration ----
def register(self, spec: KernelSpec, impl: Callable) -> None:
"""Register a kernel specification and its implementation."""
if spec.name in self._by_name:
# Allow re-registration (plugin override)
self._unregister(spec.name)
self._by_name[spec.name] = spec
self._impls[spec.name] = impl
key = (spec.family, spec.mode)
self._by_operator[key].append(spec)
self._by_operator[key].sort(key=lambda s: s.priority, reverse=True)
self._invalidate_cache(key)
def _unregister(self, name: str) -> None:
if name not in self._by_name:
return
spec = self._by_name.pop(name)
self._impls.pop(name, None)
key = (spec.family, spec.mode)
self._by_operator[key] = [s for s in self._by_operator[key] if s.name != name]
self._invalidate_cache(key)
# ---- Queries ----
def get_by_name(self, name: str) -> KernelSpec | None:
"""Get a specific kernel spec by name."""
return self._by_name.get(name)
def get_impl(self, name: str) -> Callable | None:
"""Get a kernel's callable implementation by name."""
return self._impls.get(name)
def get_for_operator(
self,
family: str,
mode: str,
*,
features: frozenset[str] | None = None,
platform: PlatformInfo | None = None,
format_signature: FormatSignature | None = None,
tags: set[str] | None = None,
solution: str | None = None,
) -> list[KernelSpec]:
"""Get all kernels for an operator, optionally filtered."""
specs = list(self._by_operator.get((family, mode), []))
if features is not None:
specs = [s for s in specs if features.issubset(s.features)]
if platform:
specs = [s for s in specs if s.capability.satisfied_by(platform)]
if format_signature:
specs = [s for s in specs if s.supports_format_signature(format_signature)]
if tags:
specs = [s for s in specs if tags.issubset(s.tags)]
if solution:
specs = [s for s in specs if s.solution == solution]
return specs
def list_operators(self) -> list[tuple[str, str]]:
"""List all registered (family, mode) pairs."""
return list(self._by_operator.keys())
def list_kernels(
self,
family: str | None = None,
mode: str | None = None,
) -> list[KernelSpec]:
"""List registered kernel specs, optionally filtered."""
if family and mode:
return list(self._by_operator.get((family, mode), []))
specs = list(self._by_name.values())
if family:
specs = [s for s in specs if s.family == family]
if mode:
specs = [s for s in specs if s.mode == mode]
return specs
def list_solutions(self, family: str, mode: str) -> list[str]:
"""List available solutions for an operator."""
return list({s.solution for s in self._by_operator.get((family, mode), [])})
# ---- Cache management ----
def cache_get(self, key: tuple) -> SelectedKernel | None:
"""Look up a cached selection result."""
return self._selection_cache.get(key)
def cache_put(self, key: tuple, selected_kernel: SelectedKernel) -> None:
"""Store a selection result in the cache."""
self._selection_cache[key] = selected_kernel
def _invalidate_cache(self, key: tuple[str, str]) -> None:
self._selection_cache = {
k: v for k, v in self._selection_cache.items() if k[:2] != key
}
def clear_cache(self) -> None:
self._selection_cache.clear()
def register_kernel(
family: str,
mode: str,
*,
name: str | None = None,
features: set[str] | None = None,
solution: str,
capability: CapabilityRequirement | None = None,
signatures: set[FormatSignature] | frozenset[FormatSignature],
traits: dict[str, frozenset[Any]] | None = None,
priority: Priority | int = Priority.PERFORMANT + 2,
tags: set[str] | None = None,
weight_preprocessor: Callable | None = None,
) -> Callable:
"""Decorator to register a kernel function.
``priority`` accepts a :class:`Priority` band (recommended) or a raw ``int``
in ``[0, 20)``. Within a band, add a small offset for relative preference,
e.g. ``Priority.SPECIALIZED + 2``. See :class:`Priority` for the meaning of
each band and how to choose between them.
Example::
from tokenspeed_kernel.signature import format_signatures
@register_kernel(
"attention", "decode",
features={"paged"},
solution="triton",
capability=CapabilityRequirement(
min_arch_version=ArchVersion(10, 0),
required_features=frozenset({"tensor_core:f8"}),
),
signatures=format_signatures(
("q", "k", "v"), "dense", {torch.float16, torch.bfloat16}
),
# Narrowly gated on SM100 + tcgen05 → SPECIALIZED band.
priority=Priority.SPECIALIZED + 1,
tags={"latency", "determinism"},
)
def triton_decode_attention(query, key_cache, value_cache, ...):
...
"""
priority_int = _validate_priority(priority)
normalized_weight_preprocessor = _validate_weight_preprocessor(weight_preprocessor)
def decorator(fn: Callable) -> Callable:
kernel_name = name or f"{solution}_{family}_{mode}"
spec = KernelSpec(
name=kernel_name,
family=family,
mode=mode,
solution=solution,
features=frozenset(features or set()),
format_signatures=frozenset(signatures),
capability=capability or CapabilityRequirement(),
traits=traits or {},
priority=priority_int,
tags=frozenset(tags or set()),
weight_preprocessor=normalized_weight_preprocessor,
)
KernelRegistry.get().register(spec, fn)
return fn
return decorator
def describe_kernel(name: str) -> str:
"""Generate human-readable description of a kernel."""
registry = KernelRegistry.get()
spec = registry.get_by_name(name)
if not spec:
return f"Kernel '{name}' not found"
band = _band_for(spec.priority)
offset = spec.priority - int(band)
band_str = band.name if offset == 0 else f"{band.name}+{offset}"
lines = [
f"Kernel: {spec.name}",
f" Operator: {spec.family}.{spec.mode}",
f" Solution: {spec.solution}",
f" Priority: {spec.priority} ({band_str})",
" Format signatures: "
+ ("; ".join(str(p) for p in spec.format_signatures) or "none"),
f" Platform: {spec.capability}",
f" Tags: {', '.join(spec.tags) or 'none'}",
]
if spec.weight_preprocessor is None:
lines.append(" Weight preprocessor: none")
else:
lines.append(
f" Weight preprocessor: {_callable_name(spec.weight_preprocessor)}"
)
return "\n".join(lines)
def load_builtin_kernels() -> None:
import sys
if not KernelRegistry.get().list_kernels():
# Registry was reset; clear cached ops modules so decorators re-run.
for key in list(sys.modules.keys()):
if key.startswith("tokenspeed_kernel.ops.") or key.startswith(
"tokenspeed_kernel.numerics.reference."
):
del sys.modules[key]
import tokenspeed_kernel.ops.embedding # noqa: F401
import tokenspeed_kernel.ops.gemm # noqa: F401
import tokenspeed_kernel.ops.moe # noqa: F401
import tokenspeed_kernel.ops.quantization # noqa: F401
import tokenspeed_kernel.ops.sampling # noqa: F401
import tokenspeed_kernel.ops.transform # noqa: F401
def error_fn(*args, **kwargs):
"""A placeholder function when kernel is not properly imported or registered."""
raise RuntimeError("Kernel implementation not found")
class ErrorClass:
"""A placeholder class when kernel implementation is not properly imported or registered."""
def __init__(self, *args, **kwargs):
raise RuntimeError("Kernel implementation not found")