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
128 lines
5.1 KiB
Python
128 lines
5.1 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.
|
|
|
|
"""MoE kernels: fused finalize + shared-output residual."""
|
|
|
|
import functools
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
import torch
|
|
|
|
|
|
@functools.cache
|
|
def _load_moe_finalize_fuse_shared_module():
|
|
import tvm_ffi
|
|
|
|
objs_dir = Path(__file__).parent / "objs" / "moe_finalize_fuse_shared"
|
|
so_path = objs_dir / "moe_finalize_fuse_shared.so"
|
|
if not so_path.exists():
|
|
raise RuntimeError(
|
|
f"tokenspeed_kernel moe_finalize_fuse_shared library not found at {so_path}. "
|
|
"Run: pip install -e tokenspeed_kernel/python/"
|
|
)
|
|
return tvm_ffi.load_module(str(so_path))
|
|
|
|
|
|
def moe_finalize_fuse_shared(
|
|
gemm2_out: torch.Tensor,
|
|
expanded_idx_to_permuted_idx: torch.Tensor,
|
|
expert_weights: torch.Tensor,
|
|
shared_output: Optional[torch.Tensor],
|
|
top_k: int,
|
|
enable_pdl: bool = False,
|
|
) -> torch.Tensor:
|
|
"""Fused MoE finalize + optional shared-output residual (bf16, SM>=90).
|
|
|
|
Computes, per token ``t``::
|
|
|
|
out[t] = Σ_k expert_weights[t, k] * gemm2_out[permuted_idx(t, k)]
|
|
+ shared_output[t] # if non-null
|
|
|
|
Replaces the flashinfer built-in finalize kernel + the native
|
|
``routed + shared`` tensor add. The caller is responsible for ensuring
|
|
``shared_output`` is ready on the current stream (e.g. via
|
|
``current_stream.wait_stream(alt_stream)``).
|
|
|
|
Expert-weight scale convention: ``expert_weights`` are read verbatim.
|
|
In the DSv3/K2.5 path they already carry ``routed_scaling_factor``
|
|
because TopK folds it in, so this kernel does not apply any additional
|
|
scale.
|
|
|
|
Args:
|
|
gemm2_out: ``[total_num_padded_tokens, hidden_dim_padded]`` bf16 —
|
|
raw permuted MoE output when the flashinfer runner was called
|
|
with ``do_finalize=False``.
|
|
expanded_idx_to_permuted_idx: ``[num_tokens * top_k]`` int32 —
|
|
permute map (``-1`` means "drop this slot").
|
|
expert_weights: ``[num_tokens, top_k]`` float32 or bfloat16 — per-token
|
|
topk weights, already scaled. DSv3/K2.5 trtllm backends use
|
|
float32 (``_routing_logits_dtype = torch.float32``); other
|
|
backends use bf16. The kernel is templated on this dtype.
|
|
shared_output: ``[num_tokens, hidden_dim]`` bf16 or ``None`` —
|
|
per-token residual to fold into the finalize.
|
|
top_k: top-k count (must be ``<= 64``).
|
|
enable_pdl: honor upstream/downstream PDL if True.
|
|
|
|
Returns:
|
|
``[num_tokens, hidden_dim]`` bf16.
|
|
"""
|
|
assert gemm2_out.dtype == torch.bfloat16
|
|
assert expert_weights.dtype in (torch.float32, torch.bfloat16)
|
|
assert expanded_idx_to_permuted_idx.dtype == torch.int32
|
|
assert gemm2_out.dim() == 2
|
|
assert expert_weights.dim() == 2
|
|
num_tokens, top_k_check = expert_weights.shape
|
|
assert top_k_check == top_k
|
|
hidden_dim = gemm2_out.shape[1]
|
|
# hiddenDim = out.shape[-1]; caller may want a trimmed hidden_dim if
|
|
# padding was applied on the permuted side.
|
|
if shared_output is not None:
|
|
assert shared_output.dtype == torch.bfloat16
|
|
assert shared_output.dim() == 2
|
|
assert shared_output.shape[0] == num_tokens
|
|
hidden_dim = shared_output.shape[1]
|
|
assert hidden_dim <= gemm2_out.shape[1]
|
|
|
|
out = torch.empty(
|
|
num_tokens, hidden_dim, dtype=torch.bfloat16, device=gemm2_out.device
|
|
)
|
|
# Idle DP ranks may finalize 0 tokens; the kernel launch cannot take
|
|
# an empty grid, so return the empty output directly.
|
|
if num_tokens == 0:
|
|
return out
|
|
# The C++ side uses numel()==0 to mean "no shared bias"; pass an empty
|
|
# placeholder when the caller didn't provide one. Avoids optional-tensor
|
|
# plumbing through tvm_ffi.
|
|
if shared_output is None:
|
|
shared_output = gemm2_out.new_empty((0, 0), dtype=torch.bfloat16)
|
|
|
|
mod = _load_moe_finalize_fuse_shared_module()
|
|
mod.moe_finalize_fuse_shared(
|
|
out,
|
|
gemm2_out,
|
|
expanded_idx_to_permuted_idx,
|
|
expert_weights,
|
|
shared_output,
|
|
int(top_k),
|
|
bool(enable_pdl),
|
|
)
|
|
return out
|