evox.triton_kernels.op_register 源代码

from typing import Sequence

import torch

from ..utils.op_register import register_vmap_op
from .backend import has_triton
from .backend import triton_device_types as _get_triton_device_types


[文档] def register_triton_op( *, fake_fn, triton_fn, vmap_fn=None, fake_vmap_fn=None, vmap_wrap_inputs=None, vmap_out_dims=0, max_vmap_level=None, name=None, mutates_args=(), device_types=None, triton_device_types: str | Sequence[str] | None = None, schema=None, ): """Register an operator with a PyTorch fallback and an optional Triton CUDA kernel. This decorator wraps :func:`evox.utils.register_vmap_op`, registering the decorated function (``fallback_fn``) as the default implementation with fake / vmap support, and additionally registering ``triton_fn`` as a CUDA-specific kernel via :func:`torch.library.register_kernel`. PyTorch's dispatcher automatically selects the correct backend at call time: - **CUDA tensors** → Triton kernel (``triton_fn``) - **CPU / MPS / other tensors** → PyTorch fallback (the decorated function) If Triton is not installed, only the PyTorch fallback is registered and the operation works on all devices without Triton. :param fake_fn: The fake (abstract evaluation) function for the op. Required. :param triton_fn: The Triton CUDA kernel launcher. Called with the same arguments as the decorated function when dispatched on CUDA. Must return outputs matching the fake function's shapes/dtypes. :param vmap_fn: Optional vmap implementation. See :func:`register_vmap_op`. :param fake_vmap_fn: Optional fake vmap function. See :func:`register_vmap_op`. :param vmap_wrap_inputs: Optional input wrapper for vmap. See :func:`register_vmap_op`. :param vmap_out_dims: Output vmap dimensions. See :func:`register_vmap_op`. :param max_vmap_level: Maximum vmap nesting level. See :func:`register_vmap_op`. :param name: Custom op name. Default ``"evox::_custom_op_" + fn.__name__``. :param mutates_args: Args mutated by the op. See :func:`register_vmap_op`. :param device_types: Supported device types for the op registration. :param triton_device_types: Device type(s) for which the Triton kernel is registered via :func:`torch.library.register_kernel`. When ``None`` (default), uses the globally registered device types (:func:`~evox.triton_kernels.backend.triton_device_types`, default ``{"cuda"}``). Accepts a single string (e.g. ``"npu"``) or a sequence of strings (e.g. ``["cuda", "npu"]``) to extend or restrict coverage. :param schema: Op schema string. ## Example ```python @triton.jit def _add_kernel(x_ptr, y_ptr, out_ptr, n_elements, BLOCK_SIZE: tl.constexpr): pid = tl.program_id(0) offsets = pid * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE) mask = offsets < n_elements x = tl.load(x_ptr + offsets, mask=mask) y = tl.load(y_ptr + offsets, mask=mask) tl.store(out_ptr + offsets, x + y, mask=mask) def _triton_add(x, y): out = torch.empty_like(x) n = x.numel() grid = lambda meta: (triton.cdiv(n, meta["BLOCK_SIZE"]),) _add_kernel[grid](x, y, out, n, BLOCK_SIZE=1024) return out def _add_fake(x, y): return torch.empty_like(x) @register_triton_op(fake_fn=_add_fake, triton_fn=_triton_add) def fused_add(x: torch.Tensor, y: torch.Tensor) -> torch.Tensor: return x + y ``` """ def decorator(fallback_fn): # Step 1: Register the PyTorch fallback as the default op + fake + vmap registered = register_vmap_op( fallback_fn, fake_fn=fake_fn, vmap_fn=vmap_fn, fake_vmap_fn=fake_vmap_fn, vmap_wrap_inputs=vmap_wrap_inputs, vmap_out_dims=vmap_out_dims, max_vmap_level=max_vmap_level, name=name, mutates_args=mutates_args, device_types=device_types, schema=schema, ) # Step 2: Determine the op name (must match register_vmap_op's naming logic) op_name = name if name is not None else "evox::_custom_op_" + fallback_fn.__name__ # Step 3: Register the Triton kernel(s) for the supported device types, # but only if Triton is available. By default this covers the globally # registered device types (e.g. "cuda"), but callers may pass an explicit # ``triton_device_types`` to extend or restrict coverage (e.g. "npu"). if has_triton(): if triton_device_types is None: resolved_types = _get_triton_device_types() elif isinstance(triton_device_types, str): resolved_types = [triton_device_types] else: resolved_types = triton_device_types for dt in resolved_types: try: torch.library.register_kernel(op_name, dt, triton_fn) except RuntimeError: # The device type is not recognized by this PyTorch build # (e.g. "npu" without torch-npu installed). Silently skip so # the module imports cleanly on all platforms; the kernel # will register when the platform-specific backend is # available, with the PyTorch fallback used in the meantime. continue return registered return decorator