Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
83819ef
Revert "nassau: remove the ParallelGuard priority-inversion retry mec…
JoeyBF Jul 20, 2026
5beaf22
nassau: park retries instead of busy-spinning on ParallelGuard
claude Jul 22, 2026
80d614f
nassau: make the parallel-section guard per-thread
claude Jul 22, 2026
540b31f
nassau: test that the parallel-section guard is thread-local
claude Jul 22, 2026
2b54cf9
Merge remote-tracking branch 'origin/claude/nassau-relaxed-dependency…
JoeyBF Jul 22, 2026
dbc645e
milnor_gpu: drop the global device mutex; make the resident store thr…
JoeyBF Jul 22, 2026
f1cc8fa
milnor_gpu: bound GPU-path memory (row blocks, launch permits, shared…
JoeyBF Jul 23, 2026
a0022b7
milnor_gpu: byte-weighted launch budget + prefix-doubling master uploads
JoeyBF Jul 23, 2026
a04dd91
nassau: offload the d_s image build (signature_matrix) to the GPU mul…
JoeyBF Jul 23, 2026
bc253f6
milnor_gpu: zero the out_h accumulator on-device instead of uploading…
JoeyBF Jul 23, 2026
c433556
milnor_gpu: move the resident-master device upload off the handle lock
JoeyBF Jul 24, 2026
164b129
milnor_gpu: fill the term-data upload buffers directly, killing the p…
JoeyBF Jul 24, 2026
b02dcb3
milnor_gpu: raise GPU_PAIR_CHUNK to ~2^32 so giant multiplies stop ov…
JoeyBF Jul 24, 2026
e6b85fd
milnor_gpu: make the Milnor basis resident on the GPU, upload term in…
JoeyBF Jul 24, 2026
4efee67
milnor_gpu: safe multi-stream via in-place-grown resident globals
JoeyBF Jul 25, 2026
0c1712a
fp: trace GPU vs CPU row-reduce dispatch (fp::rr)
JoeyBF Jul 25, 2026
f22f8a7
fp,algebra: serialize cubecl multiply vs cooperative GPU row-reduce
JoeyBF Jul 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,109 changes: 913 additions & 196 deletions ext/crates/algebra/src/algebra/milnor_gpu.rs

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion ext/crates/fp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ paste = "1.0.15"
proptest = { version = "1.7", optional = true }
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.141"
# Only used for the GPU row-reduce dispatch instrumentation; pulled in by `gpu`.
tracing = { version = "0.1.41", optional = true }

maybe-rayon = { path = "../maybe-rayon" }
query = { path = "../query" }
Expand Down Expand Up @@ -45,7 +47,7 @@ default = ["odd-primes"]
concurrent = ["maybe-rayon/concurrent"]
odd-primes = []
# Dispatch large p=2 matrix products to the Hopper GPU backend (`fp-cuda`).
gpu = ["dep:fp-cuda"]
gpu = ["dep:fp-cuda", "dep:tracing"]

[[bench]]
name = "mul"
Expand Down
20 changes: 19 additions & 1 deletion ext/crates/fp/src/blas/cuda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,26 @@
//! CPU path is used. Defaults to 2048; the GPU only wins once the kernel work
//! dwarfs the H2D/D2H + TMA-layout marshalling, which dominates small sizes.

use std::sync::{Mutex, OnceLock};
use std::sync::{Mutex, OnceLock, RwLock};

use fp_cuda::GpuContext;

use crate::{matrix::Matrix, prime::TWO};

/// Serializes the fp-cuda **cooperative** row-reduce (raw cudarc) against the algebra
/// **cubecl** Milnor multiply, which share the one physical GPU from different CUDA
/// runtimes. The row-reduce's `cuLaunchCooperativeKernel` (panel_factor_coop's grid-wide
/// spin barrier) needs *all* its CTAs co-resident; a cubecl multiply kernel occupying SMs
/// at that moment prevents co-residency, so the missing CTAs never reach the barrier and
/// the resident ones spin forever (flat sm=100% — the intermittent stem-150 wedge).
///
/// Contract: the cooperative row-reduce takes the **write** lock (exclusive GPU) around its
/// launch+download; every cubecl multiply takes a **read** lock held from launch through its
/// readback (so releasing means that kernel has actually completed, not merely enqueued).
/// Readers run concurrently with each other; a pending row-reduce drains them, runs alone,
/// then lets them resume. See `algebra::algebra::milnor_gpu` for the read side.
pub static GPU_EXCLUSIVE: RwLock<()> = RwLock::new(());

/// Smallest `min(m, k, n)` for which we attempt the GPU matmul. Below this the
/// host marshalling (bit-repack into TMA tiles + copies) costs more than it saves.
const DEFAULT_THRESHOLD: usize = 2048;
Expand Down Expand Up @@ -127,7 +141,11 @@ pub(crate) fn try_row_reduce(m: &mut Matrix) -> Option<usize> {
let stride = cols.div_ceil(64);
let limbs = to_limbs(m);

// Exclusive GPU for the cooperative reduce: block new cubecl multiplies and wait for
// in-flight ones to complete, so panel_factor_coop's grid-wide barrier can co-reside all
// its CTAs (see [`GPU_EXCLUSIVE`]). Held across launch + download; ordered before ctx.lock.
let (dev_limbs, perm, r, pivot_cols) = {
let _exclusive = GPU_EXCLUSIVE.write().unwrap_or_else(|e| e.into_inner());
let gpu = ctx.lock().ok()?;
let mut dm = gpu.upload(&limbs, rows, cols).ok()?;
let (perm, r, pivot_cols) = gpu.row_reduce_dev(&mut dm).ok()?;
Expand Down
6 changes: 6 additions & 0 deletions ext/crates/fp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ pub mod vector;

pub mod blas;

/// Cross-runtime GPU serialization lock (cubecl multiply vs. cooperative fp-cuda row-reduce).
/// Re-exported so `algebra`'s Milnor-multiply GPU path can take the read side. See
/// [`blas::cuda::GPU_EXCLUSIVE`].
#[cfg(feature = "gpu")]
pub use blas::cuda::GPU_EXCLUSIVE;

pub(crate) mod simd;

// This is useful for traits that want to implement `Arbitrary`. This lets us specify that they
Expand Down
50 changes: 46 additions & 4 deletions ext/crates/fp/src/matrix/matrix_inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -677,11 +677,53 @@ impl Matrix {
// For large p = 2 matrices, try the device-resident GPU reduction; it
// produces the identical canonical RREF + pivots. Falls back to the CPU
// M4RI path below when the GPU is unavailable or below threshold.
//
// Instrumentation: for every p=2 reduction of a non-trivial matrix
// (min(rows,cols) >= 1024) we emit a `fp::rr` tracing event recording the
// dimensions and whether the GPU path was taken (`path="gpu"`) or it fell
// back to CPU M4RI (`path="cpu"` — either below the 8192 threshold or a
// launch failure; the logged dims disambiguate). The event inherits the
// active nassau span, so it carries the bidegree/signature context.
#[cfg(feature = "gpu")]
if p == 2
&& let Some(rank) = crate::blas::cuda::try_row_reduce(self)
{
return rank;
if p == 2 {
let (rr_rows, rr_cols) = (self.rows(), self.columns());
let rr_big = rr_rows.min(rr_cols) >= 1024;
// Wrap the GPU reduce in an ENTERED span (not just a completion event) so a wedge
// *inside* `try_row_reduce` leaves an open `gpu_row_reduce` span with no `close` in the
// log — distinguishing an RREF hang from a milnor-multiply hang. Inherits the active
// nassau step span, so it carries the bidegree/signature. Dropped on return/fallthrough.
let _rr_span = rr_big.then(|| {
tracing::info_span!(target: "fp::rr", "gpu_row_reduce", rows = rr_rows, cols = rr_cols)
.entered()
});
match crate::blas::cuda::try_row_reduce(self) {
Some(rank) => {
if rr_big {
tracing::info!(
target: "fp::rr",
rows = rr_rows,
cols = rr_cols,
min = rr_rows.min(rr_cols),
path = "gpu",
rank,
"row_reduce"
);
}
return rank;
}
None => {
if rr_big {
tracing::info!(
target: "fp::rr",
rows = rr_rows,
cols = rr_cols,
min = rr_rows.min(rr_cols),
path = "cpu",
"row_reduce"
);
}
}
}
}

self.initialize_pivots();
Expand Down
Loading
Loading