666 lines
18 KiB
Python
666 lines
18 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
import inspect
|
|
|
|
import torch
|
|
|
|
# this import will also register the custom ops
|
|
# import vllm.model_executor.kernels.mhc # noqa: F401
|
|
import vllm.model_executor.kernels.mhc as mhc_kernels
|
|
from vllm._aiter_ops import is_aiter_found_and_supported
|
|
from vllm.model_executor.custom_op import CustomOp
|
|
from vllm.platforms import current_platform
|
|
from vllm.utils.import_utils import has_tilelang
|
|
|
|
|
|
def _has_tilelang_mhc() -> bool:
|
|
if not has_tilelang():
|
|
return False
|
|
if current_platform.is_cuda():
|
|
return True
|
|
if current_platform.is_rocm():
|
|
from vllm.platforms.rocm import on_gfx942
|
|
|
|
# TileLang MHC currently produces incorrect results on gfx942. Keep
|
|
# gfx942 on the existing torch/triton fallbacks until that path is fixed.
|
|
return not on_gfx942()
|
|
return False
|
|
|
|
|
|
HAS_TILELANG_MHC = _has_tilelang_mhc()
|
|
HAS_AITER_MHC = is_aiter_found_and_supported()
|
|
|
|
|
|
def _has_aiter_mhc_fused() -> bool:
|
|
if not HAS_AITER_MHC:
|
|
return False
|
|
try:
|
|
from aiter.ops.mhc import mhc_fused_post_pre
|
|
except Exception:
|
|
return False
|
|
return callable(mhc_fused_post_pre)
|
|
|
|
|
|
def _aiter_mhc_op_accepts_norm(op_name: str) -> bool:
|
|
if not HAS_AITER_MHC:
|
|
return False
|
|
try:
|
|
from aiter.ops import mhc as aiter_mhc
|
|
|
|
for candidate_name in (op_name, f"{op_name}_fake"):
|
|
op = getattr(aiter_mhc, candidate_name, None)
|
|
if op is None:
|
|
continue
|
|
parameters = inspect.signature(op).parameters
|
|
if "norm_weight" in parameters and "norm_eps" in parameters:
|
|
return True
|
|
except (AttributeError, ImportError, TypeError, ValueError):
|
|
pass
|
|
return False
|
|
|
|
|
|
HAS_AITER_MHC_FUSED = _has_aiter_mhc_fused()
|
|
HAS_AITER_MHC_PRE_NORM = _aiter_mhc_op_accepts_norm("mhc_pre")
|
|
HAS_AITER_MHC_FUSED_NORM = _aiter_mhc_op_accepts_norm("mhc_fused_post_pre")
|
|
|
|
|
|
def _aiter_mhc_supported(
|
|
residual: torch.Tensor,
|
|
norm_weight: torch.Tensor | None,
|
|
*,
|
|
supports_norm: bool,
|
|
) -> bool:
|
|
hidden_size = residual.shape[-1]
|
|
hc_mult = residual.shape[-2]
|
|
return (
|
|
HAS_AITER_MHC
|
|
and hidden_size % 256 == 0
|
|
and hc_mult == 4
|
|
and (norm_weight is None or supports_norm)
|
|
)
|
|
|
|
|
|
def _apply_mhc_norm(
|
|
layer_input: torch.Tensor,
|
|
norm_weight: torch.Tensor | None,
|
|
norm_eps: float,
|
|
) -> torch.Tensor:
|
|
if norm_weight is None:
|
|
return layer_input
|
|
from vllm import ir
|
|
|
|
return ir.ops.rms_norm(layer_input, norm_weight, norm_eps)
|
|
|
|
|
|
# --8<-- [start:mhc_pre]
|
|
@CustomOp.register("mhc_pre")
|
|
class MHCPreOp(CustomOp):
|
|
"""MHC pre block.
|
|
|
|
Computes mix logits from RMS-normalized HC residual streams, then
|
|
returns post_mix, comb_mix, and
|
|
layer_input = sum_i pre_mix_i * residual_i.
|
|
"""
|
|
|
|
# --8<-- [end:mhc_pre]
|
|
@classmethod
|
|
def enabled(cls) -> bool:
|
|
return True
|
|
|
|
def forward_cuda(
|
|
self,
|
|
residual: torch.Tensor,
|
|
fn: torch.Tensor,
|
|
hc_scale: torch.Tensor,
|
|
hc_base: torch.Tensor,
|
|
rms_eps: float,
|
|
hc_pre_eps: float,
|
|
hc_sinkhorn_eps: float,
|
|
hc_post_mult_value: float,
|
|
sinkhorn_repeat: int,
|
|
n_splits: int = 1,
|
|
norm_weight: torch.Tensor | None = None,
|
|
norm_eps: float = 0.0,
|
|
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
return torch.ops.vllm.mhc_pre_tilelang(
|
|
residual,
|
|
fn,
|
|
hc_scale,
|
|
hc_base,
|
|
rms_eps,
|
|
hc_pre_eps,
|
|
hc_sinkhorn_eps,
|
|
hc_post_mult_value,
|
|
sinkhorn_repeat,
|
|
n_splits,
|
|
norm_weight,
|
|
norm_eps,
|
|
)
|
|
|
|
def forward_hip(
|
|
self,
|
|
residual: torch.Tensor,
|
|
fn: torch.Tensor,
|
|
hc_scale: torch.Tensor,
|
|
hc_base: torch.Tensor,
|
|
rms_eps: float,
|
|
hc_pre_eps: float,
|
|
hc_sinkhorn_eps: float,
|
|
hc_post_mult_value: float,
|
|
sinkhorn_repeat: int,
|
|
n_splits: int = 1,
|
|
norm_weight: torch.Tensor | None = None,
|
|
norm_eps: float = 0.0,
|
|
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
if _aiter_mhc_supported(
|
|
residual,
|
|
norm_weight,
|
|
supports_norm=HAS_AITER_MHC_PRE_NORM,
|
|
):
|
|
return torch.ops.vllm.mhc_pre_aiter(
|
|
residual,
|
|
fn,
|
|
hc_scale,
|
|
hc_base,
|
|
rms_eps,
|
|
hc_pre_eps,
|
|
hc_sinkhorn_eps,
|
|
hc_post_mult_value,
|
|
sinkhorn_repeat,
|
|
n_splits,
|
|
norm_weight,
|
|
norm_eps,
|
|
)
|
|
elif HAS_TILELANG_MHC:
|
|
return torch.ops.vllm.mhc_pre_tilelang(
|
|
residual,
|
|
fn,
|
|
hc_scale,
|
|
hc_base,
|
|
rms_eps,
|
|
hc_pre_eps,
|
|
hc_sinkhorn_eps,
|
|
hc_post_mult_value,
|
|
sinkhorn_repeat,
|
|
n_splits,
|
|
norm_weight,
|
|
norm_eps,
|
|
)
|
|
else:
|
|
post_mix, comb_mix, layer_input = self.forward_native(
|
|
residual,
|
|
fn,
|
|
hc_scale,
|
|
hc_base,
|
|
rms_eps,
|
|
hc_pre_eps,
|
|
hc_sinkhorn_eps,
|
|
hc_post_mult_value,
|
|
sinkhorn_repeat,
|
|
n_splits,
|
|
norm_weight,
|
|
norm_eps,
|
|
)
|
|
return (
|
|
post_mix,
|
|
comb_mix,
|
|
_apply_mhc_norm(layer_input, norm_weight, norm_eps),
|
|
)
|
|
|
|
def forward_native(
|
|
self,
|
|
residual: torch.Tensor,
|
|
fn: torch.Tensor,
|
|
hc_scale: torch.Tensor,
|
|
hc_base: torch.Tensor,
|
|
rms_eps: float,
|
|
hc_pre_eps: float,
|
|
hc_sinkhorn_eps: float,
|
|
hc_post_mult_value: float,
|
|
sinkhorn_repeat: int,
|
|
n_splits: int = 1,
|
|
norm_weight: torch.Tensor | None = None,
|
|
norm_eps: float = 0.0,
|
|
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
return mhc_kernels.mhc_pre_torch(
|
|
residual,
|
|
fn,
|
|
hc_scale,
|
|
hc_base,
|
|
rms_eps,
|
|
hc_pre_eps,
|
|
hc_sinkhorn_eps,
|
|
hc_post_mult_value,
|
|
sinkhorn_repeat,
|
|
)
|
|
|
|
def forward_xpu(
|
|
self,
|
|
residual: torch.Tensor,
|
|
fn: torch.Tensor,
|
|
hc_scale: torch.Tensor,
|
|
hc_base: torch.Tensor,
|
|
rms_eps: float,
|
|
hc_pre_eps: float,
|
|
hc_sinkhorn_eps: float,
|
|
hc_post_mult_value: float,
|
|
sinkhorn_repeat: int,
|
|
n_splits: int = 1,
|
|
norm_weight: torch.Tensor | None = None,
|
|
norm_eps: float = 0.0,
|
|
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
return torch.ops._xpu_C.mhc_pre(
|
|
residual,
|
|
fn,
|
|
hc_scale,
|
|
hc_base,
|
|
rms_eps,
|
|
hc_pre_eps,
|
|
hc_sinkhorn_eps,
|
|
hc_post_mult_value,
|
|
sinkhorn_repeat,
|
|
)
|
|
|
|
|
|
# --8<-- [start:mhc_post]
|
|
@CustomOp.register("mhc_post")
|
|
class MHCPostOp(CustomOp):
|
|
"""MHC post block.
|
|
|
|
Combines the layer output with the HC residual streams:
|
|
out_j = post_layer_mix_j * x + sum_i comb_res_mix_ij * residual_i.
|
|
"""
|
|
|
|
# --8<-- [end:mhc_post]
|
|
|
|
@classmethod
|
|
def enabled(cls) -> bool:
|
|
return True
|
|
|
|
def forward_cuda(
|
|
self,
|
|
x: torch.Tensor,
|
|
residual: torch.Tensor,
|
|
post_layer_mix: torch.Tensor,
|
|
comb_res_mix: torch.Tensor,
|
|
) -> torch.Tensor:
|
|
return torch.ops.vllm.mhc_post_tilelang(
|
|
x, residual, post_layer_mix, comb_res_mix
|
|
)
|
|
|
|
def forward_hip(
|
|
self,
|
|
x: torch.Tensor,
|
|
residual: torch.Tensor,
|
|
post_layer_mix: torch.Tensor,
|
|
comb_res_mix: torch.Tensor,
|
|
) -> torch.Tensor:
|
|
if _aiter_mhc_supported(residual, None, supports_norm=True):
|
|
return torch.ops.vllm.mhc_post_aiter(
|
|
x,
|
|
residual,
|
|
post_layer_mix,
|
|
comb_res_mix,
|
|
)
|
|
if HAS_TILELANG_MHC:
|
|
return torch.ops.vllm.mhc_post_tilelang(
|
|
x, residual, post_layer_mix, comb_res_mix
|
|
)
|
|
else:
|
|
return self.forward_native(x, residual, post_layer_mix, comb_res_mix)
|
|
|
|
def forward_native(
|
|
self,
|
|
x: torch.Tensor,
|
|
residual: torch.Tensor,
|
|
post_layer_mix: torch.Tensor,
|
|
comb_res_mix: torch.Tensor,
|
|
) -> torch.Tensor:
|
|
return mhc_kernels.mhc_post_torch(
|
|
x,
|
|
residual,
|
|
post_layer_mix,
|
|
comb_res_mix,
|
|
)
|
|
|
|
def forward_xpu(
|
|
self,
|
|
x: torch.Tensor,
|
|
residual: torch.Tensor,
|
|
post_layer_mix: torch.Tensor,
|
|
comb_res_mix: torch.Tensor,
|
|
) -> torch.Tensor:
|
|
return torch.ops._xpu_C.mhc_post(
|
|
x,
|
|
residual,
|
|
post_layer_mix,
|
|
comb_res_mix,
|
|
)
|
|
|
|
|
|
# --8<-- [start:hc_head]
|
|
@CustomOp.register("hc_head")
|
|
class HCHeadOp(CustomOp):
|
|
"""HC head reduction for DeepSeek V4.
|
|
|
|
Computes gates from the RMS-normalized flattened HC residual and
|
|
returns out = sum_i gate_i * residual_i, collapsing hc_mult streams
|
|
to one.
|
|
"""
|
|
|
|
# --8<-- [end:hc_head]
|
|
@classmethod
|
|
def enabled(cls) -> bool:
|
|
return True
|
|
|
|
def forward_cuda(
|
|
self,
|
|
hidden_states: torch.Tensor,
|
|
hc_fn: torch.Tensor,
|
|
hc_scale: torch.Tensor,
|
|
hc_base: torch.Tensor,
|
|
rms_norm_eps: float,
|
|
hc_eps: float,
|
|
) -> torch.Tensor:
|
|
hc_mult, hidden_size = hidden_states.shape[-2:]
|
|
outer_shape = hidden_states.shape[:-2]
|
|
hs_flat = hidden_states.view(-1, hc_mult, hidden_size)
|
|
out = torch.ops.vllm.hc_head_fused_kernel_tilelang(
|
|
hs_flat,
|
|
hc_fn,
|
|
hc_scale,
|
|
hc_base,
|
|
rms_norm_eps,
|
|
hc_eps,
|
|
)
|
|
return out.view(*outer_shape, hidden_size)
|
|
|
|
def forward_hip(
|
|
self,
|
|
hidden_states: torch.Tensor,
|
|
hc_fn: torch.Tensor,
|
|
hc_scale: torch.Tensor,
|
|
hc_base: torch.Tensor,
|
|
rms_norm_eps: float,
|
|
hc_eps: float,
|
|
) -> torch.Tensor:
|
|
hc_mult, hidden_size = hidden_states.shape[-2:]
|
|
outer_shape = hidden_states.shape[:-2]
|
|
hs_flat = hidden_states.view(-1, hc_mult, hidden_size)
|
|
|
|
if HAS_TILELANG_MHC:
|
|
out = torch.ops.vllm.hc_head_fused_kernel_tilelang(
|
|
hs_flat,
|
|
hc_fn,
|
|
hc_scale,
|
|
hc_base,
|
|
rms_norm_eps,
|
|
hc_eps,
|
|
)
|
|
else:
|
|
num_tokens = hs_flat.shape[0]
|
|
out = torch.empty(
|
|
num_tokens,
|
|
hidden_size,
|
|
dtype=torch.bfloat16,
|
|
device=hidden_states.device,
|
|
)
|
|
torch.ops.vllm.hc_head_triton(
|
|
hs_flat,
|
|
hc_fn,
|
|
hc_scale,
|
|
hc_base,
|
|
out,
|
|
hidden_size,
|
|
rms_norm_eps,
|
|
hc_eps,
|
|
hc_mult,
|
|
)
|
|
|
|
return out.view(*outer_shape, hidden_size)
|
|
|
|
def forward_native(self, *args, **kwargs):
|
|
raise NotImplementedError("Native implementation of hc_head is not available")
|
|
|
|
def forward_xpu(
|
|
self,
|
|
hidden_states: torch.Tensor,
|
|
hc_fn: torch.Tensor,
|
|
hc_scale: torch.Tensor,
|
|
hc_base: torch.Tensor,
|
|
rms_norm_eps: float,
|
|
hc_eps: float,
|
|
) -> torch.Tensor:
|
|
hc_mult, hidden_size = hidden_states.shape[-2:]
|
|
outer_shape = hidden_states.shape[:-2]
|
|
hs_flat = hidden_states.view(-1, hc_mult, hidden_size)
|
|
num_tokens = hs_flat.shape[0]
|
|
|
|
out = torch.empty(
|
|
num_tokens, hidden_size, dtype=torch.bfloat16, device=hidden_states.device
|
|
)
|
|
torch.ops._xpu_C.hc_head_fused(
|
|
hs_flat, hc_fn, hc_scale, hc_base, out, rms_norm_eps, hc_eps
|
|
)
|
|
return out.view(*outer_shape, hidden_size)
|
|
|
|
|
|
# --8<-- [start:mhc_fused_post_pre]
|
|
@CustomOp.register("mhc_fused_post_pre")
|
|
class MHCFusedPostPreOp(CustomOp):
|
|
"""Fused MHC post block followed by the next MHC pre block.
|
|
|
|
Equivalent to applying MHCPostOp and then MHCPreOp to the updated
|
|
residual streams, returning residual_cur, post_mix_cur, comb_mix_cur,
|
|
and layer_input_cur.
|
|
"""
|
|
|
|
# --8<-- [end:mhc_fused_post_pre]
|
|
@classmethod
|
|
def enabled(cls) -> bool:
|
|
return True
|
|
|
|
def forward_cuda(
|
|
self,
|
|
x: torch.Tensor,
|
|
residual: torch.Tensor,
|
|
post_layer_mix: torch.Tensor,
|
|
comb_res_mix: torch.Tensor,
|
|
fn: torch.Tensor,
|
|
hc_scale: torch.Tensor,
|
|
hc_base: torch.Tensor,
|
|
rms_eps: float,
|
|
hc_pre_eps: float,
|
|
hc_sinkhorn_eps: float,
|
|
hc_post_mult_value: float,
|
|
sinkhorn_repeat: int,
|
|
n_splits: int = 1,
|
|
tile_n: int = 1,
|
|
norm_weight: torch.Tensor | None = None,
|
|
norm_eps: float = 0.0,
|
|
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
return torch.ops.vllm.mhc_fused_post_pre_tilelang(
|
|
x,
|
|
residual,
|
|
post_layer_mix,
|
|
comb_res_mix,
|
|
fn,
|
|
hc_scale,
|
|
hc_base,
|
|
rms_eps,
|
|
hc_pre_eps,
|
|
hc_sinkhorn_eps,
|
|
hc_post_mult_value,
|
|
sinkhorn_repeat,
|
|
n_splits,
|
|
tile_n,
|
|
norm_weight,
|
|
norm_eps,
|
|
)
|
|
|
|
def forward_hip(
|
|
self,
|
|
x: torch.Tensor,
|
|
residual: torch.Tensor,
|
|
post_layer_mix: torch.Tensor,
|
|
comb_res_mix: torch.Tensor,
|
|
fn: torch.Tensor,
|
|
hc_scale: torch.Tensor,
|
|
hc_base: torch.Tensor,
|
|
rms_eps: float,
|
|
hc_pre_eps: float,
|
|
hc_sinkhorn_eps: float,
|
|
hc_post_mult_value: float,
|
|
sinkhorn_repeat: int,
|
|
n_splits: int = 1,
|
|
tile_n: int = 1,
|
|
norm_weight: torch.Tensor | None = None,
|
|
norm_eps: float = 0.0,
|
|
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
if HAS_AITER_MHC_FUSED and _aiter_mhc_supported(
|
|
residual,
|
|
norm_weight,
|
|
supports_norm=HAS_AITER_MHC_FUSED_NORM,
|
|
):
|
|
return torch.ops.vllm.mhc_fused_post_pre_aiter(
|
|
x,
|
|
residual,
|
|
post_layer_mix,
|
|
comb_res_mix,
|
|
fn,
|
|
hc_scale,
|
|
hc_base,
|
|
rms_eps,
|
|
hc_pre_eps,
|
|
hc_sinkhorn_eps,
|
|
hc_post_mult_value,
|
|
sinkhorn_repeat,
|
|
n_splits,
|
|
tile_n,
|
|
norm_weight,
|
|
norm_eps,
|
|
)
|
|
if HAS_TILELANG_MHC:
|
|
return torch.ops.vllm.mhc_fused_post_pre_tilelang(
|
|
x,
|
|
residual,
|
|
post_layer_mix,
|
|
comb_res_mix,
|
|
fn,
|
|
hc_scale,
|
|
hc_base,
|
|
rms_eps,
|
|
hc_pre_eps,
|
|
hc_sinkhorn_eps,
|
|
hc_post_mult_value,
|
|
sinkhorn_repeat,
|
|
n_splits,
|
|
tile_n,
|
|
norm_weight,
|
|
norm_eps,
|
|
)
|
|
residual_cur, post_mix_cur, comb_mix_cur, layer_input_cur = self.forward_native(
|
|
x,
|
|
residual,
|
|
post_layer_mix,
|
|
comb_res_mix,
|
|
fn,
|
|
hc_scale,
|
|
hc_base,
|
|
rms_eps,
|
|
hc_pre_eps,
|
|
hc_sinkhorn_eps,
|
|
hc_post_mult_value,
|
|
sinkhorn_repeat,
|
|
n_splits,
|
|
tile_n,
|
|
norm_weight,
|
|
norm_eps,
|
|
)
|
|
return (
|
|
residual_cur,
|
|
post_mix_cur,
|
|
comb_mix_cur,
|
|
_apply_mhc_norm(layer_input_cur, norm_weight, norm_eps),
|
|
)
|
|
|
|
def forward_native(
|
|
self,
|
|
x: torch.Tensor,
|
|
residual: torch.Tensor,
|
|
post_layer_mix: torch.Tensor,
|
|
comb_res_mix: torch.Tensor,
|
|
fn: torch.Tensor,
|
|
hc_scale: torch.Tensor,
|
|
hc_base: torch.Tensor,
|
|
rms_eps: float,
|
|
hc_pre_eps: float,
|
|
hc_sinkhorn_eps: float,
|
|
hc_post_mult_value: float,
|
|
sinkhorn_repeat: int,
|
|
n_splits: int = 1,
|
|
tile_n: int = 1,
|
|
norm_weight: torch.Tensor | None = None,
|
|
norm_eps: float = 0.0,
|
|
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
# Decompose into post + pre (no fused kernel available).
|
|
residual_cur = mhc_kernels.mhc_post_torch(
|
|
x, residual, post_layer_mix, comb_res_mix
|
|
)
|
|
post_mix_cur, comb_mix_cur, layer_input_cur = mhc_kernels.mhc_pre_torch(
|
|
residual_cur,
|
|
fn,
|
|
hc_scale,
|
|
hc_base,
|
|
rms_eps,
|
|
hc_pre_eps,
|
|
hc_sinkhorn_eps,
|
|
hc_post_mult_value,
|
|
sinkhorn_repeat,
|
|
)
|
|
return residual_cur, post_mix_cur, comb_mix_cur, layer_input_cur
|
|
|
|
def forward_xpu(
|
|
self,
|
|
x: torch.Tensor,
|
|
residual: torch.Tensor,
|
|
post_layer_mix: torch.Tensor,
|
|
comb_res_mix: torch.Tensor,
|
|
fn: torch.Tensor,
|
|
hc_scale: torch.Tensor,
|
|
hc_base: torch.Tensor,
|
|
rms_eps: float,
|
|
hc_pre_eps: float,
|
|
hc_sinkhorn_eps: float,
|
|
hc_post_mult_value: float,
|
|
sinkhorn_repeat: int,
|
|
n_splits: int = 1,
|
|
tile_n: int = 1,
|
|
norm_weight: torch.Tensor | None = None,
|
|
norm_eps: float = 0.0,
|
|
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
return torch.ops._xpu_C.mhc_fused_post_pre(
|
|
x,
|
|
residual,
|
|
post_layer_mix,
|
|
comb_res_mix,
|
|
fn,
|
|
hc_scale,
|
|
hc_base,
|
|
rms_eps,
|
|
hc_pre_eps,
|
|
hc_sinkhorn_eps,
|
|
hc_post_mult_value,
|
|
sinkhorn_repeat,
|
|
)
|
|
|
|
|
|
def hc_expand(x: torch.Tensor, n: int) -> torch.Tensor:
|
|
"""[s, hidden_size] -> [s, n * hidden_size] by replication."""
|
|
return x.unsqueeze(1).expand(-1, n, -1).contiguous()
|
|
|
|
|
|
def hc_contract(x: torch.Tensor, n: int) -> torch.Tensor:
|
|
"""[s, n * hidden_size] -> [s, hidden_size] by averaging."""
|
|
return x.mean(dim=1)
|