Repository navigation
feat(sweep): ✨ split the SRC sweep among the GPUs of a node - #44
Merged
Merged
Conversation
robertodr
added this pull request to stack #43
September 27, 2026 11:36
robertodr
force-pushed
the
feat/src-multi-gpu
branch
from
September 30, 2026 19:55
fb7dca8 to
e173b01
Compare
Panadestein
force-pushed
the
feat/src-multi-gpu
branch
2 times, most recently
from
October 5, 2026 15:13
44cb31e to
a5ec1e2
Compare
Panadestein
force-pushed
the
feat/src-multi-gpu
branch
from
October 8, 2026 14:03
a5ec1e2 to
03d26a6
Compare
Panadestein
marked this pull request as ready for review
October 8, 2026 14:48
Contributor
Test Results 5 files ± 0 5 suites ±0 3m 56s ⏱️ +52s Results for commit 37f3324. ± Comparison against base commit bcc0fb5. This pull request removes 2 and adds 58 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
Panadestein
added a commit
that referenced
this pull request
Oct 13, 2026
Phase 1 of the plan (#44's own description) called these dead weight. Assisted-by: GitHub Copilot:Claude Opus 5.5
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Cross-device CuPy inputs can fail on a selected single GPU, and obsolete or unsupported documentation claims remain.
4 open findings
What changed in this PR
🤖 AI text below 🤖
Adds single-node multi-GPU SRC sweeps using one thread per device.
Changes:
- Splits sketches, environments, and projections across devices.
- Adds peer-copy collectives, shared site loading, and per-device planning.
- Adds CPU/GPU tests, documentation, and scaling benchmarks.
| File | Description |
|---|---|
src/src_method/_group.py |
Implements device groups and collectives. |
src/src_method/_sweep.py |
Distributes the SRC sweep. |
src/src_method/_sites.py |
Shares staged cores across devices. |
src/src_method/_plan.py |
Plans multi-device memory usage. |
src/src_method/utils/_backend.py |
Adds device and peer-copy helpers. |
src/src_method/utils/__init__.py |
Exports backend helpers. |
tests/test_group.py |
Tests collectives and failures. |
tests/test_stack.py |
Tests distributed sweep behavior. |
tests/test_sites.py |
Tests shared site loading. |
tests/test_plan.py |
Tests multi-device planning. |
tests/test_gpu_backend.py |
Adds multi-GPU tests. |
tests/test_backend.py |
Tests host backend helpers. |
benches/large/bench_large.py |
Adds scaling benchmarks. |
benches/large/README.md |
Documents benchmark usage. |
README.md |
Introduces multi-GPU usage. |
docs/content/docs/features/large-problems.mdx |
Documents multi-GPU execution. |
docs/content/docs/features/gpu.mdx |
Links GPU guidance. |
docs/content/docs/contributing/testing.mdx |
Documents multi-device testing. |
docs/superpowers/specs/2026-09-25-src-multi-gpu-design.md |
Adds the obsolete MPI design. |
docs/superpowers/plans/2026-09-25-src-multi-gpu.md |
Adds the obsolete MPI plan. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Panadestein
added a commit
that referenced
this pull request
Oct 13, 2026
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
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
Panadestein
added a commit
that referenced
this pull request
Oct 13, 2026
Phase 1 of the plan (#44's own description) called these dead weight. Assisted-by: GitHub Copilot:Claude Opus 5.5
Panadestein
added a commit
that referenced
this pull request
Oct 13, 2026
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
feat/src-multi-gpu
branch
from
October 13, 2026 12:29
7e7bdb9 to
3d5dde5
Compare
# Conflicts: # benches/large/README.md
robertodr
commented
Oct 14, 2026
robertodr
commented
Oct 14, 2026
Panadestein
added a commit
that referenced
this pull request
Oct 14, 2026
* docs(spec): 📝 design for an SRC sweep over the GPUs of a node 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 * docs(spec): 📝 fold the refinements found while planning into the spec 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 * docs(plan): 📝 implementation plan for the multi-GPU SRC sweep 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 * docs(plan): 📝 align the multi-GPU spec and plan with main 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 * feat(utils): ✨ device selection and peer copies between GPUs Assisted-by: Pi:claude-opus-5.5 * feat(group): ✨ one thread per device and the collectives between them 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 * feat(sweep): ✨ split the sweep among the GPUs of a node 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 * perf(bench): 📈 multi-GPU scaling command for the large benchmark Assisted-by: Pi:claude-opus-5.5 * docs: 📝 running one sweep on several GPUs Assisted-by: Pi:claude-opus-5.5 * perf(bench): 📈 multi-GPU tests and scaling on Leonardo (4xA100-64GB) - 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 * docs(bench): 📝 correct what the site stalls measure 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 * fix(sweep): 🐛 split a batch that runs out of GPU memory 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 * perf(bench): 📈 reference size on 1 and 4 GPUs of Leonardo 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 * chore: 🔥 drop the superpowers planning docs Phase 1 of the plan (#44's own description) called these dead weight. Assisted-by: GitHub Copilot:Claude Opus 5.5 * fix(sites): 🐛 stage cores held on another GPU 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 * perf(bench): 📈 2-GPU point at the reference size 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 * docs(bench): 📝 keep the best 4-GPU point at the reference size Assisted-by: GitHub Copilot:Claude Opus 5.5 * docs(hpc): 📝 HPC clusters section; drop the Leonardo logs and scripts 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 --------- Co-authored-by: Roberto Di Remigio Eikås <roberto@totaltrash.xyz>
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 🤖
Splits one SRC sweep among the GPUs of a single node, so that large stacks (the out-of-core reference problem of #42) finish close to
Gtimes faster onGGPUs. The mathematics, the random draws and the result do not change. Multi-node is out of scope for now.Design
One process, one thread per GPU. The earlier MPI/SPMD design (still in
docs/superpowers/, to be dropped) is replaced: nothing in it paid off on a single node. The call works as before: it returns the cores, raises errors normally, needs nompirun, and reads in-memory inputs once.chi_outsketch columns and builds only those columns of every environment, so the environments (the dominant memory) are split too.S, which is then gathered on every GPU._group.py) are allgather, gather and scatter, built from peer copies (memcpyPeerAsync) and ordered with the kernels by CUDA events. If one GPU fails, the group is aborted, the other GPUs are woken and the original error is raised, so nothing hangs.gpu_memoryis per GPU (detected: the smallest free memory); host memory and scratch disk are shared.main, including withcutoffand spilling.devices=nsimulates the GPUs with threads that share the host, through the same code. This lets the suite test the distributed logic without GPUs.What is not split (sets the efficiency)
Sand the output core. The largest D_M is therefore that of one GPU, about 10⁴ atchi_out = 2000on an H200.Contractions scale as χ²D², these costs at most as D², so large problems should scale close to linearly; small ones will not.
Changes
utils/_backend.py_group.py(new)Resources.devices_plan.py_sites.py_sweep.pyscalingcommand (--devices 1 2 4 8),--devicesonrun,--device cpufor smoke testsbenches/large/docs/content/docs/,README.mdTesting
chi_outnot a multiple of the device count or smaller than it, a custom device order,cutoff, spilling to disk with cleanup, and an error on one device;Status
benches/large/README.mddocs/superpowers/Left for later, if measurements call for them: a distributed CholeskyQR2 instead of the QR on GPU 0, splitting the bond of
Mfor D_M above about 10⁴, and NUMA-local host buffers on GH200 nodes.