pub const ROOT_BATCH_MIN_PROBLEMS: usize = 256;Expand description
Problem count below which a ComputeBackend::CpuMulti request runs the
iterative root-finding kernels (solve_bracketed_batch,
solve_newton_batch) on the calling thread instead.
§Measured crossover
Methodology. Measured 2026-08-12 on this workspace’s development machine,
std::thread::available_parallelism() = 4, release build, --features parallel, rayon’s global pool, otherwise-idle machine. Batches of n
independent problems x^2 - k_i = 0 with k_i spread over [1, 3) so that
per-lane iteration counts differ, bracket [0, 4], RootMethod::Brent,
RootSettings::default. Best of 7 samples per point, reported as wall
clock for one whole batch. Produced by the #[ignore]d
root_batch_crossover_benchmark test in parallel/tests.rs and transcribed
from its printed output. The cheap closure is the two-flop residual above;
the costly closure adds a ln/exp/sqrt chain per evaluation, standing
in for a JANAF enthalpy. The serial columns are from the first run; the
(repeat) columns are the speed-ups from a second, independent run of the
same benchmark, carried alongside rather than averaged away because the
parallel column is far noisier than the serial one.
| Problems | cheap serial | cheap multi | speed-up | (repeat) | costly serial | costly multi | speed-up | (repeat) |
|---|---|---|---|---|---|---|---|---|
| 16 | 2.87 us | 34.77 us | 0.08x | 0.07x | 11.21 us | 42.19 us | 0.27x | 0.93x |
| 32 | 6.67 us | 36.60 us | 0.18x | 0.71x | 19.32 us | 43.55 us | 0.44x | 1.35x |
| 64 | 12.99 us | 45.16 us | 0.29x | 0.29x | 42.85 us | 62.99 us | 0.68x | 0.83x |
| 128 | 25.96 us | 57.24 us | 0.45x | 0.59x | 84.88 us | 86.65 us | 0.98x | 1.26x |
| 256 | 52.94 us | 42.79 us | 1.24x | 2.28x | 175.37 us | 126.70 us | 1.38x | 3.18x |
| 512 | 108.80 us | 68.63 us | 1.59x | 2.91x | 356.78 us | 170.71 us | 2.09x | 2.04x |
| 1 024 | 201.83 us | 127.52 us | 1.58x | 2.01x | 695.26 us | 284.70 us | 2.44x | 2.17x |
| 4 096 | 805.49 us | 501.42 us | 1.61x | 3.63x | 2774.06 us | 992.73 us | 2.79x | 3.58x |
| 16 384 | 3338.49 us | 1173.15 us | 2.85x | 2.71x | 11423.08 us | 3916.59 us | 2.92x | 2.83x |
| 65 536 | 13730.89 us | 5071.05 us | 2.71x | 2.94x | 45778.24 us | 16260.96 us | 2.82x | 3.02x |
Result. 256 is the smallest size at which neither closure lost in either run — cheap 1.24x and 2.28x, costly 1.38x and 3.18x — and it is the value this constant takes. At 128 the cheap closure lost both runs (0.45x, 0.59x) while the costly one was a coin flip (0.98x, 1.26x), which is the expected shape: a more expensive residual crosses over earlier because there is more work per lane to amortise the same dispatch cost. The honest headline is therefore that the crossover depends on the caller’s residual, and this single number is set for the cheapest residual a caller is likely to pass, so that it is safe for both.
Interpretation. This is 16x lower than the crate-wide placeholder
crate::compute::CPU_MULTI_MIN_WORK_ITEMS (4 096) and 512x lower than
crate::fields::parallel::FIELD_PARALLEL_CROSSOVER (131 072), and the
reason is structural rather than incidental. Field algebra does one or two
flops per element loaded and is memory-bandwidth bound, so extra cores buy
almost nothing; a root solve does tens of iterations of arithmetic per lane
against a handful of loaded bytes, so it is compute bound and threads scale
on it. Keeping the crate-wide placeholder here would forfeit a measured
1.6x-2.4x on batches of 1 024.
§Relationship to crate::compute::CPU_MULTI_MIN_WORK_ITEMS
That constant documents itself as an unmeasured placeholder and invites
per-kernel overrides. This is that override for the iterative root finders.
It is now the fourth measured value in the crate, and the spread across the
four (256, 4 096, 131 072, 262 144 — a factor of 1 024) is further
evidence that no single crate-wide threshold can be right. Offered as input
to bead op-yvj.4.7.
§Limitations
One machine, four logical cores, one closure family, idle. Not measured on
Android/Termux hardware and not on a many-core server. The crossover moves
with the caller’s residual cost, so a caller that knows its own closure is
expensive is better off calling the kernel with an explicit
ComputeBackend than trusting a single number.
§Units
A count of independent root problems, dimensionless.