perf: back-pointer DP for AutoScheme bit allocation to cut path-copy RAM#1959
perf: back-pointer DP for AutoScheme bit allocation to cut path-copy RAM#1959SuperMarioYL wants to merge 2 commits into
Conversation
|
Thanks for the PR! Do you have an estimate of the RAM savings for an 8B model? |
|
I have tested the state number before, it's not too large as the state has been pruned |
|
Thanks @wenhuach21! You're right that once the state set is pruned the count stays modest — the saving here isn't in the number of states (that's what #1916's beam bounds), it's in the per-state path bookkeeping, which is independent of the model weights. The old code stored a full To put a number on it I measured the DP's path memory in isolation (synthetic
So:
The other half of the win is removing the These are synthetic DP-isolation numbers (no GPU/real model needed, since the path cost is weight-independent). Happy to run an end-to-end AutoScheme RSS measurement if you can point me at a repro config from #1913. |
|
/azp run Unit-Test-CUDA-AutoRound |
|
Azure Pipelines successfully started running 1 pipeline(s). |
choose_bits_per_layer_with_path stored the full chosen path in every DP state and rebuilt it with `cur_path + [(layer_names, scheme)]` on every transition. That copy is O(L) per surviving state per option, making the search O(L^2 * S) in both time and live path memory for L layers and S Pareto states. For models with many layers and incommensurate sizes the per-state path lists are a large part of the AutoScheme RAM footprint (see intel#1913, where intel#1916 brought peak RAM 90G -> 50G+ but "not enough"). Replace the per-state path list with an immutable back-pointer cons cell `(layer_names, scheme, prev_tail)`. Appending a layer is now an O(1) tuple that structurally shares the unchanged prefix with all sibling states, and the full path is reconstructed once at the end by walking the chain in O(L). The Pareto pruning and the `max_states` beam are unchanged; the returned (loss, path) is identical to before — verified bit-for-bit against the old implementation on 20k randomized instances across all `max_states` settings. Add test/test_cpu/schemes/test_choose_bits_per_layer.py (the function had no coverage): the DP optimum matches an exhaustive brute force, grouped layer names survive reconstruction, infeasible budgets return (None, None), and the beam cap still yields a feasible full-length path. Signed-off-by: supermario_leo <leo.stack@outlook.com>
7a1d22d to
1ccf58a
Compare
|
Rebased onto the current I re-verified the rebased implementation returns bit-for-bit identical |
|
We’ll merge this PR later since we’re under code freeze |
What
choose_bits_per_layer_with_path(the AutoScheme knapsack DP that picks theper-layer bit allocation) kept the full chosen path inside every DP state and
rebuilt it on every transition with:
That
cur_path + [...]copies the entire path (length up toL) once persurviving Pareto state per option, so the search is O(L² · S) in both time
and live path memory for
Llayers andSsurviving states. For models withmany layers and incommensurate sizes, the per-state path lists are a large part
of the AutoScheme RAM footprint.
This replaces the per-state path list with an immutable back-pointer cons
cell
(layer_names, scheme, prev_tail). Appending a layer is now an O(1)tuple allocation that structurally shares the unchanged prefix with all
sibling states, and the full path is reconstructed once at the end by walking
the chain in O(L). Pareto pruning and the
max_statesbeam are untouched.Why
#1913 (AutoScheme RAM usage is too large, still open) reports a multi-GB RSS
blow-up on this code path; #1916 brought peak RAM from ~90 GB to ~50 GB and the
maintainer noted that is "not enough". The path-copy is one of the remaining
sources of that footprint and was left unchanged by #1916, so this is a
complementary, low-risk reduction rather than a competing approach.
This change is intentionally scoped to the back-pointer rewrite only — it does
not change the
max_statesdefault or any accuracy-affecting behaviour.Correctness
The returned
(loss, path)is identical to the previous implementation:(1–9 layers, 1–4 options each, random bit/loss costs, and every
max_statessetting incl.
None/1/2/3/5) — 0 mismatches.test/test_cpu/schemes/test_choose_bits_per_layer.py(the function had nocoverage): DP optimum matches an exhaustive brute force; grouped layer-name
tuples survive reconstruction; infeasible budgets return
(None, None); thebeam cap still yields a feasible, full-length path.
Testing
black/isort(line-length 120) andruff(E4,E7,E9,F,NPY,FURB) are cleanon both files.