Uh oh!
There was an error while loading. Please reload this page.
ci(cuda): gate nvcc peak memory on a budget, and record why -j2 is not headroom - #95
Merged
Conversation
…t headroom Two follow-ups to the measurement added earlier, both prompted by reading the numbers rather than just printing them. 1. The readout is now a gate. build-cuda fails if any module's peak RSS exceeds FF_MEM_BUDGET_KB (14 GiB), against a measured maximum of 12.98 GB for reg_flow on a 16 GB runner. Measuring and only logging would have repeated the exact mistake this branch exists to fix: a number nobody reads is not a gate, in the same way a link without --no-undefined is not a check. The band is ~1 GB and the heavy modules reproduce to better than 0.1% across runs, so it will not flap; if it fires, the answer is to find out what got heavier, not to raise the number. 2. The -j2 rationale is corrected, not just the per-module figure. Feeding the measured wall times through make's -j2 dispatch order shows the heavy modules do not take turns: reg_field + reg_field_rls overlap ~760 s peaks sum to 15.59 GB reg_field + reg_flow overlap ~50 s peaks sum to 21.90 GB reg_flow + reg_flow_rls overlap ~150 s peaks sum to 14.88 GB The second is over a 16 GB runner outright; the first clears it by ~0.4 GB. The build is green because each module's peak is a brief spike late in its own ptxas phase and two spikes have not yet coincided -- not because two regularisers fit. Nothing enforces that, and anything shifting relative phase (reordering MODULES, a different nvcc, ccache, a bound/spline policy change, adding -arch targets) reshuffles it. That is now stated in src/lib-cuda/Makefile so the next person reads it before raising -j. The per-module budget cannot catch two peaks coinciding; the comment says so rather than implying the gate covers it. MODULES, -O1 and -j2 are unchanged. The split is right; it was the recorded rationale that was wrong.
Uh oh!
There was an error while loading. Please reload this page.
balbasty pushed a commit
that referenced
this pull request
Aug 20, 2026
Brings in #87, #90, #91 and #95. One conflict, in include/fastfields/impl/kernels/parallel.h: #91 renamed FF_NAMESPACE_BEGIN(FF) to FF_NAMESPACE_BEGIN(FF_NS) on the line this branch inserts the FF_GRAIN_SIZE block above. Resolved by keeping both -- the new block, then main's FF_NS spelling. Everything else auto-merged. #91's renames do not touch anything this branch depends on: has_atomic_add / anyAtomicAdd keep their names, FF_NS still expands to ff, and the CUDEV -> FF_CUDEV rename is confined to the CUDA half of atomic.h. FF_GRAIN_SIZE, the one macro this branch adds to an installed header, already satisfies #91's FF_-prefix rule -- `tools/rename-macros.py --check` reports "0 file(s) would change" and "include/ is clean". Re-verified on the merge result: * tools/test-baseline.sh --legs default,lib -> byte-identical to tools/test-baseline.expected. 13 suites, 59,886 checks, 0 failures. * -DFF_GRAIN_SIZE=1 -> 59,886 / 13 / 0. * -DFF_GRAIN_SIZE=1 + TSan, FF_NUM_THREADS=4, halt_on_error=1 -> 59,886 / 13 / 0, zero reports. * clone syscalls: 0 across all 13 binaries at the shipping grain size, 2 per binary at FF_GRAIN_SIZE=1. The threshold is unchanged by the merge (0 clones at n=32768, 2 at n=32769, on main and on this branch alike). * The thread-pool defects still reproduce on main at f63c7d8: the data race is deterministic under TSan (threadpool.h:148 write / :164 read) and the lost-wakeup deadlock is stochastic (8/320 trials over FF_NUM_THREADS 8/16/32/64). Both are gone on this branch: 0/320 hangs, 0 TSan reports.
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 freeto 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.
Follow-up to #87 (which merged at 23:43:58 while this was being written, so it could not go in there). Answers the two questions raised on that PR: is
-j2safe or luck, and does theFFMEMreadout survive usefully or does it need a threshold.MODULES,-O1and-j2are unchanged. The split is right; it was the recorded rationale that was wrong, and #87 fixed the number. This fixes the reasoning and makes the measurement a gate.1.
-j2is luck, not headroom#87 established the per-module peaks. Feeding the measured wall times through
make's
-j2dispatch order (which isMODULESorder) shows the heavy modulesdo not take turns — they overlap:
reg_field+reg_field_rlsreg_field+reg_flowreg_flow+reg_flow_rlsOn a 16 GB runner the second is over the limit outright and the first clears it
by ~0.4 GB (2.6%). The build is green anyway because a module's peak RSS is a
brief spike late in its own
ptxasphase, and so far two spikes have not landedin the same instant.
Nothing enforces that. Anything that shifts the relative phase of two heavy
compiles — reordering
MODULES, a different nvcc, ccache, a bound/spline policychange, adding
-arch/-gencodetargets — reshuffles it, and the failure modeis an OOM kill that reads as a random infrastructure flake rather than a build
decision. So: my answer to "is it safe?" is no, it is luck, and that is now
written down next to
MODULESrather than left as an inference.Model check: the simulated schedule totals 35.1 min against an observed compile
step of 33.2 min (23:07:15→23:40:29 on run 32311771480), i.e. the real schedule
packs slightly tighter than the model — so if anything there is more overlap,
not less.
2. The readout is now a gate, not a log line
build-cudafails if any module's peak RSS exceedsFF_MEM_BUDGET_KB(14 GiB),against a measured maximum of 12.98 GB.
Measuring and only logging would repeat the exact mistake #87 exists to fix: a
number nobody reads is not a gate, in the same way a link without
-Wl,--no-undefinedis not a check. The band is ~1 GB; the heavy modulesreproduce to better than 0.1% across the two runs measured, so it will not flap.
If it fires, the intended response is to find out what got heavier — the error
message says so, and points at the measured table.
Logic verified locally against the real
FFMEMdata before pushing: passes at14 GiB with the measured table, fails and names the offender when a module
exceeds, and exits 0 gracefully when there is no table at all (a compile that
died before any nvcc finished — an apt stall — is the compile step's failure to
report, not this one's).
What it deliberately does not claim: a per-module budget cannot catch two
modules peaking at once. That risk is scheduling, and the comment says so rather
than implying the gate covers it.
On multi-arch (
-gencode), since it was askedNot touched here — another agent owns it — but the measurement bears on whether
that matrix is affordable, so, flagged rather than assumed:
-arch/-gencodeat all, so these numbersare for one default architecture.
--threads 1), so I wouldexpect additional targets to multiply wall time roughly linearly while
leaving peak RSS near its current level — the peak is one
ptxason onearch. On that expectation
reg_flowstays ~13 GB but its 1097 s becomes~2200 s (2 arch) or ~3300 s (3 arch), and the ~33 min compile step becomes
~65–100 min against a 120 min job timeout. Time is the binding constraint,
not memory — but only while nvcc stays single-threaded per arch;
--threadsor a parallel
-gencodepath would multiply memory instead andreg_flowhasno room for that.
my container and no multi-arch build has been run. Worth one probe build
before committing to a cu11/cu12/cu13 matrix.
Validation
CI on this branch. Note the new gate only exercises on a CUDA-triggering change,
which this is (
src/lib-cuda/Makefile,.github/workflows/).As on #87: a green
build-cudameans compile + link only. There is no GPU inCI and nothing here claims anything about GPU runtime behaviour.
Generated by Claude Code