Repository navigation
fix(predict): size predict chunks with a measured predict cost model (#47) - #54
Merged
Merged
Conversation
…47) Predict chunks were sized with the fit memory model, which grows with the training table. Past ~3,500 x 500 no chunk "fit", chunking fell to one row, and a 100-row predict became 100 sequential model calls: 14.4 s (TabICLv2) and 6.3 s (TabPFN-3.5) instead of ~0.1 s on an A100; ~24 s and ~11 s on prod's A40s. PredictCostModel (memory/predict.py) bounds a kv-context predict's peak as a fixed cost per call and a cost per test row, both by width, plus a cost per training row, built from predict peaks measured under the serving configuration (benchmarks/calibrate_predict.py; generated memory/calibration_predict_a100.py). It bounds every measurement and scales in proportion to width past the widest, so it can't underestimate there. choose_chunk_rows no longer splits to single rows when nothing fits: that is the call's fixed cost, which splitting can't reduce; a real OOM still halves. Also: bench_stalls.py, profile_predict.py, sweep_gateway.py, and devbox.sh `down` that works on a cluster whose creation failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #54 +/- ##
==========================================
+ Coverage 78.72% 79.51% +0.78%
==========================================
Files 39 41 +2
Lines 3098 3193 +95
==========================================
+ Hits 2439 2539 +100
+ Misses 659 654 -5
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
… cost - choose_chunk_rows: when a call's fixed cost alone exceeds the headroom, size the rows above it against the budget instead of returning a large fallback chunk (that could add ~18 GB beside a fit at its peak). No headroom means the smallest chunk: slow, but it adds nothing. min_chunk_rows must be >= 1. MAX_CHUNK_ROWS = 2,048 = LIGHT_PREDICT_ROWS. - PredictCostModel: steepest slope between consecutive measured sizes, an explicit running maximum, and a docstring that separates what is guaranteed (bounds every measurement) from what is assumed (at most linear growth beyond them). - Factory: the A100 calibration applies only to the configuration it was measured under (kv, int8, compiled TabICL; TabPFN); others get no model. - gen_predict_grid.py refuses inputs from different devices or settings. - Tests: the regression test now fails without the model (6/6 verified); ragged calibrations (one test size at a width, negative slopes, narrower than measured, convex curves); chunk sizing under a fixed cost over budget and under no headroom. Co-Authored-By: Claude Opus 5.5 <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.
Part of #47 (the predict stall; wide-fit compiles in-request are next).
Problem
On prod (4× A40), predicts against wide contexts took a flat ~24 s (
tabicl-v2) and ~11 s (tabpfn-3.5) from 3,500 × 500 up, whatever the table size. Reproduced on an A100 (14.4 s / 6.3 s).cProfileof one predict (benchmarks/profile_predict.py) showedbackend.predictrunning 100 times for one 100-row request:choose_chunk_rowssized chunks withestimate_bytes(n_train, chunk, n_features), which for kv contexts outside the old predict grid fell back to the fit formula. That grows with the training table, so past a size no chunk "fit" and chunking fell to one row.Fix
memory/predict.py,PredictCostModel. Measured (benchmarks/calibrate_predict.py, A100, serving config: compiled TabICL, int8 kv), a kv-context predict's peak follows its test rows and their width, plus a small cost per training row (int8 KV dequantized per layer). The model is built from measured marginal costs, not a fitted surface (fitted surfaces underestimated held-out widths by up to a third):Both width curves are non-decreasing; past the widest measurement they scale in proportion to width. Guaranteed: the bound is at or above every measurement it was built from. Assumed: at most linear growth beyond them (steepest slope between consecutive sizes; affine in width). Over the 68 measurements: TabICL median ×1.16 overestimate, TabPFN ×1.09; held-out widths ≥0.98× before the 1.1 margin.
AdaptiveMemoryEstimator(predict_cost=…)uses it for kv predicts.reprandoffmodes, where predicts re-encode the training table, keep the dominance grid. The factory installs it per model from the generatedmemory/calibration_predict_a100.py.choose_chunk_rows, when a call's fixed cost alone exceeds the headroom, sizes only the rows above it against the budget instead of splitting to single rows, which only multiplied that fixed cost. With no headroom (a full device, say beside a fit at its peak) it still uses the smallest chunks, so it adds nothing.MAX_CHUNK_ROWS=LIGHT_PREDICT_ROWS= 2,048, so a light predict is one call.Configuration-keyed: the A100 calibration applies only to the configuration it was measured under (kv, int8, compiled TabICL; TabPFN). Other configurations get no model.
Verified
tests/unit/test_predict_cost.py:repr;benchmarks/bench_stalls.py, engine built as serving builds it, 3 predicts per shape):Every predict is ≤0.24 s and compiles nothing.
Review
An independent review agent found that the first version's fallback could add an ~18 GB chunk beside a running fit, and that the regression test couldn't fail. Both are fixed in the second commit, along with its should-fixes: configuration keying, docstring accuracy, consecutive slopes, ragged-calibration tests, and the generator rejecting mixed inputs. GPU results were re-run after the fixes, and are unchanged.
Not in this PR
Also adds
benchmarks/sweep_gateway.py, and fixesdevbox.sh downfor a cluster whose creation failed.🤖 Generated with Claude Code