Skip to main content

Module par

Module par 

Source
Expand description

The single seam every CPU parallel region in this crate goes through.

There used to be about fifty spellings of “run this over rows in parallel” scattered through crate::weight_matrix alone, each one an inline rayon iterator chain. That is the shape this repo has been burned by before: many copies of one decision, with nothing making them agree. Routing them all through the handful of functions below means the choice of how work is scheduled is made in one place.

Which is exactly what issue #27 needs, because it wants that choice changed: rayon forks and joins per operation, per layer, per token, and llama.cpp instead hands work to a pool that is already awake. Backend::Spin is that pool (crate::cpu_pool).

§The switch

Which of the two runs is decided per operation, from its size, by the one predicate in policy: backend. FERROX_CPU_POOL pins it either way (spin / rayon) and is an A/B override, not the decision. See policy::SPIN_MIN_OP_MACS for the crossover and what is and is not measured about it.

Every helper below asks backend and none of them decides anything itself, which is what stops the two arms of that choice from drifting apart across thirty call sites.

§min_len, and where MIN_TASK_MACS went

Every helper takes a min_len. On the rayon arm it is passed straight to with_min_len, which is what the call sites did by hand before, so the fork-join path’s task decomposition is bit-for-bit what it was.

On the spin arm it is ignored. MIN_TASK_MACS existed to stop rayon splitting a matvec into tasks too small to pay for their own fork-join; when a region costs a cache-line transfer instead of a futex there is nothing to pay for, so the spin arm chunks purely by pool width (task_count) the way ggml_compute_forward_mul_mat does. That is the deletion issue #27 asks for, and it is a deletion rather than a retune: no MAC threshold is consulted on this path at all. It survives on the rayon arm because the rayon arm still runs every operation below the crossover, and removing it there re-opens the measured 13-16x small-model regression documented on crate::weight_matrix::WeightMatrix::min_rows_per_task.

Re-exports§

pub use policy::backend;
pub use policy::macs_per_row;
pub use policy::with_op_work;

Modules§

policy
Which CPU scheduler one operation’s parallel regions run on, and the work quantity that decides it.

Enums§

Backend
Which scheduler CPU parallel regions use.

Functions§

chunks_mut
Run f(chunk_index, &mut chunk) over data split into runs of chunk_len, the shape rayon spells par_chunks_mut(chunk_len).
chunks_mut2
chunks_mut over two slices of the same length at once, the shape rayon spells a.par_chunks_mut(k).zip(b.par_chunks_mut(k)).
chunks_mut2_by
chunks_mut2 with a chunk length per slice: a in runs of len_a, b in runs of len_b, the SAME number of chunks (a gated delta-net’s per-head S x S state beside its per-head S output).
chunks_mut_init
chunks_mut with a per-task scratch value.
cold_regions
Parallel regions this thread has opened without being a rayon worker.
indices
Run f(index) for every index in 0..n.
indices_init
indices with a per-task scratch value, the shape rayon spells for_each_init. One S is created per task, not per index.
items_mut
Run f(index, &mut item) over data, the shape rayon spells par_iter_mut().with_min_len(..).enumerate().
join2
Two independent pieces of work.
join3
Three independent pieces of work; see join2.
num_threads
Worker count of the active backend.
on_workers
Run f on a rayon worker, so every parallel region it opens takes rayon’s IN-WORKER path instead of its cold-submission path.
task_count
How many tasks the spin arm splits n_items into.