Execute a per-node computation that produces a single aggregate per partition. Each partition computes a partial result, then the `reduce` function merges all partials into a final result.
(
snapshot: &Arc<CsrSnapshot>,
config: &ParallelConfig,
map_fn: impl Fn(NodeRange, &CsrSnapshot) -> P + Send + Sync,
reduce_fn: impl Fn(Vec<P>) -> R,
)
| 151 | /// Each partition computes a partial result, then the `reduce` function |
| 152 | /// merges all partials into a final result. |
| 153 | pub fn parallel_reduce<P: Send, R>( |
| 154 | snapshot: &Arc<CsrSnapshot>, |
| 155 | config: &ParallelConfig, |
| 156 | map_fn: impl Fn(NodeRange, &CsrSnapshot) -> P + Send + Sync, |
| 157 | reduce_fn: impl Fn(Vec<P>) -> R, |
| 158 | ) -> R { |
| 159 | let n = snapshot.node_count(); |
| 160 | let partitions = compute_partitions(n, config); |
| 161 | let map_fn = &map_fn; |
| 162 | |
| 163 | if partitions.is_empty() { |
| 164 | return reduce_fn(Vec::new()); |
| 165 | } |
| 166 | |
| 167 | if partitions.len() <= 1 { |
| 168 | let partial = map_fn(partitions[0], snapshot); |
| 169 | return reduce_fn(vec![partial]); |
| 170 | } |
| 171 | |
| 172 | let partials: Vec<P> = std::thread::scope(|scope| { |
| 173 | let handles: Vec<_> = partitions |
| 174 | .iter() |
| 175 | .map(|&range| { |
| 176 | let snap = Arc::clone(snapshot); |
| 177 | scope.spawn(move || map_fn(range, &snap)) |
| 178 | }) |
| 179 | .collect(); |
| 180 | |
| 181 | handles |
| 182 | .into_iter() |
| 183 | .map(|h| h.join().expect("parallel worker panicked")) |
| 184 | .collect() |
| 185 | }); |
| 186 | |
| 187 | reduce_fn(partials) |
| 188 | } |
| 189 | |
| 190 | #[cfg(test)] |
| 191 | mod tests { |