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

129 lines
4.8 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.
"""merge_state: vendored from flashinfer's MergeStateKernel + lse_scale knob + PDL.
The CUDA source lives at ``thirdparty/cuda/csrc/merge_state.cu``. This module is
the public Python API — it loads the prebuilt ``.so`` lazily, validates inputs,
allocates outputs, and forwards to the kernel.
"""
from __future__ import annotations
import functools
import math
from pathlib import Path
from typing import Tuple
import torch
# Common scale presets — multiplying LSE by these brings it into log2 domain,
# which is what the kernel uses internally (ex2.approx is the native PTX
# intrinsic on NVIDIA, so we always work in log2 and pre-/post-rescale instead).
LSE_LOG2 = 1.0
LSE_LN = math.log2(math.e) # ≈ 1.4426950408889634
@functools.cache
def _load_merge_state_module():
import tvm_ffi
so_path = Path(__file__).parent / "objs" / "merge_state" / "merge_state.so"
if not so_path.exists():
raise RuntimeError(
f"tokenspeed_kernel merge_state library not found at {so_path}. "
"Run: pip install -e tokenspeed_kernel/python/"
)
return tvm_ffi.load_module(str(so_path))
def merge_state(
v_a: torch.Tensor,
s_a: torch.Tensor,
v_b: torch.Tensor,
s_b: torch.Tensor,
*,
inplace: bool = False,
lse_scale_log2: float = LSE_LN,
enable_pdl: bool = False,
) -> Tuple[torch.Tensor, torch.Tensor]:
"""Merge two attention partials.
Vendored from flashinfer's ``MergeStateKernel`` (see
``thirdparty/cuda/csrc/merge_state.cu``) with two additions: ``lse_scale_log2``
so callers can pass LSE in any base, and PDL hooks so the kernel can overlap
its preamble with upstream / downstream PDL-aware kernels.
The kernel works in log2 space internally (PTX-native ``ex2.approx``).
``lse_scale_log2`` is the multiplier that converts the caller's LSE into
log2 domain — pass ``LSE_LN = log2(e)`` (default) for natural-log LSE
(trtllm-gen / FA cute / cuteDSL MLA convention) or ``LSE_LOG2 = 1.0`` if
the caller's LSE is already in log2 (flashinfer C++ convention). The
output LSE is returned in the same basis as the input.
Parameters
----------
v_a, v_b : ``[seq_len, num_heads, head_dim]``, ``bfloat16`` or ``float16``.
s_a, s_b : ``[seq_len, num_heads]``, must be ``float32``.
inplace : when ``True``, the merged output is written back into ``v_a`` and
``s_a`` (mutated) and the same tensors are returned. When ``False``,
fresh buffers are allocated.
lse_scale_log2 : multiplier mapping caller's LSE basis to log2.
enable_pdl : opt into Programmatic Dependent Launch (Hopper+). Caller must
also enable PDL on the upstream / downstream kernels for the overlap
to materialize.
Returns
-------
v_merged : ``[seq_len, num_heads, head_dim]`` in ``v_a.dtype``.
s_merged : ``[seq_len, num_heads]`` in fp32, same basis as inputs.
"""
assert v_a.is_contiguous() and v_b.is_contiguous()
assert s_a.is_contiguous() and s_b.is_contiguous()
assert v_a.shape == v_b.shape
assert s_a.shape == s_b.shape
assert v_a.shape[:2] == s_a.shape
assert v_a.dtype == v_b.dtype
assert v_a.dtype in (
torch.bfloat16,
torch.float16,
), f"merge_state V must be bf16/fp16, got {v_a.dtype}"
assert (
s_a.dtype == torch.float32 and s_b.dtype == torch.float32
), f"merge_state expects fp32 LSE, got s_a={s_a.dtype} s_b={s_b.dtype}"
if inplace:
v_merged = v_a
s_merged = s_a
else:
v_merged = torch.empty_like(v_a)
s_merged = torch.empty_like(s_a)
_load_merge_state_module().merge_state(
v_a,
s_a,
v_b,
s_b,
v_merged,
s_merged,
float(lse_scale_log2),
bool(enable_pdl),
)
return v_merged, s_merged