HPC fused iHC (independent Hyper-Connections) kernels for HY V4.
Replaces the eager HYV4HCPreLayer / HYV4HCPostLayer / HYV4HCHeadLayer bodies with single-kernel HPC implementations:
pre : rms square sum + w projection + sigmoid gates + hc-dim weighted sum post : y[n, i, :] = H_post[n, i] * x[n, :] + residual[n, i, :] head : same as pre but hc_mult projection rows and no H_post output
The eager path issues 20 / 5 / 15 kernels for pre / post / head; each HPC op is a single kernel.
Constraints
- Requires VLLM_ENABLE_HPC_OPS=1
- Requires the hpc package (.so) built for the current arch
- Only sm100 / sm103 (compute capability 100, 103)
- Only hc_mult == 4 and hidden_size in {4096, 6144}
- x / residual must be bfloat16; hc_fn weights stay float32 (the checkpoint keeps them out of fp8 quantization via modules_to_not_convert)
The fused modules own no parameters: they hold references to the weights of the eager layer they replace, bound in process_weights_after_loading (invoked generically for every HpcModule by the model loader).
NOTE: The reference implementation also offers a cross-layer post+pre fusion (HpcIHCPostPre) that folds one segment's post into the next segment's pre. It requires reworking the decoder-layer forward scheduling and is not ported here. TODO: add it once the decoder dataflow is restructured for it.
Classes:
-
HpcIHCHead – Fused iHC head block: merge the hc channels back into one hidden state.
-
HpcIHCPost – Fused iHC post block: H_post gating plus multi-channel residual add.
-
HpcIHCPre –
HpcIHCHead
Bases: HpcModule
Fused iHC head block: merge the hc channels back into one hidden state.
Same structure as HpcIHCPre but the projection emits only hc_mult gate logits and there is no H_post output. Called once per forward, after the last decoder layer.
Source code in vllm/model_executor/layers/hpc/hpc_ihc.py
| class HpcIHCHead(HpcModule):
"""Fused iHC head block: merge the hc channels back into one hidden state.
Same structure as HpcIHCPre but the projection emits only hc_mult gate
logits and there is no H_post output. Called once per forward, after the
last decoder layer.
"""
def __init__(
self,
hc_mult: int,
hidden_size: int,
hc_eps: float,
norm_eps: float,
fallback_op: torch.nn.Module,
) -> None:
super().__init__()
self.hc_mult = hc_mult
self.hidden_size = hidden_size
self.hc_eps = hc_eps
self.norm_eps = norm_eps
# See HpcIHCPre: keep the owner out of the module tree.
object.__setattr__(self, "_fallback_op", fallback_op)
self.weight = None
self.hc_scale = None
self.hc_base = None
@classmethod
def support(cls, hc_mult: int, hidden_size: int) -> bool:
return _ihc_supported(hc_mult, hidden_size)
def process_weights_after_loading(self, model=None):
# hc_head_fn weight is [hc_mult, hc_mult * d], float32.
owner = self._fallback_op
self.weight = owner.hc_head_fn.weight
self.hc_scale = owner.hc_head_scale
self.hc_base = owner.hc_head_base
def forward(self, x: torch.Tensor) -> torch.Tensor:
import hpc
return hpc.fuse_ihc_head(
x,
self.weight,
self.hc_scale,
self.hc_base,
self.norm_eps,
self.hc_eps,
)
|
HpcIHCPost
Bases: HpcModule
Fused iHC post block: H_post gating plus multi-channel residual add.
Unlike mHC there is no comb matrix, so each output channel only needs its own residual channel and the whole thing is one fused multiply-add per element.
Source code in vllm/model_executor/layers/hpc/hpc_ihc.py
| class HpcIHCPost(HpcModule):
"""Fused iHC post block: H_post gating plus multi-channel residual add.
Unlike mHC there is no comb matrix, so each output channel only needs its
own residual channel and the whole thing is one fused multiply-add per
element.
"""
def __init__(self, hc_mult: int, hidden_size: int) -> None:
super().__init__()
self.hc_mult = hc_mult
self.hidden_size = hidden_size
@classmethod
def support(cls, hc_mult: int, hidden_size: int) -> bool:
return _ihc_supported(hc_mult, hidden_size)
def forward(
self, x: torch.Tensor, residual: torch.Tensor, H_post: torch.Tensor
) -> torch.Tensor:
import hpc
return hpc.fuse_ihc_post(x, residual, H_post)
|
HpcIHCPre
Bases: HpcModule
Fused iHC pre block.
Computes, in one kernel: x_flat = x.flatten(1) r = rsqrt(x_flat.square().mean(-1) + rms_norm_eps) mixes = (x_flat @ w.T) * r H_pre = sigmoid(mixes[:, :hc] * hc_scale[0] + hc_base[:hc]) + hc_eps H_post = magnitude * sigmoid( mixes[:, hc:] * hc_scale[1] + hc_base[hc:]) + hc_eps y = sum_i H_pre[:, i] * x[:, i, :]
Parameters:
-
hc_mult
(int) – -
hidden_size
(int) – -
magnitude
(float) – H_post multiplier (config.hc_magnitude).
-
hc_eps
(float) – Epsilon added to both gates (config.hc_eps).
-
norm_eps
(float) – Epsilon inside the rsqrt (config.rms_norm_eps).
-
fallback_op
(Module) – The eager HYV4HCPreLayer to source weights from.
-
norm_owner
(Module | None, default: None ) – Optional RMSNorm that immediately follows this pre block. When given, its weight/eps are folded into the kernel so a single launch covers pre + RMSNorm (the caller then skips the separate layernorm). None keeps the original pre-only behaviour.
Source code in vllm/model_executor/layers/hpc/hpc_ihc.py
| class HpcIHCPre(HpcModule):
"""Fused iHC pre block.
Computes, in one kernel:
x_flat = x.flatten(1)
r = rsqrt(x_flat.square().mean(-1) + rms_norm_eps)
mixes = (x_flat @ w.T) * r
H_pre = sigmoid(mixes[:, :hc] * hc_scale[0] + hc_base[:hc]) + hc_eps
H_post = magnitude * sigmoid(
mixes[:, hc:] * hc_scale[1] + hc_base[hc:]) + hc_eps
y = sum_i H_pre[:, i] * x[:, i, :]
Args:
hc_mult: HC expand ratio.
hidden_size: Model hidden dimension.
magnitude: H_post multiplier (config.hc_magnitude).
hc_eps: Epsilon added to both gates (config.hc_eps).
norm_eps: Epsilon inside the rsqrt (config.rms_norm_eps).
fallback_op: The eager HYV4HCPreLayer to source weights from.
norm_owner: Optional RMSNorm that immediately follows this pre block.
When given, its weight/eps are folded into the kernel so a single
launch covers pre + RMSNorm (the caller then skips the separate
layernorm). None keeps the original pre-only behaviour.
"""
def __init__(
self,
hc_mult: int,
hidden_size: int,
magnitude: float,
hc_eps: float,
norm_eps: float,
fallback_op: torch.nn.Module,
norm_owner: torch.nn.Module | None = None,
) -> None:
super().__init__()
self.hc_mult = hc_mult
self.hidden_size = hidden_size
self.magnitude = magnitude
self.hc_eps = hc_eps
self.norm_eps = norm_eps
# Stash the owner outside nn.Module's attribute machinery: callers pass
# their own `self` here, and registering that as a submodule would make
# the module tree cyclic (state_dict() then hits RecursionError).
object.__setattr__(self, "_fallback_op", fallback_op)
# Same anti-cycle idiom for the fused RMSNorm owner (a sibling module).
object.__setattr__(self, "_norm_owner", norm_owner)
self.weight = None
self.hc_scale = None
self.hc_base = None
self.rms_weight = None
self.rms_eps = 0.0
@classmethod
def support(cls, hc_mult: int, hidden_size: int) -> bool:
return _ihc_supported(hc_mult, hidden_size)
def process_weights_after_loading(self, model=None):
# hc_fn is a ReplicatedLinear whose weight is [2 * hc_mult, hc_mult * d].
# It stays float32 -- the HPC kernel consumes fp32 weights directly, so
# there is no conversion (and no precision loss) here.
owner = self._fallback_op
self.weight = owner.hc_fn.weight
self.hc_scale = owner.hc_scale
self.hc_base = owner.hc_base
if self._norm_owner is not None:
self.rms_weight = self._norm_owner.weight
self.rms_eps = self._norm_owner.variance_epsilon
def forward(self, x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
import hpc
return hpc.fuse_ihc_pre(
x,
self.weight,
self.hc_scale,
self.hc_base,
self.norm_eps,
self.hc_eps,
self.magnitude,
self.rms_weight,
self.rms_eps,
)
|
_ihc_supported(hc_mult, hidden_size)
Shared gate for all three iHC ops.
Source code in vllm/model_executor/layers/hpc/hpc_ihc.py
| def _ihc_supported(hc_mult: int, hidden_size: int) -> bool:
"""Shared gate for all three iHC ops."""
if not envs.VLLM_ENABLE_HPC_OPS:
return False
if not has_hpc():
logger.warning_once(
"HPC iHC disabled: 'hpc' package is not installed. "
"Please install the HPC library (.so) to enable fused kernels."
)
return False
from vllm.platforms import current_platform
if not current_platform.is_cuda():
logger.warning_once("HPC iHC disabled: only CUDA is supported.")
return False
capability = current_platform.get_device_capability()
if capability is None or capability.to_int() not in _SUPPORTED_CAPABILITIES:
logger.warning_once(
"HPC iHC disabled: compute capability %s not in %s.",
capability,
_SUPPORTED_CAPABILITIES,
)
return False
if hc_mult not in _SUPPORTED_HC_MULTS:
logger.warning_once(
"HPC iHC disabled: hc_mult=%d not in %s.", hc_mult, _SUPPORTED_HC_MULTS
)
return False
if hidden_size not in _SUPPORTED_HIDDEN_SIZES:
logger.warning_once(
"HPC iHC disabled: hidden_size=%d not in %s.",
hidden_size,
_SUPPORTED_HIDDEN_SIZES,
)
return False
if envs.VLLM_BATCH_INVARIANT:
# Batch-invariant mode relies on torch's deterministic reductions; the
# HPC kernel reduces in a different order, so keep the two regimes
# apart.
logger.warning_once(
"HPC iHC disabled: not supported under VLLM_BATCH_INVARIANT."
)
return False
logger.info_once("HPC iHC enabled by set VLLM_ENABLE_HPC_OPS.")
return True
|