| 170 | } |
| 171 | |
| 172 | pub fn run_parallel<A, R, F, E>( |
| 173 | atoms: &[A], |
| 174 | cfg: HpcParallelConfig, |
| 175 | callback: F, |
| 176 | ) -> Result<ParallelRunReport<R>, HpcParallelError> |
| 177 | where |
| 178 | A: Sync, |
| 179 | R: Send, |
| 180 | F: Fn(&[A]) -> Result<R, E> + Send + Sync, |
| 181 | E: Display, |
| 182 | { |
| 183 | validate_config(cfg)?; |
| 184 | if atoms.is_empty() { |
| 185 | return Ok(ParallelRunReport { |
| 186 | outputs: Vec::new(), |
| 187 | metrics: HpcParallelMetrics { |
| 188 | atoms_total: 0, |
| 189 | molecules_total: 0, |
| 190 | runtime: Duration::ZERO, |
| 191 | throughput_atoms_per_sec: 0.0, |
| 192 | throughput_molecules_per_sec: 0.0, |
| 193 | partition_imbalance_ratio: 0.0, |
| 194 | progress: Vec::new(), |
| 195 | }, |
| 196 | }); |
| 197 | } |
| 198 | |
| 199 | let worker_count = match cfg.mode { |
| 200 | ExecutionMode::Serial => 1, |
| 201 | ExecutionMode::Threaded { num_threads } => num_threads, |
| 202 | }; |
| 203 | let target_molecules = worker_count.saturating_mul(cfg.mp_batches).max(1); |
| 204 | let partitions = partition_atoms(atoms.len(), target_molecules, cfg.partition)?; |
| 205 | let started = Instant::now(); |
| 206 | |
| 207 | let (outputs, progress) = match cfg.mode { |
| 208 | ExecutionMode::Serial => run_serial(atoms, &partitions, cfg.progress_every, &callback)?, |
| 209 | ExecutionMode::Threaded { num_threads } => { |
| 210 | run_threaded(atoms, &partitions, cfg.progress_every, num_threads, callback)? |
| 211 | } |
| 212 | }; |
| 213 | |
| 214 | Ok(ParallelRunReport { |
| 215 | outputs, |
| 216 | metrics: build_metrics(atoms.len(), &partitions, started.elapsed(), progress), |
| 217 | }) |
| 218 | } |
| 219 | |
| 220 | pub fn dispatch_async<A, R, F, E>( |
| 221 | atoms: Vec<A>, |