[Perf][SM70] Narrow Qwen3.8 AWQ active grouped decode - #520
Conversation
Reuse the existing active-stage operator for TP4 g32 M2-8 decode, align warmup admission, and annotate scratch mutations. Keep C1 and unmatched contracts on existing routes; add focused coverage without new public operators or GEMM kernels. Validated with 51 GPU-directed tests, 32 dynamic-route operator comparisons, and QSA-fixed matched model runs. Single-run pure-decode gains: C4x64K +5.62%, C8x16K +2.24%. Natural-EOS AWQ outputs match 17/18; the retained and/+ near-tie and W13 rounding differences are not claimed bitwise exact. No precision-default changes or deployment. Co-authored-by: OpenAI Codex Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Separate implementation-only and existing-autotune gains; document EOS rounding limitations, capacity and startup observations, rollback, and the dual-format C1 follow-up. Co-authored-by: OpenAI Codex Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Record the new main integration gate separately from frozen GPU results; keep prefill and grouped decode admission disjoint. Co-authored-by: OpenAI Codex Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Co-authored-by: OpenAI Codex Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Assisted-by: OpenAI Codex Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Use the retained engine log instead of extrapolating cache block count. No production or benchmark changes. Co-authored-by: OpenAI Codex <noreply@openai.com> Signed-off-by: Leonccaa <166551845+Leonccaa@users.noreply.github.com>
Fresh matched default/rollback regression (2026-09-06 UTC)Reviewed head Version boundary: #522 subsequently changed its compact-metadata default at Contract: 4 x V100 32GB / TP4, the same AWQ checkpoint and frozen input IDs, One warmup then one scored run per cell, output cap 320,
“Online pure decode” uses one shared engine-output wall interval: after Quality and numerical limitsThe 65 frozen quality records had no per-record score flips, with 45/65 Completion changes must not be hidden by unchanged scores: IFEval case Same-arm teacher-forced A/A was exactly equal (70 rows in each arm), but Both model/scoring captures completed successfully. Ready live allocation Startup-peak clarification: neither tested #522 snapshot includes the still-open AI assistance: Codex performed this regression and prepared the addendum at |
Purpose
Reuse the existing SM70 AWQ active-stage operator for Qwen3.8 TP4 small-batch decode (2 to 8 input tokens), and align CUDA Graph warmup with that route so replay hits the same kernels. This is the reduced-surface successor to closed #491: no new operator, Python wrapper or GEMM kernel is added.
VLLM_SM70_AWQ_QWEN38_MOE_COMPACT_GROUPED_DECODE=0before startup.VLLM_SM70_AWQ_TUNE_SMALL_SHAPES=1autotune is documented as an optional, separately measured configuration and stays globally default-off.Implementation
Five production files, one test file, one design/evidence document.
awq_sm70_moe.py: layer contract check_qwen38_active_grouped_layer_contract, runtime admission_use_qwen38_active_grouped_decode(2 to 8 tokens, top-k 10, batched GEMM on, the exact-W2 experiment flags off), and the W13/W2 route switch ontoawq_moe_active_dense_stage_sm70_outwith the grouped-active exact W2 reduction.awq_sm70_warmup.py: warm the active-stage op for the admitted 2 to 8 token shapes with the same admission function, so capture and replay use the same dynamic-offset GEMM.awq_sm70_gemm.cu: insideawq_moe_active_dense_stage_sm70_out, a 17-line fast path for exactly these shapes (group 32, 20 to 80 slots in multiples of 10, K2560/N320 or K160/N2560) that runs the offsets-based GEMM in one call instead of the per-segment loop.torch_bindings.cpp: the schema now declaresactive_expert_offsetsandactive_expert_idsas mutated (Tensor(b!),Tensor(c!)), which the implementation already did.envs.py: the new default-on switch.Review note: the C++ fast path additionally reads the same environment variable with
std::getenv, so the op stays self-guarding for direct callers such as warmup and one variable rolls back both sides. The Python admission is the primary gate. If maintainers prefer an explicit argument instead of the environment read, that is a small follow-up and does not change the measured route.Results
4x Tesla V100 PCIe 32 GB, TP4/MTP0, FP16 activations and KV, chunked prefill with 8192 batched tokens, prefix cache off, frozen input IDs,
ignore_eos=false, 320 output tokens, one score per cell and arm. Pure aggregate decode tok/s over the common all-active window.Implementation-only effect, frozen base
5cceeaad89, autotune off in both arms:Additional effect of the existing opt-in autotune, grouped on in both arms, fresh processes, no imported LUT:
A post-score C4 profile shows the W13 tactic moving from M64x128x32 to M8x256x64 on all four ranks, 5.531 to 2.109 GPU ms per step, with other categories nearly unchanged. Do not add the two tables' percentages.
Integration acceptance on base
755baae1d0(grouped decode and autotune enabled, shared-expert overlap off): C1 x 64K 51.29, C4 x 64K 131.71, C8 x 16K 247.59 tok/s. All 18 short quality requests stopped at EOS and passed basic answer checks; C4 self-repeats matched complete token IDs and top-5 logprobs 4/4. KV capacity 427,385 tokens, graph memory 0.37 GiB, no OOM. This is integration acceptance on the new base, not a new OFF/ON pair; a same-base OFF/ON pair on currentmainhas not been rerun yet. E2E for these long-prefill cells improves only about 1.4% (C4) and 3.4% (C8) because prefill dominates them; the decode-window gain is the target of this PR.Precision
andvs+, answer unchanged) and an open Chinese response of 71 vs 70 tokens.Duplicate-work check
Open-PR searches on 2026-09-06 in
1CatAI/1Cat-vLLMfor AWQ grouped decode, "AWQ QPN", "SM70 AWQ" and "#491" found no competing AWQ small-batch route. #504 is NVFP4 batched HC/QSA work; #509 is conversion-cache release; #519 adds an AWQ M=1 operator and does not touch the 2 to 8 token path. Base ismain@755baae1d0.Tests
tests/quantization/test_sm70_awq_active_grouped_decode.py,test_sm70_awq_indexed_prefill.py,test_sm70_awq_compact_metadata.py,test_awq_sm70_persistent_cap.py: 70 passed. Boundary cases cover 1/2/8/9/127/128 tokens and disjoint indexed-prefill versus grouped-decode admission.git diff --check: clean._C,_moe_C,_C_stable_libtorchbuilt for SM70 from this head, import and SHA-256 gate, then the bounded model acceptance above.Provenance
Fork review draft: Leonccaa#8 (same commits). The opt-in AWQ M=1 dispatch proposed separately reuses
_qwen38_active_grouped_layer_contractfrom this PR; the M=1 operator itself (#519) is independent.AI assistance was used. OpenAI Codex implemented the change, tests and evidence under Leon's direction; Claude Code reviewed the diff, re-ran the CPU tests and pre-commit, and prepared this submission. Human review is still required.
🤖 Generated with Claude Code