Conversation
Introduce an opt-in path for model code to pre-quantize activations for quantized linears, so RMSNorm + input-quant fusion can be expressed directly in model code rather than relying on torch.compile passes. - Add QuantizedActivation + rms_norm_input_quant helper. - Split CutlassNvFp4LinearKernel.apply_weights into apply_weights (chain) and apply_quantized (pre-quantized entry point). - Have CompressedTensorsW4A4Fp4 advertise layer.input_quant_key and implement quantize_input; route QuantizedActivation through to the kernel's apply_quantized. - Wire Qwen3 decoder layer to call rms_norm_input_quant before qkv_proj and gate_up_proj. Only the Cutlass NVFP4 kernel opts in; FlashInfer/fbgemm fall back to the legacy in-apply quant path. Other quant methods and other models are unchanged. Requires --enforce-eager (or piecewise compile) to take effect; the isinstance(QuantizedActivation) dispatch breaks the torch.compile graph by design, which is the direction this rework is moving toward. Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: mgoin <mgoin64@gmail.com>
There was a problem hiding this comment.
Code Review
This pull request introduces input quantization fusion for NVFP4 linear kernels. Key changes include refactoring the CUTLASS kernel to support pre-quantized activations via a new apply_quantized method, adding a QuantizedActivation dataclass, and implementing the rms_norm_input_quant utility to combine RMS normalization with quantization. The Qwen3 model has been updated to leverage these improvements. Feedback was provided regarding the safety of attribute access in the rms_norm_input_quant function, suggesting the use of getattr and hasattr to prevent potential AttributeError when accessing quant_method on a torch.nn.Module.
| if getattr(linear, "input_quant_key", None) is None: | ||
| return out, residual | ||
| return linear.quant_method.quantize_input(linear, out), residual |
There was a problem hiding this comment.
The access to linear.quant_method is unsafe here. linear is typed as torch.nn.Module, which does not have a quant_method attribute in the standard PyTorch API. While vLLM linear layers typically have this attribute, it's safer to use getattr to avoid potential AttributeError. Additionally, quant_method could be None for unquantized layers, or it might not implement quantize_input if it's a different quantization method.
| if getattr(linear, "input_quant_key", None) is None: | |
| return out, residual | |
| return linear.quant_method.quantize_input(linear, out), residual | |
| if getattr(linear, "input_quant_key", None) is not None: | |
| quant_method = getattr(linear, "quant_method", None) | |
| if quant_method is not None and hasattr(quant_method, "quantize_input"): | |
| return quant_method.quantize_input(linear, out), residual | |
| return out, residual |
…env var Extend the manual-input-quant hoisting prototype to compressed_tensors static-per-tensor FP8 (kFp8StaticTensorSym) and wire the Llama decoder layer to use it. Smoke-tested on RedHatAI/Llama-3.2-1B-FP8 with byte-identical output vs. the legacy path. - Gate scheme opt-in on VLLM_HOIST_INPUT_QUANT=1; default off keeps the torch.compile + rms_quant_fusion pass active as today. - CompressedTensorsW8A8Fp8: opt in only when static-tensor activation; quantize_input calls scaled_fp8_quant(layer.input_scale), apply_weights routes QuantizedActivation by passing the FP8 tensor through (kernel already has a "skip quant if x.dtype == fp8" branch). - CompressedTensorsW4A4Fp4: same gate retroactively applied so the NVFP4 path also defaults off. - llama.py LlamaDecoderLayer.forward: call rms_norm_input_quant before qkv_proj and gate_up_proj. Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: mgoin <mgoin64@gmail.com>
Add a scheme-level rms_norm_quantize_input that the helper prefers over the chained norm+quantize path. CompressedTensorsW8A8Fp8 implements it by calling torch.ops._C.rms_norm_static_fp8_quant / fused_add_rms_norm_static_fp8_quant directly, so manual-fusion mode hits the same C++ kernel as the compile-fusion path. CompressedTensorsLinearMethod gains a method-level forwarder that delegates to the scheme's fused entry point if present, otherwise falls back to chained norm + scheme.quantize_input. NVFP4 keeps the chained path since no real fused kernel exists yet. Tested on RedHatAI/Llama-3.2-1B-FP8 across all four mode combinations (eager/compile x HOIST=0/1) with byte-identical generated text; verified under fullgraph torch.compile that Dynamo traces into the new path. Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: mgoin <mgoin64@gmail.com>
Both manual-fusion and compile paths produce byte-identical output and both reach the same fused C++ kernel, so the env-var gate is just dead weight. Each scheme keeps its own narrow correctness gate (NVFP4: only when the kernel exposes apply_quantized; FP8: only static-per-tensor), so non-supported configurations still fall through to the legacy path. Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: mgoin <mgoin64@gmail.com>
…o kernel Strip the NVFP4 + Qwen3 changes to focus the prototype on static FP8. Move the QuantizedActivation-vs-Tensor dispatch from CompressedTensorsW8A8Fp8.apply_weights down into FP8ScaledMMLinearKernel.apply_weights so the scheme and the CompressedTensorsLinearMethod are pure passthroughs; the isinstance check lives in exactly one place. Drop the now-unused CompressedTensorsW8A8Fp8.quantize_input and CompressedTensorsLinearMethod.quantize_input forwarder, and collapse rms_norm_quantize_input on the method to a one-line delegation. Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: mgoin <mgoin64@gmail.com>
Make quant_fusion agnostic of the LinearMethod/Scheme abstractions: it
reads layer.input_quant_key and dispatches directly to the right fused
op (today: kFp8StaticTensorSym → torch.ops._C.{rms_norm,fused_add_rms_norm}_static_fp8_quant).
Remove the now-unused rms_norm_quantize_input methods from
CompressedTensorsW8A8Fp8 and CompressedTensorsLinearMethod.
Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: mgoin <mgoin64@gmail.com>
Make the rms_norm_input_quant signature kwarg-explicit so model code documents what's being pulled from the downstream linear instead of handing the whole module to the helper to introspect. Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: mgoin <mgoin64@gmail.com>
…all site" This reverts commit 5c84221.
|
This pull request has merge conflicts that must be resolved before it can be |
Prototype for the torch.compile-removal migration: model code calls a small helper that runs RMSNorm and, when the downstream linear opts in, returns a
QuantizedActivationproduced by the fusedrms_norm_static_fp8_quantC++ kernel. The linear's kernel passes it through to the matmul without re-quantizing.Scope: static-per-tensor FP8 (
kFp8StaticTensorSym) via compressed-tensors, wired through Llama.quant_fusion.py:QuantizedActivation+rms_norm_input_quant. Dispatches onlayer.input_quant_key; no scheme/method indirection.CompressedTensorsW8A8Fp8: setslayer.input_quant_keyfor static-tensor;apply_weightsis a passthrough.FP8ScaledMMLinearKernel.apply_weights: unwrapsQuantizedActivationand skips internal quant.LlamaDecoderLayer.forward: calls the helper beforeqkv_projandgate_up_proj.Tested on
RedHatAI/Llama-3.2-1B-FP8in both eager andtorch.compilemodes; output matches main. AI-assisted.