项目文件夹

文件
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

165 行
5.5 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.
"""Binary FMHA prefill backend for tokenspeed-mla.
Loads a pre-compiled AOT TVM-FFI .so calls the exported function.
SO files are looked up in:
<package>/objs/cute_dsl_fmha_fp8_e4m3_to_bf16_hd192_{arch}.so
Set TOKENSPEED_MLA_FMHA_BINARY_SO to override with a custom .so path.
Note on LSE layout: the binary kernel writes LSE in (1, h_k, h_r, total_q)
layout (row-major), which differs from the CuteDSL backend's (total_q, h_q).
The caller (mla_prefill) allocates the binary-layout buffer and reshapes the
result to (total_q, h_q) before returning.
"""
from __future__ import annotations
import functools
import logging
import os
import platform
from pathlib import Path
import torch
logger = logging.getLogger(__name__)
# Exported TVM-FFI function names inside the bundled SO, keyed by (is_causal, return_lse).
# Names follow compile_dsl_fmha.py::get_variant_name() with:
# in_dtype=Float8E4M3FN, out_dtype=BFloat16, head_dim=192,
# varlen=True, is_persistent=False, enable_skip_softmax=False, enable_tvm_ffi=True
_FUNC_NAMES: dict[tuple[bool, bool], str] = {
(
True,
True,
): "cute_dsl_fmha_fp8_e4m3_bf16_hd192_causal_nonpersistent_varlen_lse_tvmffi",
(
True,
False,
): "cute_dsl_fmha_fp8_e4m3_bf16_hd192_causal_nonpersistent_varlen_tvmffi",
(
False,
True,
): "cute_dsl_fmha_fp8_e4m3_bf16_hd192_nocausal_nonpersistent_varlen_lse_tvmffi",
(
False,
False,
): "cute_dsl_fmha_fp8_e4m3_bf16_hd192_nocausal_nonpersistent_varlen_tvmffi",
}
def _objs_dir() -> Path:
return Path(__file__).resolve().parent / "objs"
def _resolve_so_path() -> Path:
"""Return the SO path: env override first, then arch-specific default."""
env = os.environ.get("TOKENSPEED_MLA_FMHA_BINARY_SO")
if env:
return Path(env)
props = torch.cuda.get_device_properties(torch.cuda.current_device())
machine = platform.machine().lower()
if machine == "amd64":
machine = "x86_64"
elif machine == "arm64":
machine = "aarch64"
arch = f"sm_{props.major}{props.minor}a_{machine}"
name = f"cute_dsl_fmha_fp8_e4m3_to_bf16_hd192_{arch}.so"
return _objs_dir() / name
@functools.cache
def _load_module(so_path_str: str):
"""Load a TVM-FFI module from the given .so path (result is cached per path)."""
import cutlass.cute as cute
so_path = Path(so_path_str)
if not so_path.exists():
raise RuntimeError(
f"tokenspeed_mla binary FMHA SO not found: {so_path}. "
"Place the compiled .so there or set TOKENSPEED_MLA_FMHA_BINARY_SO."
)
logger.info("Loading binary FMHA module from %s", so_path)
return cute.runtime.load_module(str(so_path), enable_tvm_ffi=True)
def has_binary_prefill() -> bool:
"""Return True if a binary FMHA SO is present and loadable for the current GPU."""
try:
_load_module(str(_resolve_so_path()))
return True
except Exception:
return False
def call_binary_prefill(
q_ct,
k_ct,
v_ct,
o_ct,
lse_ct,
problem_size: tuple,
cum_q_ct,
cum_k_ct,
scale_softmax_log2: float,
scale_softmax: float,
is_causal: bool,
return_lse: bool,
) -> None:
"""Invoke the pre-compiled binary FMHA prefill kernel via TVM-FFI.
Tensor layout requirements (must match the AOT-compiled binary):
Q : cute.Tensor, shape (1, total_q, h_k, h_r, d_qk), row-major
K : cute.Tensor, shape (1, total_kv, h_k, 1, d_qk), row-major
V : cute.Tensor, shape (1, total_kv, h_k, 1, d_v), row-major
O : cute.Tensor, shape (1, total_q, h_k, h_r, d_v), row-major
lse: cute.Tensor, shape (1, h_k, h_r, total_q), row-major — or None
cum_q_ct / cum_k_ct: cute.Tensor of int32 cumulative sequence lengths
"""
import tvm_ffi
module = _load_module(str(_resolve_so_path()))
func = getattr(module, _FUNC_NAMES[(is_causal, return_lse)])
window_size_right = 0 if is_causal else None
with tvm_ffi.use_torch_stream():
func(
q_ct,
k_ct,
v_ct,
o_ct,
problem_size,
cum_q_ct,
cum_k_ct,
lse_ct,
scale_softmax_log2,
scale_softmax,
1.0, # scale_output
None, # skip_softmax_threshold_log2 (disabled)
None, # window_size_left
window_size_right,
None, # skip_softmax_count
None, # total_softmax_count
)