diff --git a/crates/specialized/rtx-cfd/src/solvers/incompressible/poisson.rs b/crates/specialized/rtx-cfd/src/solvers/incompressible/poisson.rs index 5798fb4..a545eed 100644 --- a/crates/specialized/rtx-cfd/src/solvers/incompressible/poisson.rs +++ b/crates/specialized/rtx-cfd/src/solvers/incompressible/poisson.rs @@ -604,6 +604,7 @@ impl Level { self.cells .par_iter() .zip(tmp[..n].par_iter_mut()) + .with_min_len(PAR_MIN_LEN) .for_each(|(&idx, t)| { *t = b[idx] - (self.ap[idx] * x[idx] - self.neighbour_sum(x, idx)); }); @@ -628,6 +629,7 @@ impl Level { colour .par_iter() .zip(tmp[..n].par_iter_mut()) + .with_min_len(PAR_MIN_LEN) .for_each(|(&idx, t)| { *t = (b[idx] + self.neighbour_sum(x_ro, idx)) / self.ap[idx]; }); @@ -802,10 +804,11 @@ impl Hierarchy { wf.x[idx] = T::ZERO; } let Work { b, x, r, tmp } = wf; + let par_here = parallel && fine.cells.len() >= PAR_MIN_CELLS; for _ in 0..self.sweeps { - fine.smooth(b, x, tmp, smoother, parallel); + fine.smooth(b, x, tmp, smoother, par_here); } - if parallel { + if par_here { fine.residual_par(b, x, r, tmp); } else { fine.residual(b, x, r); @@ -844,8 +847,9 @@ impl Hierarchy { wf.x[idx] += T::from_f64(COARSE_CORRECTION) * wc.x[fine.coarse_of[idx]]; } let Work { b, x, tmp, .. } = wf; + let par_here = parallel && fine.cells.len() >= PAR_MIN_CELLS; for _ in 0..self.sweeps { - fine.smooth(b, x, tmp, smoother, parallel); + fine.smooth(b, x, tmp, smoother, par_here); } } for &idx in &levels[0].cells { @@ -976,6 +980,14 @@ impl Prepared { } } +/// Threads pay only when a level is large: below this many active cells a +/// level's maps run serially even with `threads > 1` (the fork-join of a +/// map over a few thousand cells costs more than the map), and each task +/// covers at least [`PAR_MIN_LEN`] cells. Both are pure scheduling: the +/// values are the serial ones. +const PAR_MIN_CELLS: usize = 8192; +const PAR_MIN_LEN: usize = 2048; + /// Configure rayon's global thread pool for the multigrid's parallel maps /// (PERF-2 P2). Idempotent: a pool that already exists is kept (rayon /// refuses a second global pool) — the first caller decides.