Execute a per-node computation in parallel across partitions. `f` is called once per node with `(node_id, snapshot)`. Results are collected into a Vec indexed by node ID. Uses `std::thread::scope` for structured concurrency — all threads are joined before this function returns. Safe for `!Send` callers (the snapshot is shared via `Arc`).
(
snapshot: &Arc<CsrSnapshot>,
config: &ParallelConfig,
f: impl Fn(u32, &CsrSnapshot) -> T + Send + Sync,
)
| 99 | /// are joined before this function returns. Safe for `!Send` callers |
| 100 | /// (the snapshot is shared via `Arc`). |
| 101 | pub fn parallel_map<T: Send + Default + Clone>( |
| 102 | snapshot: &Arc<CsrSnapshot>, |
| 103 | config: &ParallelConfig, |
| 104 | f: impl Fn(u32, &CsrSnapshot) -> T + Send + Sync, |
| 105 | ) -> Vec<T> { |
| 106 | let n = snapshot.node_count(); |
| 107 | if n == 0 { |
| 108 | return Vec::new(); |
| 109 | } |
| 110 | |
| 111 | let partitions = compute_partitions(n, config); |
| 112 | let f = &f; |
| 113 | |
| 114 | if partitions.len() <= 1 { |
| 115 | // Single-threaded fast path. |
| 116 | return (0..n as u32).map(|node| f(node, snapshot)).collect(); |
| 117 | } |
| 118 | |
| 119 | let mut results = vec![T::default(); n]; |
| 120 | |
| 121 | std::thread::scope(|scope| { |
| 122 | let mut handles = Vec::with_capacity(partitions.len()); |
| 123 | |
| 124 | for range in &partitions { |
| 125 | let snap = Arc::clone(snapshot); |
| 126 | let range = *range; |
| 127 | |
| 128 | let handle = scope.spawn(move || { |
| 129 | let mut partial = Vec::with_capacity(range.len()); |
| 130 | for node in range.start..range.end { |
| 131 | partial.push(f(node, &snap)); |
| 132 | } |
| 133 | (range, partial) |
| 134 | }); |
| 135 | handles.push(handle); |
| 136 | } |
| 137 | |
| 138 | for handle in handles { |
| 139 | let (range, partial) = handle.join().expect("parallel worker panicked"); |
| 140 | for (i, val) in partial.into_iter().enumerate() { |
| 141 | results[range.start as usize + i] = val; |
| 142 | } |
| 143 | } |
| 144 | }); |
| 145 | |
| 146 | results |
| 147 | } |
| 148 | |
| 149 | /// Execute a per-node computation that produces a single aggregate per partition. |
| 150 | /// |