inference/research
verifiedhigh · 0.92majorupdated 2026-10-04

vLLM W4A8 (INT4 weight + INT8 activation) is CPU-only, not Arm-only: it runs on x86 through two code paths the support table marks NO, and the NVIDIA NOs are real because loading fails loudly

[major] kernel

Fields

id
gotchas/w4q-vllm-w4a8-is-cpu-only-on-cpu-not-arm-only
Class
kernel
Severity
major
Concepts
w4a8llm-compressorcompressed-tensorskleidiaiamxavx512kernel-selectionsm90fallthrough-to-valueerror
Symptom
Two opposite surprises from one format. On the GPU side, an INT4-weight + INT8-activation checkpoint refuses to load at all with ValueError('Failed to find a kernel that can implement the WNA16 linear layer. Reasons: ...') followed by a per-kernel reason list in which every candidate says why it declined - this is a hard failure, not a slow path, and it is the good outcome. On the CPU side, the vendor support table says W4A8 is available on Arm CPU and NOT on x86 CPU, so an operator who reads it will conclude the format cannot run on x86 at all. It can. It runs on x86 by two independent routes, and neither is exotic: a general AVX512 route that needs nothing special, and an AMX-tile route on Sapphire Rapids or newer with bfloat16 activations.
Root cause
The GPU side is real and mechanical. CompressedTensorsW4A8Int.get_min_capability() returns 1 - the lowest value the selector admits - precisely because it does not gate on GPU capability; it delegates to choose_mp_linear_kernel() with weight_type=int4 and act_type=params_dtype. The CUDA candidate list is [CutlassW4A8LinearKernel, MacheteLinearKernel, MarlinLinearKernel, ConchLinearKernel, ExllamaLinearKernel, TritonW4A16LinearKernel, HummingLinearKernel], and all seven decline an INT8-activation W4A8 config: CutlassW4A8 requires act_type == torch.float8_e4m3fn; Machete requires is_device_capability(90) exactly plus FP16/BF16/FP8 activations; Marlin, Triton and Humming are weight-only (no input_quant_key, so they cannot quantize activations at all); Conch is weight-only with group sizes [-1,128] and needs the optional conch-triton-kernels package; Exllama 'only supports float16 activations'. The loop then raises ValueError with the accumulated reasons. The x86 side: the same selector's CPU list is [Dynamic4bitLinearKernel, ZentorchWNA16LinearKernel, CPUWNA16LinearKernel], and Dynamic4bitLinearKernel - whose own module docstring calls it 'the KleidiAI-accelerated w4a8int quantization scheme' - has a can_implement() that checks only is_cpu() and weight_type in [scalar_types.int4]. Its Arm-specific requirements (float32/bfloat16/float16 activations, and a torch.ops.aten._dyn_quant_matmul_4bit availability probe) both live inside a branch keyed on current_platform.get_cpu_architecture() == CpuArchEnum.ARM, so they do not exclude x86. Separately, CPUWNA16LinearKernel.process_weights_after_loading() computes layer.use_w4a8 = envs.VLLM_CPU_INT4_W4A8 (default True) and act_type == torch.bfloat16 and (torch.cpu._is_amx_tile_supported() or architecture is RISCV); when true it repacks to the AWQ blocked format via ops.convert_weight_packed_scale_zp(..., ops.CPUQuantAlgo.AWQ) and apply_weights dispatches to ops.int4_scaled_mm_cpu instead of ops.cpu_gemm_wna16. That op exists for x86: csrc/cpu/sgl-kernels/gemm_int4.cpp defines int4_scaled_mm_cpu delegating to int4_scaled_mm_cpu_with_quant, which quantizes activations itself ('activation quants into uint8_t', constexpr bool sym_quant_act = false), and the file's arch guards are CPU_CAPABILITY_AVX512 and CPU_CAPABILITY_RVV. The root cause of the doc error is that the support table's x86 CPU cell was transcribed rather than derived, while the Arm cell was derived from the recipe document's KleidiAI sentence.
Workaround
On a GPU, do not try to make W4A8 work by changing kernels - it is not a kernel-selection problem, the implementation does not exist for INT8 activations on any NVIDIA part in vLLM main. If you have an INT4-weight + INT8-activation checkpoint and a GPU, the two real options are to re-quantize to a weight-only format (AWQ or GPTQ W4A16, which Marlin serves from SM75 up) or to move the W4A8 model to CPU serving, which x86 now supports. On x86 CPU, enable it by ensuring bf16 activations and AMX tile support for the fast route, or accept the AVX512 route otherwise; both are reached without any flag beyond VLLM_CPU_INT4_W4A8, which is already True by default. One near-miss to be aware of: a W4A8 checkpoint whose ACTIVATIONS are FP8 e4m3 rather than INT8 is a DIFFERENT format in vLLM (CompressedTensorsW4A8Fp8) and is gated by _is_fp8_w4a8_sm90, which calls _check_scheme_supported(90, match_exact=True) - capability must equal 90 exactly, so it runs on Hopper SM90 and on nothing else, including sm100/103/107 and sm120.
notes
VERIFIED NEGATIVE, WITH EVIDENCE, for the claim 'W4A8 needs Arm'. Evidence is the absence of any x86 or ISA exclusion in Dynamic4bitLinearKernel.can_implement() and the existence of the x86 AVX512 CPU op int4_scaled_mm_cpu with CPU_CAPABILITY_AVX512 guards, plus the AMX/RISC-V route in CPUWNA16LinearKernel gated on torch.cpu._is_amx_tile_supported(). VERIFIED NEGATIVE, WITH EVIDENCE, for W4A8-INT8 on every NVIDIA generation in vLLM main: the ValueError from choose_mp_linear_kernel, with all seven CUDA candidates' can_implement() reasons. CONFIRMED 2026-10-04 against vLLM main. IMPORTANT FOR THIS REPO: the previous reading of qhw-vllm-supported-hardware-matrix stated that 'run W4A8 on the CPU is false on x86' and built a decision rule on it. That rule is WRONG and has been corrected in that record. The corrected shape is the inverse and is the more useful fact - W4A8 is unavailable on every GPU and available on CPU on both x86 and Arm, so it is a CPU-serving format, not a hardware-capability question. This repo prefers a documented failure to a blank cell, and both directions are now documented rather than one being a gap. Not to be confused with the separate, unrelated 'W4A8' naming in TensorRT-LLM's matrix, whose W4A8 AWQ/GPTQ columns mean INT4 weights with INT8 activations under TRT-LLM's own kernels and are marked '.' for Ampere and all four cells '.' for sm120.

