Repository navigation
fix(sweep): 🐛 split a batch that runs out of GPU memory - #60
Merged
Merged
Conversation
Contributor
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Result concatenation can reintroduce OOM, and removed Ruff suppressions will fail linting.
3 open findings
What changed in this PR
Improves sweep resilience to fragmented GPU memory and records Leonardo multi-GPU results.
Changes:
- Retries failed kernels using smaller batches.
- Adds QR-based accuracy measurement and Leonardo benchmark evidence.
- Documents memory handling and removes obsolete plans.
| File | Description |
|---|---|
src/src_method/_sweep.py |
Adds adaptive batch splitting. |
src/src_method/utils/_backend.py |
Adds pool-cache release helper. |
src/src_method/utils/__init__.py |
Exports the helper. |
tests/test_stack.py |
Tests split retries and failures. |
benches/large/bench_large.py |
Adds QR distance reporting. |
benches/large/README.md |
Documents Leonardo results. |
benches/large/leonardo/run_multi.sh |
Adds four-GPU job runner. |
benches/large/leonardo/logs/scaling_m1000.jsonl |
Records warm scaling results. |
benches/large/leonardo/logs/scaling_m1000_cold.jsonl |
Records cold scaling results. |
benches/large/leonardo/logs/retry_tests_1gpu_60137742.out |
Records retry test run. |
benches/large/leonardo/logs/retry_4gpu_60137752.out |
Records reference four-GPU run. |
benches/large/leonardo/logs/multi_E_budget_59783330.out |
Records budget comparisons. |
benches/large/leonardo/logs/multi_D_m4000_1gpu_60143214.out |
Records reference single-GPU run. |
benches/large/leonardo/logs/multi_B_m1000_59782618.out |
Records warm scaling run. |
benches/large/leonardo/logs/multi_B_m1000_59776459.out |
Records cold scaling run. |
benches/large/leonardo/logs/multi_A_tests_59776456.out |
Records GPU test results. |
docs/content/docs/features/large-problems.mdx |
Documents OOM splitting. |
docs/superpowers/specs/2026-09-25-src-multi-gpu-design.md |
Removes obsolete design. |
docs/superpowers/plans/2026-09-25-src-multi-gpu.md |
Removes obsolete plan. |
.gitignore |
Preserves benchmark logs. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
4 tasks done
Phase 2 of the multi-GPU plan: one process per GPU, NCCL collectives, the sketch index split among the GPUs, with a communicator passed in Resources. Targets strong scaling at the Phase 1 reference size. Assisted-by: Pi:claude-opus-5-5
An abort method on the communicator, the truncation rank agreed through a callback, output sinks, a logged warning for large in-memory inputs, and MPICH in CI and in the dev shell. Assisted-by: Pi:claude-opus-5-5
Eleven tasks, each test-first, from the communicator to the scaling runs. Written under the no-full-code rule of the design phase: interfaces, pseudocode and test specifications. Assisted-by: Pi:claude-opus-5-5
Phase 1 is merged, logging is the standard library, the docs moved to the Fumadocs site, and the CUDA 13 CuPy needs the cu13 NCCL wheel. Assisted-by: Pi:claude-opus-5.5
Assisted-by: Pi:claude-opus-5.5
Allgather, gather and scatter over peer copies, ordered by events, in a single process. A failing rank aborts the group so that no rank waits forever; on the CPU the devices are simulated for testing. Assisted-by: Pi:claude-opus-5.5
Resources(devices=...) gives each device a contiguous block of the sketch columns. The left-to-right pass needs no communication; per site of the right-to-left pass the sketch is gathered on the first device for the QR, the output core scattered by rows, and the projected environment gathered on every device. Cores are read once and, with peer access, uploaded in slices and gathered over the peer links. One device keeps the previous sweep, bit for bit. Assisted-by: Pi:claude-opus-5.5
Assisted-by: Pi:claude-opus-5.5
Assisted-by: Pi:claude-opus-5.5
- Add `leonardo/run_multi.sh`: any command with 4 GPUs in one process, printing the topology and peer-access matrix; `WARM_DIR` reads the stack beforehand. - `scaling` also reports a QR distance over `a - b`, which resolves differences below the sqrt(eps) floor of the inner-product distance. - GPU tests pass (19/19); D_M = 1000 scaling with QR distances below 1e-12. The reference size is queued; its results follow. - Keep `benches/*/leonardo/logs/*.out` in git. Assisted-by: GitHub Copilot:Claude Opus 5.5
The stall timer ends before the upload to the device, so it does not include the split upload or the NVLink gather; it also counts the loader waiting for the GPU. Assisted-by: GitHub Copilot:Claude Opus 5.5
At D_M = 4000, chi_out = 2000 on an A100-64GB, CuPy's pool fragments beyond the margin the plan leaves for it: live memory peaked at 52 GB against the planned 56.7 GB, but the pool held up to 66.7 GB and a 12.6-14.7 GB allocation failed, at a different point in each run. On out of memory, a kernel now releases the pool's cached blocks and computes the batch in halves, joined along the batch axis, keeping the split for the rest of the sweep. The plan and the batches the store reads and writes are unchanged, so the devices need not agree on it. Assisted-by: GitHub Copilot:Claude Opus 5.5
At D_M = 4000, chi_out = 2000: 3925.3 s on one A100-64GB, 1099.9 s on four, a speed-up of 3.57 and an efficiency of 89%. Reading environments back from host memory and disk takes 82-84% of the right-to-left pass. The docs describe the out-of-memory split. The 2-GPU point and the distance at this size are queued. Assisted-by: GitHub Copilot:Claude Opus 5.5
Phase 1 of the plan (#44's own description) called these dead weight. Assisted-by: GitHub Copilot:Claude Opus 5.5
A single-GPU sweep reused any CuPy core in place, so with Resources(devices=[d]) a core on another GPU met environments and sketches on GPU d. Reuse a core only when it sits on the sweep's GPU; stage the others through the host buffer as a group does. Addresses the Copilot review on #44. Assisted-by: GitHub Copilot:Claude Opus 5.5
Panadestein
force-pushed
the
chore/multi-gpu-leonardo
branch
from
October 13, 2026 12:29
0392e7e to
fb1b213
Compare
# Conflicts: # benches/large/README.md
# Conflicts: # benches/large/README.md
# Conflicts: # benches/large/README.md # benches/large/bench_large.py # src/src_method/_sites.py # src/src_method/_sweep.py # tests/test_gpu_backend.py # tests/test_stack.py
robertodr
reviewed
Oct 14, 2026
Add the 2-GPU run and a second 4-GPU run (scaling --devices 4 2) at D_M = 4000, chi_out = 2000: 2348.8 s on two GPUs (84% against one GPU), 1290.4 s on four, QR distance 7.5e-13 between them. Correct what the environment stalls measure: they cover both passes and include waiting for the GPU. Assisted-by: GitHub Copilot:Claude Opus 5.5
robertodr
reviewed
Oct 14, 2026
Comment on lines
+72
to
+76
| """The site kernels, computed in smaller pieces after running out of memory. | ||
|
|
||
| A batch that fails is split along its batch axis and its pieces joined, so | ||
| the batches read from and written to the store stay those of the plan. The | ||
| split of a kernel lasts for the rest of the sweep. One instance per device. |
Member
There was a problem hiding this comment.
can you rephrase the prose a bit? More human readable and less the usual, convoluted Claudio-style...
robertodr
previously approved these changes
Oct 14, 2026
robertodr
left a comment
Member
There was a problem hiding this comment.
this is really exciting stuff! Are the leonardo logs to be kept in the repo?
Assisted-by: GitHub Copilot:Claude Opus 5.5
Remove the benches/*/leonardo folders: the Slurm scripts and the run logs, which stay in the git history. bench_mpo_mpo.py moves up to benches/primitives/. The bench READMEs keep the result summaries, without job IDs. Add an "HPC clusters" section to the docs with a Leonardo page: the CUDA 13 forward-compat setup, an example Slurm job script for one or four GPUs, queues and performance notes. Assisted-by: GitHub Copilot:Claude Opus 5.5
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.


🤖 AI text below 🤖
Runs the multi-GPU sweep of #44 on real GPUs for the first time (one Leonardo Booster node: 4x A100-SXM-64GB, all pairs linked by NVLink) and fixes the out-of-memory error that stopped the reference size.
Results
GPU tests:
tests/test_gpu_backend.py, 20/20 pass, including the multi-GPU tests. Peer access is on for every pair of GPUs, so the split-upload path is the one exercised. The fast suite passes on a GPU node too.Reference size (D_M = 4000, chi_out = 2000, 50 sites, complex128, 184 GB of input), against the single-GPU run:
main.D_M = 1000, chi_out = 500:
Full tables and analysis are in the Leonardo section of
benches/large/README.md.Changes
fix(sweep): split a batch that runs out of GPU memory.fix(sites): stage cores held on another GPU. A single-GPU sweep onResources(devices=[d])reused a core held on another GPU as is. It now stages it through the host, as a group does. A new GPU test covers it. (Copilot review on feat(sweep): ✨ split the SRC sweep among the GPUs of a node #44.)scalingalso reports a QR-based distance over the differencea - b. The inner-product distance cancels down to aboutsqrt(eps)(~1.5e-8), so it cannot show1e-10; the QR distance can.benches/*/leonardo/folders (Slurm scripts and run logs) are removed; they remain in the git history.bench_mpo_mpo.pymoves up tobenches/primitives/, and the READMEs keep only the result summaries.large-problems.mdxdescribes the out-of-memory split.docs/superpowers/planning docs, which feat(sweep): ✨ split the SRC sweep among the GPUs of a node #44's own description already called dead weight.Findings for follow-up