GPU offload for Nassau's Milnor multiply (p = 2) - #271
Open
JoeyBF wants to merge 4 commits into
Open
Conversation
|
Warning Review limit reachedNext included review available in 19 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Team Run ID: 📒 Files selected for processing (15)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
JoeyBF
force-pushed
the
claude/nassau-gpu-refactor-k1ggps
branch
from
July 15, 2026 05:14
7824e7d to
31aced0
Compare
JoeyBF
added a commit
to JoeyBF/sseq
that referenced
this pull request
Jul 18, 2026
…Sequences#272's restricted graph PR SpectralSequences#272's restricted_partial_matrix (degree-bounded columns) had displaced PR SpectralSequences#271's GPU partial-matrix offload and cross-signature row-reuse. Add a restricted GPU variant (get_partial_matrix_restricted[_verified]) that keeps the batched Milnor multiply but sizes output to the frozen prefix and masks bits beyond it, dispatch to it from restricted_partial_matrix_maybe_gpu, and restore row-reuse (one full restricted matrix per bidegree, select_rows per signature). Verified GPU==CPU on H200 NVL (CUDA 12.4): nassau_gpu (14230 rows / 527 bidegrees) and milnor_vs_nassau (9 tests) pass with NASSAU_GPU_VERIFY=1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a flat, arc-swapped `seqno` index for the Milnor basis: an O(#p-part-entries) rank computed from a precomputed `g` table, with no hash lookup. This is a general (GPU-agnostic) data structure — it is the uploadable/on-device index primitive, and an independently benchmarkable alternative to the basis hashmap. - `SeqnoTables` + `compute_seqno_tables` (idempotent, concurrency-safe via `ArcSwapOption::rcu`) + `seqno` in `milnor_algebra.rs`, applicable at p = 2 with a trivial profile and stable ordering. - The CPU basis index deliberately still uses the hashmap (the tables lose to it on the CPU); `compute_basis` does not build them. Documented at `try_basis_element_to_index`. - `benches/seqno.rs`: A/B of the table index against the hashmap. - Covered by the `seqno_matches_enumeration_order` test. Split out of the Nassau GPU work as standalone infrastructure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UPYvsLdEfitgCbAiPxxx3U
- `seqno`: add a `debug_assert` that the element's degree is within the loaded table's `max_degree`, so a not-built-far-enough call fails with a clear diagnostic instead of a raw slice out-of-bounds panic. - `seqno_matches_enumeration_order`: build the tables partially, rebuild identically (no-op), grow, then request a smaller degree — exercising the idempotent, monotonic (non-shrinking) publish documented on `compute_seqno_tables`, not just a single build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UPYvsLdEfitgCbAiPxxx3U
JoeyBF
force-pushed
the
claude/nassau-gpu-refactor-k1ggps
branch
from
September 2, 2026 18:02
31aced0 to
8936ed8
Compare
The A/B benchmark stopped at degree 64, where the basis has a few hundred
elements and the per-degree hashmap is a few kilobytes. Both indices are
cache-resident there, so it only ever measured fixed overhead, and the
overhead it measured was largely self-inflicted: `seqno` took an `arc_swap`
guard on every call and re-derived the degree as the sum of r_i * xi_i, while
the hashmap reads the degree straight off the basis element.
`seqno` now takes the degree from the caller, which always has it, and
`seqno_ranker` acquires the tables once for a batch of lookups. Together those
are worth 2.2x-2.8x.
With that fixed and the sweep extended, the two indices separate the way their
shapes predict. The hashmap holds one entry per basis element of a single
degree, so its working set grows with that dimension; the `g` table is shared
across degrees and grows only linearly in the degree. Per lookup, at p = 2:
deg dim hashmap seqno
32 47 7.09 12.39
100 1,189 7.39 15.59
180 10,155 8.53 17.18
260 46,750 13.24 18.15
300 87,977 15.26 18.07
320 117,834 22.27 18.70
370 231,354 46.76 18.98
400 335,566 53.27 19.56
seqno is flat -- 12 ns to 20 ns while the dimension grows by four orders of
magnitude -- and the hashmap degrades 7.5x as it falls out of cache. They cross
between degree 300 and 320, and by degree 400 the table is 2.7x faster.
This is the result Christian Nassau has been reporting since 1998, when the
comparison was against binary search over a written-out basis. It survives the
move to a hashmap keyed on the packed p-part: the constant factor changed, the
asymptotics did not.
The comments claiming the tables simply lose on the CPU are corrected, and
`compute_seqno_tables` gains the degree bound `compute_basis` already asserts,
which CodeRabbit flagged: a negative argument wrapped to a huge row count.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UPYvsLdEfitgCbAiPxxx3U
Offload the admissible-matrix Milnor multiply at the heart of Nassau's `get_partial_matrix` to the GPU via CubeCL, for the p = 2 / trivial-profile regime. Gated behind the `gpu` feature; `cargo check`/`build` need no CUDA toolkit (cudarc dlopens at runtime). - `milnor_algebra.rs`: an admissible-matrix multiply (`multiply_basis_element_by_element_2` + `AdmissibleMatrix`) as the CPU reference model for the kernel, plus the `#[cfg(feature = "gpu")]` upload helpers (`admissible_matrices`, `seqno_table_u32`, `gpu_multiply_applicable`). Covered by `admissible_multiply_agrees_with_reference`. - `milnor_gpu.rs`: the batched CubeCL kernel (`multiply_batch_kernel` / `multiply_batch_on_gpu`), fusing every `(R, s)` product of one matrix build into a single launch, with dedup-by-`R`, device-resident admissible buffers, and per-launch memory release pinned to one CUDA stream. The single-kernel building blocks used only to validate it live under `#[cfg(test)]`, so the production surface is just `multiply_batch_on_gpu` + `GpuProduct`. - `ext::nassau_gpu` + the `build_partial_matrix` hook in `nassau.rs`: route the differential-matrix build through the GPU path when compiled in, opted into (`NASSAU_GPU`), applicable, and large enough to amortise launch overhead; otherwise the CPU path is used unchanged. - `free_module.rs`: skip zero generator blocks in `act` (a general CPU win). - Supporting `nassau_e2e` example, GPU/CPU consistency tests, the `milnor_gpu_ab` A/B bench, and a `gpu` dev shell in `flake.nix`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UPYvsLdEfitgCbAiPxxx3U
JoeyBF
force-pushed
the
claude/nassau-gpu-refactor-k1ggps
branch
from
September 2, 2026 18:42
8936ed8 to
0512d8a
Compare
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.
Reorganized successor to #264: the GPU Milnor-multiply offload, with the benchmark suite (#269, merged) and the
seqnoindex (#270) split out.What
Offloads the admissible-matrix Milnor multiply at the heart of Nassau's
get_partial_matrixto the GPU via CubeCL, for thep = 2/ trivial-profile regime. Gated behind thegpufeature.milnor_algebra.rs: an admissible-matrix multiply (multiply_basis_element_by_element_2+AdmissibleMatrix) as the CPU reference model for the kernel, plus the#[cfg(feature = "gpu")]upload helpers. Covered byadmissible_multiply_agrees_with_reference.milnor_gpu.rs: the batched CubeCL kernel (multiply_batch_kernel/multiply_batch_on_gpu), fusing every(R, s)product of one matrix build into a single launch, with dedup-by-R, device-resident admissible buffers, and per-launch memory release pinned to one CUDA stream. Single-kernel building blocks used only to validate it live under#[cfg(test)], so the production surface is justmultiply_batch_on_gpu+GpuProduct.ext::nassau_gpu+ thebuild_partial_matrixhook innassau.rs: route the differential-matrix build through the GPU path when compiled in, opted into (NASSAU_GPU), applicable, and large enough to amortise launch overhead; otherwise the CPU path is unchanged.free_module.rs: skip zero generator blocks inact(a general CPU win).nassau_e2eexample, GPU/CPU consistency tests, and themilnor_gpu_abA/B bench.Rebased onto current master
Master has moved a long way since this was opened, in two ways that touch this PR directly.
#280 packed the Milnor p-part into a
u64. The host-side extraction is ported to thePPartAPI throughout. One place this is a straight improvement: the GPU resident cache keyed its per-Radmissible-matrix table onVec<PPartEntry>, and that key is now the packedPPartitself —Copy,Hash, no allocation per lookup. The admissible sweep inmultiply_basis_element_by_element_2was rewritten to assemble its result in a plainu64and store it once, the same approach #280 took inPPartMultiplier; the trailing-zero trim it used to need is gone, because the packed form does not represent trailing zeros.fp-cudaarrived, withext's owngpufeature meaning "fp's Hopper F₂ matmul". Rather than introduce a second GPU feature name,ext'sgpunow enables both backends: they sit at different layers (F₂ linear algebra vs the Milnor product) and are independent at runtime. Note the consequence —fp-cudacompiles its kernel with nvcc in a build script, so--features gpunow needs the toolkit at build time, which is why the workspace excludes that crate. The Milnor half alone has no such requirement (cubecl JITs through NVRTC at runtime), socargo check -p algebra --features gputype-checks it with no CUDA installed.The
flake.nixchanges this PR used to carry are dropped entirely — master'sdevShells.gpualready provides the CUDA toolkit and loader paths they were adding.Notes vs #264
Dead code and dev-only profiling scaffolding removed; the validation-only kernels moved behind
#[cfg(test)]. The env-var dispatch and the process-global GPU context are unchanged (deferred as larger follow-ups).Test plan
cargo test -p algebra— 89 pass, incl.admissible_multiply_agrees_with_reference, the CPU oracle for the kernelcargo test -p ext --features nassau --test milnor_vs_nassau— CPU Nassau correctnesscargo clippy -p algebra --features gpu --all-targets— type-checks the kernel and thecfg(test)validation kernels without CUDAjust lint(nightly fmt +cargo hack clippyfeature-powerset,-D warnings)tests/nassau_gpu*.rs, and the in-cratemilnor_gpudevice tests) require a live CUDA device — not run here or in CI🤖 Generated with Claude Code
Generated by Claude Code