Skip to content

perf(quantizer): vectorize MSE shrink grid and AWQ clip grid evaluation - #3435

Merged
Qubitium merged 3 commits into
mainfrom
mse-clip-grid-vectorize
Oct 24, 2026
Merged

Qubitium merged 3 commits into
mainfrom
mse-clip-grid-vectorize

Conversation

@Qubitium

@Qubitium Qubitium commented Oct 24, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Vectorize the MSE shrink grid in Quantizer.find_params (GPTQ + QQQ) and the
AWQ clip grid in AWQProcessor._compute_best_clip. Each grid step used to
launch its own clamp/quantize/error/where chain (~a dozen small kernels per
candidate, ~100-200 candidates per call); the grids now evaluate in large
chunked batches, so launch overhead collapses into a few ops per chunk.

The implementation preserves candidate order and first-minimum selection,
including chunk boundaries, adjacent-zero search (2-bit), and the trits
(x > s/2)*s + (x < z/2)*z -> subtract -> abs -> pow chain.
Exact CPU oracle comparisons pass for the tested inputs; GPU numerical
equivalence and performance have not been validated on this host.

Follow-up review found and fixed premature FP16/BF16 AWQ factor rounding,
an unchunked AWQ reference-product allocation, and MSE reduction-order drift
for sliced transpose views. The new regression cases detect all three bugs
in the pre-review implementation.

