feat(mp): per-row mixed-precision wiring + Qwen3 PPL sweep - #6
Merged
Conversation
Wires scmp_kernels.mp.MPConfig (fixed-fraction quantile) into the SC core
so each token row in Linear and each query row in attention can use a
different stoc_len. MPConfig is loaded from an MP_CONFIG_JSON env var and
attached to model.config.sc_mp_config; both SCLinear.forward and
sc_eager_attention_forward dispatch per row when present, falling back to
the global sc_stoc_len when not.
- model/sc_common.py: per-row classification + per-level sc_matmul dispatch
in SCLinear; per-(B,H) loop in attention; weighted-mean effective stoc_len
tracker (mp_tracker_*).
- loader.py: apply_mp_config_from_env() reads MP_CONFIG_JSON and instantiates
MPConfig. AdaptiveMPConfig is recognised in scmp_kernels.mp but the JSON
loader rejects it for now (calibrated thresholds not wired yet).
- benchmark/ppl/ppl.py: invoke MP loader, reset the tracker per SC run, and
print avg_sl alongside the PPL row.
- benchmark/ppl/mp_{a,m,c}.json: three reference MP configs
(avg_sl ≈ 67 / 96 / 112).
- tests/run_mp_sweep.sh: single-script reproducer; defaults reproduce the
overnight 2026-05-26 result, with flags to narrow models/MP/ctx/tokens.
- benchmark/ppl/_mp_overnight*.sh + _env_common.sh: per-node driver,
5-node fan-out, and SUMMARY.txt aggregator that the reproducer chains to.
- MP.md: invocation guide + implementation notes + known limitations
(no AdaptiveMPConfig path; attention MP scales with B·H).
- kernels submodule: bump 59531ea -> a576b83 to pull in PR #16
(Owen-in-rescale) which the MP path relies on.
- model/smoothquant_apply.py + benchmark/ppl/calibrate_smoothquant.py:
add the previously-uncommitted SmoothQuant glue that ppl.py imports.
Headline result (halved sl_max=128 + SQ@0.5 + per_row attention, 65k tokens):
model | MP_c (avg=112) | MP_m (avg=96) | MP_a (avg=67) | FP16
4B | x1.097 | x1.204 | x2.842 | 11.27
8B | x1.105 | x1.167 | x1.690 | 11.10
14B | x0.999 | x1.036 | x1.291 | 9.85
30B-A3B | x1.096 | x1.161 | x1.614 | 8.02
32B | x1.014 | x1.042 | x1.228 | 8.71
MP_c (~12% cycle savings) is essentially lossless on >=14B models. MP_m
(~25% savings) is the sweet spot for big models. MP_a (~48% savings)
collapses 4B quality and should not be used.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR integrates per-row mixed-precision (MP) dispatch into the SC compute paths (Linear + eager attention), adds env-driven MP config loading, and introduces scripts/docs to reproduce and summarize a Qwen3 perplexity sweep (including SmoothQuant calibration/apply glue that benchmark/ppl/ppl.py relies on).
Changes:
- Wire
MPConfig-based per-row dispatch intoSCLinear.forwardandsc_eager_attention_forward, plus MP effective-stoc_lentracking for reporting. - Add SmoothQuant calibration + application utilities, and hook them into the PPL benchmark.
- Add reproducer/driver scripts, JSON configs, and documentation for the MP PPL sweep workflow.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
tests/run_mp_sweep.sh |
Single-node reproducer script for the 5-model × 3-MP PPL sweep (plus calibration and summary steps). |
MP.md |
Documents MP invocation, JSON schema, and implementation/limitations. |
model/smoothquant_apply.py |
Implements SmoothQuant activation-scale calibration and per-layer scale attachment to SCLinear. |
model/sc_common.py |
Adds MP import wiring, per-row dispatch in SCLinear and attention matmuls, and MP stoc-len tracking helpers. |
loader.py |
Adds env parsing for SC_HALVE_BIPOLAR_STOC_LEN and MP_CONFIG_JSON → model.config.sc_mp_config. |
benchmark/ppl/ppl.py |
Applies SmoothQuant/MP from env, resets MP tracker per run, and prints avg effective stoc_len for MP. |
benchmark/ppl/mp_a.json |
Reference MP config (more aggressive avg stoc_len). |
benchmark/ppl/mp_m.json |
Reference MP config (mid avg stoc_len). |
benchmark/ppl/mp_c.json |
Reference MP config (conservative avg stoc_len). |
benchmark/ppl/calibrate_smoothquant.py |
CLI script to generate per-model activation scales for SmoothQuant. |
benchmark/ppl/_mp_overnight.sh |
Per-node driver for running all MP configs serially for a single model. |
benchmark/ppl/_mp_overnight_summary.sh |
Aggregates per-(model, MP) logs into a summary table. |
benchmark/ppl/_mp_overnight_dispatch.sh |
Great Lakes multi-node dispatcher for fanning the sweep across nodes. |
benchmark/ppl/_env_common.sh |
Common env setup sourced by the overnight scripts. |
benchmark/ppl/_mp_overnight_20260526_040536/SUMMARY.txt |
Checked-in reference summary output from the overnight run. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+70
to
+73
| """Process-wide accumulator for effective per-row stoc_len. | ||
|
|
||
| Set ``model.config.sc_mp_track = True`` to record. Use | ||
| ``mp_tracker_snapshot()`` to read and reset between sweep runs. |
Comment on lines
+90
to
+103
| def _record_assignment(assignment) -> None: | ||
| """Accumulate weighted-sum of per-row stoc_len for reporting. | ||
|
|
||
| Each level's stoc_len is the *effective* cycle count seen by the kernel | ||
| (MP levels are already in the halved space when halve_bipolar_stoc_len | ||
| is on — they're just sliced from [0, 2**(sc_prec-1)]). | ||
| """ | ||
| state = _mp_tracker() | ||
| for sl, idxs in assignment.level_row_indices.items(): | ||
| n = int(idxs.numel()) | ||
| if n == 0: | ||
| continue | ||
| state["weighted_sl"] += float(sl) * n | ||
| state["rows"] += n |
| # Aggregate per-(model, MP) log lines into SUMMARY.txt. | ||
| # Run after all .done markers land — or anytime as a partial summary. | ||
| set -e | ||
| OUTDIR="${1:-$(cat /home/allenjin/Projects/scmp_llm/benchmark/ppl/_mp_overnight_latest.path 2>/dev/null | sed 's/^out=//')}" |
Comment on lines
+4
to
+19
| conda activate annstention | ||
|
|
||
| # Turbo for read-mostly HF cache (10x faster cold reads vs GPFS). | ||
| export HF_HOME=/nfs/turbo/coe-nbleier/allenjin/hf_cache | ||
| export TRANSFORMERS_CACHE=$HF_HOME | ||
| export HF_DATASETS_CACHE=/nfs/turbo/coe-nbleier/allenjin/hf_datasets | ||
|
|
||
| # Scratch for write-heavy temp (pip, builds, GPU compile cache). | ||
| mkdir -p /scratch/nbleier_owned_root/nbleier_owned1/shared_data/allenjin/tmp | ||
| export TMPDIR=/scratch/nbleier_owned_root/nbleier_owned1/shared_data/allenjin/tmp | ||
|
|
||
| # SC kernel knobs locked in for the MP sweep. | ||
| unset SC_DISABLE_OWEN # Owen-in-rescale scramble must be enabled. | ||
| export SC_SCRAMBLE_RESCALE=1 # PR #16 path: scramble in rescale, not RNG. | ||
|
|
||
| cd /home/allenjin/Projects/scmp_llm |
| unset SC_DISABLE_OWEN # Owen-in-rescale scramble must be enabled. | ||
| export SC_SCRAMBLE_RESCALE=1 # PR #16 path: scramble in rescale, not RNG. | ||
|
|
||
| cd /home/allenjin/Projects/scmp_llm |
Comment on lines
+39
to
+46
| launch_one gl1802 50893492 sweep_4b Qwen/Qwen3-4B-Instruct-2507 | ||
| launch_one gl1803 50710402 sweep_8b Qwen/Qwen3-8B | ||
| launch_one gl1806 50708292 sweep_30b Qwen/Qwen3-30B-A3B-Instruct-2507 | ||
| launch_one gl1806 50893493 sweep_14b Qwen/Qwen3-14B | ||
|
|
||
| # 32B waits for its SmoothQuant calibration to land act_scales on disk. | ||
| SQ32_PATH="$HERE/act_scales_Qwen_Qwen3-32B.pt" | ||
| launch_one gl1809 50712265 sweep_32b Qwen/Qwen3-32B "$SQ32_PATH" |
Comment on lines
+137
to
+139
| mkdir -p /scratch/nbleier_owned_root/nbleier_owned1/shared_data/allenjin/tmp \ | ||
| || true | ||
| export TMPDIR=/scratch/nbleier_owned_root/nbleier_owned1/shared_data/allenjin/tmp |
Comment on lines
+23
to
+41
| base=$(basename "$log" .log) | ||
| model="${base%_mp_*}" | ||
| mp="${base##*_}" | ||
| fp16=$(grep -E "^FP16 baseline " "$log" | awk '{print $3}' | tail -1) | ||
| line=$(grep -E "^SC MP " "$log" | tail -1) | ||
| if [[ -n "$line" ]]; then | ||
| ppl=$(echo "$line" | awk '{ | ||
| for (i=1;i<=NF;i++) if ($i ~ /^[0-9]+(\.[0-9]+)?$/) {print $i; exit} | ||
| }') | ||
| ratio=$(echo "$line" | grep -oE '×[0-9.]+ vs fp16' | tr -d '×' | awk '{print $1}') | ||
| avg=$(echo "$line" | grep -oE 'avg_sl=[0-9.]+' | sed 's/avg_sl=//') | ||
| printf "%-44s %-8s %10s %10s %10s %8s %s\n" \ | ||
| "$model" "$mp" "${fp16:--}" "${ppl:--}" "${ratio:--}" "${avg:--}" "$(basename "$log")" | ||
| elif grep -qE "FAIL|Traceback" "$log"; then | ||
| printf "%-44s %-8s %10s %10s %10s %8s %s\n" \ | ||
| "$model" "$mp" "${fp16:--}" "FAIL" "-" "-" "$(basename "$log")" | ||
| else | ||
| printf "%-44s %-8s %10s %10s %10s %8s %s\n" \ | ||
| "$model" "$mp" "${fp16:--}" "RUNNING" "-" "-" "$(basename "$log")" |
Collaborator
|
LGTM |
Allenjin123
added a commit
that referenced
this pull request
Jun 3, 2026
Bring the branch up to date with main (PR #6 squash, docs/readme #8, bipolar-halving + offload-safe SC replacement d134e4a). Conflict resolution (all verified by line-level diff, not assumption): - model/sc_common.py, loader.py, benchmark/ppl/ppl.py: kept this branch's versions — each is a verified superset of main (adds AdaptiveMPConfig / calibrated-table + STE + cross-layer support on top of main's MPConfig). - tests/run_mp_sweep.sh: kept this branch's 531-line cross-layer rewrite (0d035b3); main's 216-line copy is the older same-lineage PR-#6 script, no independent feature lost. - kernels submodule: fc8d756 -> 41c16be (drop-formula-path branch: the matmul.py import hotfix + AdaptiveMPConfig formula-path removal). - Re-dropped benchmark/ppl/_mp_overnight_20260526_040536/SUMMARY.txt that the merge re-introduced (honors 5870a65). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
scmp_kernels.mp.MPConfig(fixed-fraction quantile) intoSCLinear.forwardandsc_eager_attention_forward. Per-row dispatch on token rows (Linear) and per-(B, H) query rows (attention), with the globalsc_stoc_lenas fallback when no MP config is attached. Effective avgstoc_lenis tracked and printed alongside PPL.apply_mp_config_from_env(MP_CONFIG_JSON=...) and three reference configsmp_{a,m,c}.json(avg_sl ≈ 67 / 96 / 112).tests/run_mp_sweep.shis a single reproducer: defaults regenerate the overnight 2026-05-26 5-model × 3-MP sweep; flags narrow models / MP / context / token cap / SQ / halve / etc. Header has Great Lakes salloc + tmux instructions.benchmark/ppl/_mp_overnight*.sh+_env_common.sh: per-node driver, 5-node fan-out, and SUMMARY.txt aggregator that the reproducer chains to.kernelssubmodule toa576b83(PR #16 Owen-in-rescale) which the MP path relies on.model/smoothquant_apply.py,benchmark/ppl/calibrate_smoothquant.py) thatppl.pyalready imports — without these, the existingppl.pyonmaincannot run.MP.mddocuments invocation, JSON schema, implementation notes, and the two known limitations (AdaptiveMPConfig calibrated-thresholds path not wired; attention MP scales with B·H kernel launches).Headline result
Halved (
sl_max=128) + SQ@0.5 + per_row attention, 65 k tokens,sc_prec=8:MP_c (~12% cycle savings) is essentially lossless on ≥14B models; 14B at ×0.999, 32B at ×1.014. MP_m (~25% savings) is the sweet spot on big models. MP_a (~48% savings) collapses 4B quality.
Raw numbers:
benchmark/ppl/_mp_overnight_20260526_040536/SUMMARY.txt.Excluded from this PR
act_scales_Qwen_Qwen3-*.pt(≈150 MB total) — calibration artifacts, regenerable viacalibrate_smoothquant.py._alpha_sweep.sh,_moe_legacy.sh,_halved_fix_4b.sh,_overnight_*.sh) and their run-dirs — pre-existing, unrelated to MP.Test plan
MP_CONFIG_JSONround-trip:python -c "from loader import apply_mp_config_from_env; ..."loadsmp_m.jsoncleanly.tests/run_mp_sweep.sh --models 4B --mp mp_m --max-tokens 4096finishes < 5 min with finite PPL.MP_CONFIG_JSONunset → falls back to fixedsc_stoc_len=128(no per-row dispatch).model/smoothquant_apply.py/calibrate_smoothquant.pywere genuinely orphan-untracked before this PR (git log --all --oneline -- model/smoothquant_apply.pyreturns empty onmain).kernelssubmodule SHA matches the PR #16 merge on the kernel repo.🤖 Generated with Claude Code