Files
advent-of-code-2023-rust/src/main.rs

214 lines
7.0 KiB
Rust

extern crate core;
use std::time::Instant;
use crate::day10::Day10;
use crate::day11::Day11;
use crate::day12::Day12;
use crate::day1::Day1;
use crate::day2::Day2;
use crate::day3::Day3;
use crate::day4::Day4;
use crate::day5::Day5;
use crate::day6::Day6;
use crate::day7::Day7;
use crate::day8::Day8;
use crate::day9::Day9;
use crate::day_solver::DaySolver;
use crate::util::read_file;
mod util;
mod day_solver;
mod day1;
mod day2;
mod day3;
mod day4;
mod day5;
mod day6;
mod day7;
mod day8;
mod day9;
mod day10;
mod day11;
mod day12;
const DEFAULT_BENCHMARK_AMOUNT: u32 = 100;
fn main() {
let args: Vec<String> = std::env::args().collect();
let day_arg_idx = args.iter().position(|a| a == "-d");
let single_day = day_arg_idx.is_some();
let day = if single_day { args[day_arg_idx.unwrap() + 1].parse::<u8>().unwrap() } else { 0 };
let benchmark_arg_idx_option = args.iter().position(|a| a == "--bench").or(
args.iter().position(|a| a == "-b"));
let benchmark = benchmark_arg_idx_option.is_some();
let bench_amount: u32 = if let Some(benchmark_arg_idx) = benchmark_arg_idx_option {
args.get(benchmark_arg_idx + 1)
.map_or(DEFAULT_BENCHMARK_AMOUNT, |r| r.parse::<u32>().unwrap_or(DEFAULT_BENCHMARK_AMOUNT))
} else {
DEFAULT_BENCHMARK_AMOUNT
};
let mut bench_results: Vec<AocBenchResult> = Vec::new();
// This is essentially the warmup for the benchmark:
run_once(single_day, day, false, &mut bench_results);
let first_run_bench = bench_results[0].to_owned();
let (_, total_time) = bench(||
if benchmark {
// Ignore the warmup run in the rest of the benchmark:
bench_results.clear();
for _ in 0..bench_amount {
run_once(single_day, day, true, &mut bench_results);
}
});
if benchmark {
println!("Executed {} rounds in {:.3} s ({} μs, or {} μs on average per round)", bench_results.len(), total_time as f64 / 1000000f64, total_time, total_time / bench_amount as u128);
print_bench_result(&bench_results, |b| b.total, "Execution");
print_bench_result(&bench_results, |b| b.read_file, "Reading file");
print_bench_result(&bench_results, |b| b.init, "Initialization");
print_bench_result(&bench_results, |b| b.part1, "Part 1");
print_bench_result(&bench_results, |b| b.part2, "Part 2");
print_bench_result(&bench_results, |b| b.part1 + b.part2, "Part 1 & 2 together");
println!("First time took {} μs (read {} μs, init {} μs, part 1: {} μs, part 2: {} μs)", first_run_bench.total, first_run_bench.read_file, first_run_bench.init, first_run_bench.part1, first_run_bench.part2);
println!("Average: {} μs (read {} μs, init {} μs, part 1: {} μs, part 2: {} μs)",
avg_bench(&bench_results, |b| b.total),
avg_bench(&bench_results, |b| b.read_file),
avg_bench(&bench_results, |b| b.init),
avg_bench(&bench_results, |b| b.part1),
avg_bench(&bench_results, |b| b.part2));
} else {
println!("Execution took {} μs (read {} μs, init {} μs, part 1: {} μs, part 2: {} μs)", first_run_bench.total, first_run_bench.read_file, first_run_bench.init, first_run_bench.part1, first_run_bench.part2);
}
}
#[derive(Copy, Clone)]
struct AocBenchResult {
read_file: u128,
init: u128,
part1: u128,
part2: u128,
total: u128
}
fn build_day_solver(day: u8, input: String) -> Option<Box<dyn DaySolver>> {
match day {
1 => Some(Box::new(Day1::create(input))),
2 => Some(Box::new(Day2::create(input))),
3 => Some(Box::new(Day3::create(input))),
4 => Some(Box::new(Day4::create(input))),
5 => Some(Box::new(Day5::create(input))),
6 => Some(Box::new(Day6::create(input))),
7 => Some(Box::new(Day7::create(input))),
8 => Some(Box::new(Day8::create(input))),
9 => Some(Box::new(Day9::create(input))),
10 => Some(Box::new(Day10::create(input))),
11 => Some(Box::new(Day11::create(input))),
12 => Some(Box::new(Day12::create(input))),
_ => None
}
}
fn solve(day: u8, silent: bool) -> AocBenchResult {
let now = Instant::now();
let (input, read_file_time) = bench(|| {
let input_filename = format!("input/day{:02}.txt",day);
read_file(&input_filename)
});
let (solver, init_time) = bench(|| build_day_solver(day, input.to_owned()));
let part1_time: u128;
let part2_time: u128;
match solver {
Some(mut s) => {
let(part1, pt1_time) = bench(|| s.solve_part1());
part1_time = pt1_time;
if !silent {
println!("Day {} Part 1: {}", day, part1);
}
let (part2, pt2_time) = bench(|| s.solve_part2());
part2_time = pt2_time;
if !silent {
println!("Day {} Part 2: {}", day, part2);
}
},
None => panic!("This day is not yet implemented")
}
return AocBenchResult{
read_file: read_file_time,
init: init_time,
part1: part1_time,
part2: part2_time,
total: now.elapsed().as_micros()
}
}
fn print_bench_result<F>(bench_results: &Vec<AocBenchResult>, f: F, bench_part_description: &str)
where
F: FnMut(&AocBenchResult) -> u128 {
let mut benches: Vec<u128> = bench_results.iter().map(f).collect();
benches.sort();
let avg_runtime: u128 = benches.iter().sum::<u128>() / (bench_results.len() as u128);
println!("{} took {} μs {} (Min: {} μs, Max: {} μs, Median: {} μs)", bench_part_description, avg_runtime, if bench_results.len() > 1 { "on average"} else {""},
benches[0], benches[benches.len() - 1], benches[benches.len() / 2])
}
fn avg_bench<F>(bench_results: &Vec<AocBenchResult>, f: F) -> u128
where
F: FnMut(&AocBenchResult) -> u128 {
let benches: Vec<u128> = bench_results.iter().map(f).collect();
return benches.iter().sum::<u128>() / (bench_results.len() as u128)
}
fn run_once(single_day: bool, day: u8, silent: bool, bench_results: &mut Vec<AocBenchResult>) {
let bench_result = if single_day {
solve(day, silent)
} else {
solve_all(silent)
};
bench_results.push(bench_result);
}
fn solve_all(silent: bool) -> AocBenchResult {
let mut bench_results = Vec::new();
let max_day = (1..).take_while(|d| build_day_solver(*d, "".to_string()).is_some()).last().unwrap();
for day in 1..(max_day + 1) {
bench_results.push(solve(day, silent));
}
return AocBenchResult {
read_file: bench_results.iter().map(|t| t.read_file).sum(),
init: bench_results.iter().map(|t| t.init).sum(),
part1: bench_results.iter().map(|t| t.part1).sum(),
part2: bench_results.iter().map(|t| t.part2).sum(),
total: bench_results.iter().map(|t| t.total).sum(),
}
}
fn bench<F, K>(mut f: F) -> (K, u128)
where F: FnMut() -> K {
let now = Instant::now();
let res = f();
(res, now.elapsed().as_micros())
}