Compare commits

...
Author SHA1 Message Date
mgoinandClaude Opus 4.8 3ef4bae91c [Kernel] Manual activation+quant fusion via QuantizedActivation (Llama)
Start the ActivationQuantFusionPass manual-fusion migration (RFC #43224)
on the producer side of the QuantizedActivation contract (#44260). Adds
maybe_fused_act_quant: given an activation and the linear it feeds, it
emits a QuantizedActivation via the fused silu_and_mul_quant kernel when
the linear advertises a consumable input_quant_key, and falls back to the
plain activation otherwise. Llama's MLP forward calls it for down_proj;
the change to model code is one line.

When manual fusion fires the silu_and_mul pattern is already consumed, so
the compiler ActivationQuantFusionPass finds nothing and the two never
double-fuse. Only (SiluAndMul, kFp8StaticTensorSym) is registered for now.

Smoke tested on RedHatAI/Llama-3.2-1B-Instruct-FP8 (default compiled +
CUDA-graph path): all down_proj layers take the fused branch and
generations are coherent. AI assistance (Claude) was used; all changed
lines are human-reviewed.

Signed-off-by: mgoin <mgoin64@gmail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 19:15:14 +00:00
2 changed files with 76 additions and 1 deletions
@@ -0,0 +1,74 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""
Producer side of the QuantizedActivation contract for activation layers.
Given an activation module and the downstream linear it feeds, fuse the
activation with that linear's input quantization into a single kernel when the
linear advertises a consumable input_quant_key (see quant_activation.py).
Falls back to the plain activation when nothing matches, so a model forward can
always call maybe_fused_act_quant unconditionally.
This is the manual-fusion counterpart to ActivationQuantFusionPass: when fusion
fires here the silu_and_mul pattern is already consumed, so the compiler pass
finds nothing to rewrite and the two never double-fuse.
"""
from collections.abc import Callable
import torch
from vllm.model_executor.layers.activation import SiluAndMul
from vllm.model_executor.layers.fusion.quant_activation import QuantizedActivation
from vllm.model_executor.layers.linear import LinearBase
from vllm.model_executor.layers.quantization.utils.quant_utils import (
QuantKey,
kFp8StaticTensorSym,
)
from vllm.platforms import current_platform
FP8_DTYPE = current_platform.fp8_dtype()
def _silu_and_mul_fp8_static(
x: torch.Tensor, linear: LinearBase
) -> QuantizedActivation:
d = x.shape[-1] // 2
out_shape = x.shape[:-1] + (d,)
result = torch.empty(out_shape, dtype=FP8_DTYPE, device=x.device)
# TODO(mgoin): read the consumer scale via the contract instead of reaching
# into the kernel-specific input_scale attribute.
scale = linear.input_scale
torch.ops._C.silu_and_mul_quant(result, x, scale)
return QuantizedActivation(
data=result,
scale=scale,
orig_dtype=x.dtype,
orig_shape=out_shape,
quant_key=kFp8StaticTensorSym,
)
# (activation module type, consumer input_quant_key) -> fused producer.
# Mirrors ActivationQuantFusionPass.FUSED_OPS; add a row to migrate a scheme.
_FUSED_ACT_QUANT: dict[tuple[type, QuantKey], Callable] = {
(SiluAndMul, kFp8StaticTensorSym): _silu_and_mul_fp8_static,
}
def maybe_fused_act_quant(
act_fn: torch.nn.Module,
x: torch.Tensor,
linear: LinearBase,
) -> "torch.Tensor | QuantizedActivation":
"""Apply act_fn, fusing the downstream linear's input quant when possible.
Returns a QuantizedActivation when a fused kernel matches
(act_fn, linear.input_quant_key), else the plain activated tensor.
"""
key = getattr(linear, "input_quant_key", None)
if key is not None:
producer = _FUSED_ACT_QUANT.get((type(act_fn), key))
if producer is not None:
return producer(x, linear)
return act_fn(x)
+2 -1
View File
@@ -39,6 +39,7 @@ from vllm.model_executor.layers.attention import (
Attention,
EncoderOnlyAttention,
)
from vllm.model_executor.layers.fusion.fused_act_quant import maybe_fused_act_quant
from vllm.model_executor.layers.layernorm import RMSNorm
from vllm.model_executor.layers.linear import (
MergedColumnParallelLinear,
@@ -117,7 +118,7 @@ class LlamaMLP(nn.Module):
def forward(self, x):
x, _ = self.gate_up_proj(x)
x = self.act_fn(x)
x = maybe_fused_act_quant(self.act_fn, x, self.down_proj)
x, _ = self.down_proj(x)
return x