What Changed

  • quantizer.py: new module-level _mse_shrink_grid — chunked evaluator over
    the full (candidates, rows, cols) grid. Quantizer.find_params builds the
    candidate p/xmin/xmax/scale/zero tensors once (host-float 1 - i/grid
    rounded to xmin's dtype, identical to the loop) and delegates; the 2-bit
    adjacent-zero search interleaves candidates in the same order.
  • qqq.py: Quantizer.find_params reuses the same _mse_shrink_grid
    (guard_zero_scale=False, matching QQQ's unguarded quantize).
  • awq_processor.py: _compute_best_clip evaluates the shrink-factor grid in
    candidate chunks with preallocated clamp/quant/out/product scratch buffers;
    org_max_val stays the baseline candidate, and argmin over stacked errors
    replaces the per-step where updates.
  • Workspace chunk-sizing targets: GPTQMODEL_MSE_GRID_BUDGET_MB and
    GPTQMODEL_CLIP_GRID_BUDGET_MB default to 256 MiB on GPU and 16 MiB on CPU —
    on CPU a large transient only evicts the L3-resident working set, so a small
    chunk wins there. These are not total allocation caps: AWQ sizes scratch
    buffers independently, and the minimum one-candidate/full-token-error or
    one-token-product buffers can exceed the target. Documented in the README.
  • AWQ shrink factors use float32 opmath for FP16/BF16 weights, rounding only
    the multiplied bounds, just like the scalar loop. The unclipped reference
    product now reuses token-tiled scratch rather than allocating all tokens.
  • Physical-layout note: when x is a transpose view, including strided slices,
    the sequential code's quantize output inherited column-major order and
    torch.sum accumulated in that order; the workspace is allocated in the same
    physical order so the reduction walks memory identically (a 1-ULP-different
    sum could flip strict-< ties).

Tests

Independent sequential Torch oracles cover MSE, QQQ, and AWQ candidate
selection. New cases cover FP16/BF16/FP32 AWQ bounds (symmetric/asymmetric,
grouped/ungrouped, one-candidate and batched execution), mixed dtypes, empty
grids, ties, overflow, allocation shapes, strided MSE objectives, rounding
thresholds, and QQQ low-precision chunk boundaries. Floating-point assertions
use rtol=atol=0; no relaxed tolerances hide changed selection.

The formerly unused _DEVICES list now drives actual CPU/CUDA MSE test
classes. CUDA tests explicitly skip when unavailable.

  • Unit tests executed and passed (CUDA and fixture-dependent gaps below).
  • Added simple, fast regression tests.
$ OMP_NUM_THREADS=4 python -m pytest tests/test_find_params_mse_grid.py \
    tests/test_qqq_quantizer.py tests/test_awq_clip_consistency.py \
    -q --tb=short -o log_cli=false -o addopts=''
# 134 passed, 72 skipped (CUDA unavailable)

$ ruff check --config format/ruff.toml gptqmodel/quantization/quantizer.py \
    gptqmodel/quantization/qqq.py gptqmodel/looper/awq_processor.py \
    tests/test_find_params_mse_grid.py tests/test_awq_clip_consistency.py \
    tests/test_qqq_quantizer.py
# 2 pre-existing findings in awq_processor.py; no new findings.
# All changed tests and both quantizer modules are clean.

Regression sensitivity: running the new targeted tests with the pre-review
implementations produced 19 failures (16 low-precision AWQ cases, the
reference-product allocation test, and two strided MSE objective cases).

Adding tests/test_qqq.py produced 137 passed, 73 skipped, and 3 setup errors:
its TestGroupSize integration fixture /monster/data/model/Llama-3.2-1B
is absent. Those integration cases were not validated.

Historical validation (before follow-up review)

The original out-of-tree harness replayed the sequential loops on the same
inputs: 1728/1728 checks bitwise-equal covering bits 2/3/4/8, sym/asym,
groupsizes -1/16/32/128, requires_groupwise_processing, trits, 2-bit
adjacent-zero search, QQQ (sym/asym × groupsize), clip sym/asym × groupsize,
activation 3-D/4-D, pathological inputs (outliers, zeros, constant channels),
and forced single-candidate chunking. Its coverage missed the FP16/BF16 AWQ
bug found during review; this is not evidence of universal equivalence.

End-to-end: quantized hf-internal-testing/tiny-random-LlamaForCausalLM
(4-bit, group_size=-1, mse=2.4, 8 calibration batches) with stock vs
vectorized find_params — identical state-dict digest (SHA-256 over all
non-meta tensors).

The original end-to-end digest check has not been rerun after the review fixes.

Historical benchmarks (before follow-up review)

CPU (torch 2.14.1, 8 threads, median of 5 runs per call). These measurements
predate the review fixes; no GPU performance measurements are available.

Case Legacy (ms) Vectorized (ms) Speedup
gptq 4b sym=0 gs=-1 128x512 25.3 4.3 5.94x
gptq 4b sym=0 gs=128 128x512 24.2 4.3 5.58x
gptq 4b sym=0 gs=-1 512x2048 82.4 68.9 1.20x
gptq 4b sym=0 gs=128 512x2048 76.0 65.3 1.16x
gptq 4b sym=0 gs=-1 1024x4096 497.8 301.2 1.65x
gptq 4b sym=0 gs=128 1024x4096 473.6 498.9 0.95x
gptq 4b sym=1 gs=-1 128x512 58.4 4.3 13.68x
gptq 4b sym=1 gs=128 128x512 25.4 4.1 6.13x
gptq 4b sym=1 gs=-1 512x2048 87.2 75.6 1.15x
gptq 4b sym=1 gs=128 512x2048 79.8 84.8 0.94x
gptq 4b sym=1 gs=-1 1024x4096 580.3 303.5 1.91x
gptq 4b sym=1 gs=128 1024x4096 930.7 627.8 1.48x
gptq 2b sym=0 gs=-1 128x512 102.5 18.7 5.48x
gptq 2b sym=0 gs=128 128x512 136.9 20.8 6.57x
gptq 2b sym=0 gs=-1 512x2048 366.3 309.7 1.18x
gptq 2b sym=0 gs=128 512x2048 346.8 287.9 1.20x
gptq 2b sym=0 gs=-1 1024x4096 988.9 647.1 1.53x
gptq 2b sym=0 gs=128 1024x4096 1644.7 586.2 2.81x
gptq 8b sym=0 gs=-1 128x512 25.6 5.3 4.79x
gptq 8b sym=0 gs=128 128x512 30.6 5.5 5.56x
gptq 8b sym=0 gs=-1 512x2048 106.9 66.7 1.60x
gptq 8b sym=0 gs=128 512x2048 75.0 68.0 1.10x
gptq 8b sym=0 gs=-1 1024x4096 463.1 343.5 1.35x
gptq 8b sym=0 gs=128 1024x4096 462.0 275.7 1.68x
gptq act3d 32x32x4096 377.5 292.1 1.29x
awq clip sym=0 gs=128 256x2048 t512 1198.9 257.3 4.66x
awq clip sym=1 gs=-1 512x1024 t256 669.0 130.9 5.11x

Review Requirements

  • Reviewed this PR.
  • Checked that the code matches existing project structure, APIs, and conventions.
  • Avoided unnecessary monkeypatching and used the project's normal extension points where possible.
  • Minimized and compacted the impacted code surface.
  • Considered and eliminated potential regressions.

Kernel Accuracy Requirements

  • For kernel-related changes, accuracy drift was measured against an applicable independent Torch FP32/FP64 oracle, or the reason this is not applicable is documented.

Current independent CPU oracles check exact selected scale/zero/max_val
values and strided MSE objectives, including low-precision arithmetic.
No inference-kernel weight layouts changed; quantized persistent storage is
untouched. GPU numerical validation remains outstanding.

Notes

  • Env overrides are workspace targets, not a total memory ceiling; see README.
  • CUDA cases are now collected, but skipped locally because no GPU is available.
    A green static-analysis CI run does not substitute for GPU numerical tests.
  • QQQ integration tests require the missing local Llama checkpoint fixture.

Link to Devin session: https://app.devin.ai/sessions/2eb9e33e49b54734a60b2552d71b0aca
Open in Devin Desktop: https://app.devin.ai/desktop/session/2eb9e33e49b54734a60b2552d71b0aca?variant=devin
Requested by: @Qubitium

Batch the shrink-grid candidate evaluation in Quantizer.find_params (also
shared by qqq.Quantizer) and the AWQ _compute_best_clip grid so the
per-candidate kernel-launch storm collapses into a few large ops per
chunk. Candidate order, objective values, and first-minimum tie break are
identical to the sequential loops, so selected scale/zero/max_val are
bitwise-equal; verified 1728/1728 bitwise-equal across bit widths,
sym/asym, group sizes, trits, adjacent-zero search, QQQ, and clip paths.

Workspace per call is capped by GPTQMODEL_MSE_GRID_BUDGET_MB and
GPTQMODEL_CLIP_GRID_BUDGET_MB (default 256 MiB).
The 256 MiB default that amortizes kernel launches on GPU only evicts the
L3-resident working set on CPU, turning a 1.3-2x win into a ~0.7x
regression at mid shapes. CPU defaults to 16 MiB (GPU keeps 256 MiB);
both still honor the *_BUDGET_MB env overrides. Selection semantics are
chunk-size independent, so results remain bitwise-identical.
@devin-ai-integration

Copy link
Copy Markdown
Contributor

I'll fix CI failures and address comments from users with write access that start with 'Devin'.

  • Disable automatic comment, CI, and merge conflict monitoring

Comment thread tests/test_find_params_mse_grid.py Fixed
@Qubitium
Qubitium merged commit 3333ed5 into main Oct 24, 2026
6 checks passed
@Qubitium
Qubitium deleted the mse-clip-grid-vectorize branch October 24, 2026 12:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant