Process files in parallel using rayon, returning per-file results. This is the core of the parallel recording pipeline (Phase 2). It takes a list of [`FileRecordInput`] descriptors (built during the sequential pre-pass) and processes each file independently on a rayon worker thread. # Automatic Sequential Fallback If `options.should_parallelize(inputs.len())` returns `false` (e.g., fewer than 4
(
inputs: &[FileRecordInput],
options: &ParallelRecordOptions,
)
| 432 | /// than 4 files), processing falls back to sequential iteration to avoid |
| 433 | /// rayon overhead. |
| 434 | pub fn parallel_record_files( |
| 435 | inputs: &[FileRecordInput], |
| 436 | options: &ParallelRecordOptions, |
| 437 | ) -> (Vec<Result<FileRecordOutput, String>>, ParallelRecordStats) { |
| 438 | use rayon::prelude::*; |
| 439 | |
| 440 | let start = Instant::now(); |
| 441 | let use_parallel = options.should_parallelize(inputs.len()); |
| 442 | |
| 443 | let results: Vec<Result<FileRecordOutput, String>> = if use_parallel { |
| 444 | inputs |
| 445 | .par_iter() |
| 446 | .map(|input| process_single_file(input, &options.core_options)) |
| 447 | .collect() |
| 448 | } else { |
| 449 | inputs |
| 450 | .iter() |
| 451 | .map(|input| process_single_file(input, &options.core_options)) |
| 452 | .collect() |
| 453 | }; |
| 454 | |
| 455 | let wall_time_ms = start.elapsed().as_millis() as u64; |
| 456 | |
| 457 | // Compute aggregate stats |
| 458 | let mut stats = ParallelRecordStats { |
| 459 | files_processed: inputs.len(), |
| 460 | used_parallel: use_parallel, |
| 461 | wall_time_ms, |
| 462 | ..Default::default() |
| 463 | }; |
| 464 | |
| 465 | let mut cpu_time_ms = 0u64; |
| 466 | for result in &results { |
| 467 | match result { |
| 468 | Ok(output) => { |
| 469 | cpu_time_ms += output.stats.processing_time_ms; |
| 470 | if output.skipped { |
| 471 | stats.files_skipped += 1; |
| 472 | } else if output.recorded.is_some() { |
| 473 | stats.files_recorded += 1; |
| 474 | } else { |
| 475 | stats.files_skipped += 1; |
| 476 | } |
| 477 | } |
| 478 | Err(_) => { |
| 479 | stats.files_errored += 1; |
| 480 | } |
| 481 | } |
| 482 | } |
| 483 | |
| 484 | stats.cpu_time_ms = cpu_time_ms; |
| 485 | stats.effective_parallelism = if wall_time_ms > 0 { |
| 486 | cpu_time_ms as f64 / wall_time_ms as f64 |
| 487 | } else { |
| 488 | 1.0 |
| 489 | }; |
| 490 | |
| 491 | (results, stats) |