Skip to main content

internal_parallel_demo/
internal_parallel_demo.rs

1use std::sync::Arc;
2use std::time::Duration;
3
4use roma_lib::algorithms::{
5    Algorithm,
6    GeneticAlgorithm,
7    GeneticAlgorithmParameters,
8    run_algorithm_instances_async,
9    spawn_algorithm_run,
10    TerminationCriteria,
11    TerminationCriterion,
12};
13use roma_lib::operator::{BinaryTournamentSelection, BitFlipMutation, SinglePointCrossover};
14use roma_lib::problem::{KnapsackBuilder, KnapsackProblem};
15use roma_lib::solution_set::SolutionSet;
16use roma_lib::utils::{measure_result, speedup};
17use roma_lib::utils::cli::seed_from_cli_or;
18
19fn build_problem() -> KnapsackProblem {
20    let items: Vec<(f64, f64)> = (0..90)
21        .map(|i| {
22            let weight = 3.0 + ((i * 19 % 47) as f64);
23            let value = 9.0 + ((i * 31 % 113) as f64) + weight * 1.2;
24            (weight, value)
25        })
26        .collect();
27
28    KnapsackBuilder::new()
29        .with_capacity(1000.0)
30        .add_items(items)
31        .build()
32}
33
34fn ga_params(
35    seed: u64,
36) -> GeneticAlgorithmParameters<bool, SinglePointCrossover, BitFlipMutation, BinaryTournamentSelection>
37{
38    GeneticAlgorithmParameters::new(
39        120,
40        0.90,
41        0.05,
42        SinglePointCrossover::new(),
43        BitFlipMutation::new(),
44        BinaryTournamentSelection::new(),
45        TerminationCriteria::new(vec![TerminationCriterion::MaxIterations(120)]),
46    )
47    .with_elite_size(2)
48    .with_seed(seed)
49    // Keep inner algorithm execution sequential to isolate orchestration impact.
50    .sequential()
51}
52
53fn benchmark_sequential(problem: &KnapsackProblem, instances: usize, base_seed: u64) -> Result<(Duration, f64), String> {
54    measure_result(|| {
55        let mut checksum = 0.0;
56
57        for i in 0..instances {
58            let mut algorithm = GeneticAlgorithm::new(ga_params(base_seed + i as u64));
59            let solution_set = algorithm.run(problem)?;
60            checksum += solution_set.best_solution_value_or(problem, 0.0);
61        }
62
63        Ok::<f64, String>(checksum)
64    })
65}
66
67fn benchmark_spawn_runtime(problem: Arc<KnapsackProblem>, instances: usize, base_seed: u64) -> Result<(Duration, f64), String> {
68    measure_result(|| {
69        let mut handles = Vec::with_capacity(instances);
70
71        for i in 0..instances {
72            let algorithm = GeneticAlgorithm::new(ga_params(base_seed + i as u64));
73            handles.push(spawn_algorithm_run(algorithm, Arc::clone(&problem)));
74        }
75
76        let mut checksum = 0.0;
77        for handle in handles {
78            let (_algorithm, run_result) = handle
79                .join()
80                .expect("spawn_algorithm_run worker panicked while executing");
81            let solution_set = run_result?;
82            checksum += solution_set.best_solution_value_or(problem.as_ref(), 0.0);
83        }
84
85        Ok::<f64, String>(checksum)
86    })
87}
88
89fn benchmark_batch_async(problem: Arc<KnapsackProblem>, instances: usize, base_seed: u64) -> Result<(Duration, f64), String> {
90    let algorithms: Vec<GeneticAlgorithm<bool, SinglePointCrossover, BitFlipMutation, BinaryTournamentSelection>> =
91        (0..instances)
92            .map(|i| GeneticAlgorithm::new(ga_params(base_seed + i as u64)))
93            .collect();
94
95    measure_result(|| {
96        let results = run_algorithm_instances_async::<
97            GeneticAlgorithm<bool, SinglePointCrossover, BitFlipMutation, BinaryTournamentSelection>,
98            bool,
99            f64,
100            KnapsackProblem,
101        >(Arc::clone(&problem), algorithms);
102
103        let mut checksum = 0.0;
104        for (_algorithm, run_result) in results {
105            let solution_set = run_result?;
106            checksum += solution_set.best_solution_value_or(problem.as_ref(), 0.0);
107        }
108
109        Ok::<f64, String>(checksum)
110    })
111}
112
113fn main() {
114    let seed = seed_from_cli_or(10_000u64);
115    let instances = 16usize;
116    let problem = Arc::new(build_problem());
117
118    println!("Normal-run benchmark with deterministic seeds");
119    println!("This example compares orchestration overhead for identical GA runs.");
120    println!("Each algorithm run is internally sequential (.sequential()).");
121    println!("Instances: {}", instances);
122    println!("Modes: sequential | spawn_algorithm_run | run_algorithm_instances_async");
123
124    let (seq_time, seq_sum) = match benchmark_sequential(problem.as_ref(), instances, seed) {
125        Ok(v) => v,
126        Err(e) => {
127            eprintln!("Sequential run failed: {}", e);
128            return;
129        }
130    };
131
132    let (spawn_time, spawn_sum) = match benchmark_spawn_runtime(Arc::clone(&problem), instances, seed) {
133        Ok(v) => v,
134        Err(e) => {
135            eprintln!("spawn_algorithm_run benchmark failed: {}", e);
136            return;
137        }
138    };
139
140    let (async_time, async_sum) = match benchmark_batch_async(Arc::clone(&problem), instances, seed) {
141        Ok(v) => v,
142        Err(e) => {
143            eprintln!("run_algorithm_instances_async benchmark failed: {}", e);
144            return;
145        }
146    };
147
148    println!("\nTiming:");
149    println!("  sequential          = {:?}", seq_time);
150    println!("  spawn_algorithm_run = {:?}", spawn_time);
151    println!("  run_algorithm_instances_async = {:?}", async_time);
152
153    println!("\nSpeedup vs sequential:");
154    println!("  spawn_algorithm_run : {:.2}x", speedup(seq_time, spawn_time));
155    println!("  run_algorithm_instances_async: {:.2}x", speedup(seq_time, async_time));
156
157    println!("\nChecksums (sum of best objective values):");
158    println!("  sequential          = {:.6}", seq_sum);
159    println!("  spawn_algorithm_run = {:.6}", spawn_sum);
160    println!("  run_algorithm_instances_async = {:.6}", async_sum);
161}