Notes

VERIFIED NEGATIVE, WITH EVIDENCE, for the claim 'W4A8 needs Arm'. Evidence is the absence of any x86 or ISA exclusion in Dynamic4bitLinearKernel.can_implement() and the existence of the x86 AVX512 CPU op int4_scaled_mm_cpu with CPU_CAPABILITY_AVX512 guards, plus the AMX/RISC-V route in CPUWNA16LinearKernel gated on torch.cpu._is_amx_tile_supported(). VERIFIED NEGATIVE, WITH EVIDENCE, for W4A8-INT8 on every NVIDIA generation in vLLM main: the ValueError from choose_mp_linear_kernel, with all seven CUDA candidates' can_implement() reasons. CONFIRMED 2026-10-04 against vLLM main. IMPORTANT FOR THIS REPO: the previous reading of qhw-vllm-supported-hardware-matrix stated that 'run W4A8 on the CPU is false on x86' and built a decision rule on it. That rule is WRONG and has been corrected in that record. The corrected shape is the inverse and is the more useful fact - W4A8 is unavailable on every GPU and available on CPU on both x86 and Arm, so it is a CPU-serving format, not a hardware-capability question. This repo prefers a documented failure to a blank cell, and both directions are now documented rather than one being a gap. Not to be confused with the separate, unrelated 'W4A8' naming in TensorRT-LLM's matrix, whose W4A8 AWQ/GPTQ columns mean INT4 weights with INT8 activations under TRT-LLM's own kernels and are marked '.' for Ampere and all four cells '.' for sm120.

Cross-references