Making a Ampere Optimized Quant of Qwen3.6-27b - INT4 Weights + INT8 Activations #609
Replies: 8 comments 13 replies
-
|
I addressed the decode speed bug today on dual-max, you might wanna give it a shot again before you begin your work. It's working much better now. The problem was with int8 tph kv, the requests were hitting the older Triton kernel in vllm, which was degrading decode tps. Changing it to fp8/e5m3 makes it go to the new FlashInfer kernel which seems to have addressed the issue with decode tps. dual-max was affected by this and now throws better tps than dual-fast (W4A8). |
Beta Was this translation helpful? Give feedback.
-
|
How far did you get in making your qant @chriskerley78910 ? |
Beta Was this translation helpful? Give feedback.
-
|
We have had a mixed W4A16 Qwen3.6-27B quant from Minachist for 2 months already. I'm sure you could just take his quantize.py script and modify it to make W4A8 variant in 30 min-1 hr total on dual 3090. I don't see any barriers in doing so, matter a fact stay with AutoRound and make sure you specify |
Beta Was this translation helpful? Give feedback.
-
|
Are you aware of this project: https://github.com/avesed/vllm-ampere-optimized ? Seems to be relevant to this thread. Essentially, default vLLM gates W4A8 to Hopper+, but it can be made to work on Ampere. |
Beta Was this translation helpful? Give feedback.
-
|
Followed up after Ezgera's pointer (and re-read the whole thread) — and I think it resolves the exact wall you all hit, because everyone here has been trying to make a W4A8 checkpoint, and that's the wrong frame. @3dluvr was right (that scheme-list comment): AutoRound genuinely has no So nobody needs the 11-hour build. The candidates already exist as W4A16:
One real requirement, though — and it's the catch the thread hasn't hit yet: the int4 weights must be symmetric. avesed's v0.3 note is explicit: "W4A8 asym-weight (AWQ uint4+zp)… output is garbage."
Why it works on Ampere at all (the useful mental model): W4A8-via-Marlin only needs INT8 tensor cores, which Ampere has had since sm_80 — vLLM's Hopper gate is conservative, not silicon. That's the opposite of FA3/FA4 or native FP8/FP4, which are genuinely silicon-bound (no TMA/wgmma/tcgen05 on Ampere) and can't be un-gated. Integer low precision (INT8/INT4) runs native on Ampere; float low precision (FP8/FP4) doesn't. And our own numbers back the "best of both" intuition. We've benched the INT8 W8A8 cousin ( And you don't have to wait for the W4A8 eval to get a prefill win — W8A8 is ready today. Unlike W4A8,
Why prefill moves but decode doesn't (the mechanism behind that table): the activation precision decides which tensor-core datapath the GEMM lands on. Dequant-to-bf16 (W4A16, and FP4-on-Ampere) → the bf16 cores; dequant-to-int8 (W4A8 / W8A8) → Ampere's INT8 cores at ~2× the bf16 rate — that's the prefill win, nothing more exotic. Decode is batch=1 and memory-bound on the 4-bit weight read, so the tensor-core rate is irrelevant there → decode barely moves (exactly why W4A8 keeps int4's decode while gaining int8's prefill). int4 activations would be faster still, but that's the accuracy cliff — so int8 is the floor. W4A8 is exactly "W8A8's prefill without W8A8's decode penalty" — that's the whole reason to chase it over W8A8. Until it validates, W8A8 is the ready fallback. (One hard limit on both int8 paths: W8A8 INT8 compute is sm 7.5–9.x — not on Blackwell/5090, so this is Ampere/Hopper only.) Caveat before anyone calls it production: the fork is vLLM v0.23.0 + FlashInfer 0.6.12 + CUDA 13 / torch 2.11 — around our @chriskerley78910 — want to take the first crack? You kicked this off, you've got the 3090s, and it's now a ~30-min job, not the 11-hour build. The whole experiment: grab a symmetric W4A16 (confirm avesed's, or make one — AutoRound We'll run the same on the reference 2×3090 (verify / stress / soak / 8-pack + GSM8K), so whoever posts numbers first, the other corroborates. If W4A8 lands the prefill win without denting decode or long-context recall, it's a genuinely strong Ampere serving quant and we'll wire it up as an option. Great find @Ezgera; @3dluvr your scheme-list catch was the key clue; @chriskerley78910 your instinct was right all along — it's "symmetric int4 + a flag," not a new quant. 🙏 |
Beta Was this translation helpful? Give feedback.
-
|
Since W4A4 came up earlier (@chriskerley78910's single-card idea) and the main comment stopped at W4A8, worth closing that loop — W4A8 is the floor for activations on Ampere, not a waypoint to W4A4. And "but NVFP4 is 4-bit, why not W4A4?" is the natural objection, so let me hit it head-on. The two 4-bit formats aren't the same thing, and they live on different silicon:
So the surprising bit: W4A4-integer is physically runnable on a 3090 (it has int4 tensor cores — a thing Hopper actually removed). The blocker there isn't hardware, it's:
And "NVFP4 is 4-bit" isn't a counterexample — because on Ampere, NVFP4 doesn't compute in 4 bits. Its 4-bit weights are unpacked to bf16 and the matmul runs in bf16 (the Marlin path): 4-bit storage, 16-bit compute — memory win, zero compute win. Native FP4 math is a Blackwell 5th-gen-tensor-core instruction that isn't in the sm_86 ISA — you can store FP4 on a 3090, you can't compute it (same wall as FA4/wgmma). Why FP4 is the real 4-bit-activation format and INT4 isn't: FP4 (E2M1) is floating-point — an exponent gives 4 bits a wide dynamic range, and NVFP4/MXFP4 add per-block microscaling, so it captures the activation outliers uniform INT4 can't. A usable "W4A4" therefore = FP4 activations on Blackwell (5090/GB10) — which our 3090s have neither the FP4 hardware for, nor an INT4 format good enough to substitute. On the single-card angle: A4 doesn't help there either — at batch=1 the weights are already int4 in both W4A16 and W4A4, and activations are a tiny memory slice, so A4 buys no size or decode win, just accuracy loss. Single-card levers are weight bits (int4→int3/int2 GGUF) and KV-cache quant, not activation bits. Bottom line: W4A8 is the activation floor on Ampere — int8 is the smallest activation precision that both (a) runs native on Ampere tensor cores and (b) survives the outliers. The good 4-bit-activation format (FP4) needs Blackwell silicon; the available one (INT4) is too lossy. So this thread's target — int4 weights + int8 activations — is already sitting on the right spot. |
Beta Was this translation helpful? Give feedback.
-
|
Update — I ran the full exploration today, and the story flips one more time: you don't need the fork for the W4A8 serve-time toggle at all. It's been in stock vLLM the whole time, hiding behind two real bugs — both found, root-caused, patched (pure Python), and measured. What's actually stock
Why it still fails for many checkpoints — two upstream bugs
So the requirement from my earlier comment gets sharper: not just symmetric int4 — POSITIVE symmetric int4. Measured (single RTX 3090, bf16, cudagraphs, batch 1)
All three coherent after the fixes (chat + logprob sanity — "capital of Italy is → Three honesty flags on that table:
@chriskerley78910 — decision rule for the weights you just got# 1) format: config.json → quantization_config.quant_method
# "compressed-tensors" → stock v0.24.0+ works TODAY, zero patches
# "auto-round" / gptq variants → needs the small patches (coming) or a ct-format quant
# 2) scale sign — the garbage predictor:
from safetensors import safe_open
neg = tot = 0
with safe_open("model.safetensors", framework="pt") as f:
for n in f.keys():
if "scale" in n:
s = f.get_tensor(n); neg += (s < 0).sum().item(); tot += s.numel()
print(f"{100*neg/tot:.2f}% negative scales") # anything > 0% → garbage under int8, for nowThen serve with What's next from our side
Credit where due — this one had three authors before I typed a line:
But for what chris set out to get — int4 weights + int8 activations on 3090s — stock vLLM + the right checkpoint does it today, and our two small patches extend it to the AutoRound quants everyone already has. |
Beta Was this translation helpful? Give feedback.
-
|
Quality gate results for the W4A8 numbers above — it ties the baseline on both reasoning legs. Full 8-pack on the dual-fast config (TP=2, MTP n=3, 262K, both patches): 110/150 thinking-off · 111/150 thinking-on vs the W4A16 fast tier's 109 — inside the ±5–7 noise band, with the thinking leg actually strongest (instruction-follow 15/15, bugfind 14/15). Speed — corrected with a clean single-variable A/B (both arms fp16, both through the shipped compose, only So on this stack's dual config: +50% prefill, flat decode, zero detectable quality cost — including the sign-fold path for AutoRound checkpoints. Catalog wiring is next: the patches ship with the composes (inert until enabled) and W4A8 becomes a one-env serve-time knob. The interim recipe stays as posted above. @chriskerley78910 — the config you called "basically the best possible for dual Ampere" now has quality receipts (and an honest prefill number). |
Beta Was this translation helpful? Give feedback.

Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
An earlier experiment showed significantly improvements in TTFT with 8-bit activation's, but the decode speed was still very slow compared to the INT4 weighted model [1].
I am thinking of making a quantized model of Qwen3.6-27b that is made specifically for the Amper Arch. i.e. INT4 Weights INT8 Activations.
It would have the decode speed benefit of the current default vllm/dual setup but with the TTFT speed benefit of INT8 activations ~40% faster on prefill [2].
What do you guys think?
Beta Was this translation helpful? Give feedback.
All reactions