More benchmarking overcomplicated stuff

Truly putting that AP Stats knowledge to the test
This commit is contained in:
Tyler Beckman 2023-12-16 00:17:07 -07:00
parent 78c41799e7
commit a8ece7c6d8
Signed by: Ty
GPG key ID: 2813440C772555A4
5 changed files with 206 additions and 74 deletions

7
Cargo.lock generated
View file

@ -38,6 +38,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"advent", "advent",
"clap", "clap",
"indoc",
"tokio", "tokio",
] ]
@ -621,6 +622,12 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "indoc"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
[[package]] [[package]]
name = "ipnet" name = "ipnet"
version = "2.9.0" version = "2.9.0"

View file

@ -6,4 +6,5 @@ edition = "2021"
[dependencies] [dependencies]
clap = { version = "4.4.10", features = ["derive"] } clap = { version = "4.4.10", features = ["derive"] }
tokio = { version = "1.34.0", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.34.0", features = ["macros", "rt-multi-thread"] }
advent = { path = "../lib" } advent = { path = "../lib" }
indoc = "2.0.4"

View file

@ -1,3 +1,5 @@
mod utils;
use std::{ use std::{
io::Write, io::Write,
time::{Duration, Instant}, time::{Duration, Instant},
@ -5,6 +7,7 @@ use std::{
use advent::fetcher; use advent::fetcher;
use clap::{Parser, Subcommand, ValueEnum}; use clap::{Parser, Subcommand, ValueEnum};
use indoc::printdoc;
#[derive(Parser)] #[derive(Parser)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@ -152,32 +155,32 @@ async fn main() {
} }
timings.drain(..=(warmup as usize)); timings.drain(..=(warmup as usize));
let avg = (timings.iter().sum::<Duration>() / n).as_nanos(); let avg = (timings.iter().sum::<Duration>() / n).as_nanos();
let deviation = (((timings.iter().max().unwrap().as_nanos() let max = timings.iter().max().unwrap().as_nanos();
- timings.iter().min().unwrap().as_nanos()) let min = timings.iter().min().unwrap().as_nanos();
.pow(2) let deviation = (((max - min).pow(2) / 16) as f64).sqrt() as u128;
/ 16) as f64) let range = max - min;
.sqrt() as u128; let median = if timings.len() % 2 == 0 {
(timings[timings.len()/2-1]+timings[timings.len()/2]).as_nanos() as f64 / 2.0
let (avg, avg_units) = if avg >= 1000000000 {
(avg as f64 / 1000000000.0, "s")
} else if avg >= 1000000 {
(avg as f64 / 1000000.0, "ms")
} else if avg >= 1000 {
(avg as f64 / 1000.0, "µs")
} else { } else {
(avg as f64, "ns") timings[timings.len()/2].as_nanos() as f64
}; };
let (avg, avg_units) = utils::unitify(avg as f64);
let (max, max_units) = utils::unitify(max as f64);
let (min, min_units) = utils::unitify(min as f64);
let (deviation, deviation_units) = utils::unitify(deviation as f64);
let (range, range_units) = utils::unitify(range as f64);
let (median, median_units) = utils::unitify(median as f64);
let (deviation, deviation_units) = if deviation >= 1000000000 { printdoc! {"
(deviation as f64 / 1000000000.0, "s") \rAverage timing: {avg}{avg_units}
} else if deviation >= 1000000 { |
(deviation as f64 / 1000000.0, "ms") |-- Std. Deviation: {deviation}{deviation_units}
} else if deviation >= 1000 { |-- Min: {min}{min_units}
(deviation as f64 / 1000.0, "µs") |-- Median: {median}{median_units}
} else { |-- Max: {max}{max_units}
(deviation as f64, "ns") |-- Range: {range}{range_units}
}; "}
println!("\nAverage timing: {avg}{avg_units} ± {deviation}{deviation_units}");
} }
Subcommands::Input { day } => { Subcommands::Input { day } => {
println!("{}", fetcher::fetch_input(auth, day).await); println!("{}", fetcher::fetch_input(auth, day).await);

11
cli/src/utils.rs Normal file
View file

@ -0,0 +1,11 @@
pub fn unitify(num: f64) -> (f64, &'static str) {
if num >= 1000000000.0 {
(num / 1000000000.0, "s")
} else if num >= 1000000.0 {
(num / 1000000.0, "ms")
} else if num >= 1000.0 {
(num / 1000.0, "µs")
} else {
(num, "ns")
}
}

View file

@ -4,13 +4,13 @@ use crate::utils::Day;
pub struct Beam { pub struct Beam {
pub position: Position, pub position: Position,
pub direction: Direction pub direction: Direction,
} }
#[derive(Clone, PartialEq, Eq, Debug, PartialOrd, Ord)] #[derive(Clone, PartialEq, Eq, Debug, PartialOrd, Ord)]
pub struct Position { pub struct Position {
pub x: usize, pub x: usize,
pub y: usize pub y: usize,
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
@ -18,7 +18,7 @@ pub enum Direction {
Up, Up,
Down, Down,
Right, Right,
Left Left,
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -28,22 +28,35 @@ pub struct Day16 {
impl Day for Day16 { impl Day for Day16 {
fn part1(&mut self) -> String { fn part1(&mut self) -> String {
let grid = self.input.lines().map(|l| l.chars().collect_vec()).collect_vec(); let grid = self
.input
.lines()
.map(|l| l.chars().collect_vec())
.collect_vec();
let mut heated = Vec::<(Position, Direction)>::new(); let mut heated = Vec::<(Position, Direction)>::new();
let mut beams = Vec::from(const { [Beam { position: Position { x: 0, y: 0 }, direction: Direction::Right }] }); let mut beams = Vec::from(
const {
[Beam {
position: Position { x: 0, y: 0 },
direction: Direction::Right,
}]
},
);
while beams.len() > 0 { while beams.len() > 0 {
// Step all beams // Step all beams
for mut beam in std::mem::take(&mut beams).into_iter() { for mut beam in std::mem::take(&mut beams).into_iter() {
let already_heated = heated.iter().filter(|(pos, dir)| pos == &beam.position && dir == &beam.direction).collect_vec(); let already_heated = heated
.iter()
.filter(|(pos, dir)| pos == &beam.position && dir == &beam.direction)
.collect_vec();
if already_heated.len() == 0 { if already_heated.len() == 0 {
heated.push((beam.position.clone(), beam.direction.clone())); heated.push((beam.position.clone(), beam.direction.clone()));
} else { } else {
continue; continue;
} }
match grid[beam.position.y][beam.position.x] { match grid[beam.position.y][beam.position.x] {
'/' => match beam.direction { '/' => match beam.direction {
Direction::Up => beam.direction = Direction::Right, Direction::Up => beam.direction = Direction::Right,
@ -59,29 +72,63 @@ impl Day for Day16 {
}, },
'|' => match beam.direction { '|' => match beam.direction {
Direction::Left | Direction::Right => { Direction::Left | Direction::Right => {
if beam.position.y != 0 { beams.push(Beam { position: Position { x: beam.position.x, y: beam.position.y - 1 }, direction: Direction::Up }) } if beam.position.y != 0 {
if beam.position.y != grid.len() - 1 { beams.push(Beam { position: Position { x: beam.position.x, y: beam.position.y + 1 }, direction: Direction::Down }) } beams.push(Beam {
continue position: Position {
}, x: beam.position.x,
_ => () y: beam.position.y - 1,
},
direction: Direction::Up,
})
}
if beam.position.y != grid.len() - 1 {
beams.push(Beam {
position: Position {
x: beam.position.x,
y: beam.position.y + 1,
},
direction: Direction::Down,
})
}
continue;
}
_ => (),
}, },
'-' => match beam.direction { '-' => match beam.direction {
Direction::Up | Direction::Down => { Direction::Up | Direction::Down => {
if beam.position.x != 0 { beams.push(Beam { position: Position { x: beam.position.x - 1, y: beam.position.y }, direction: Direction::Left }) } if beam.position.x != 0 {
if beam.position.x != grid[0].len() - 1 { beams.push(Beam { position: Position { x: beam.position.x + 1, y: beam.position.y }, direction: Direction::Right }) } beams.push(Beam {
continue position: Position {
}, x: beam.position.x - 1,
_ => () y: beam.position.y,
},
direction: Direction::Left,
})
}
if beam.position.x != grid[0].len() - 1 {
beams.push(Beam {
position: Position {
x: beam.position.x + 1,
y: beam.position.y,
},
direction: Direction::Right,
})
}
continue;
}
_ => (),
}, },
_ => () _ => (),
} }
match beam.direction { match beam.direction {
Direction::Up if beam.position.y != 0 => beam.position.y -= 1, Direction::Up if beam.position.y != 0 => beam.position.y -= 1,
Direction::Down if beam.position.y != grid.len() - 1 => beam.position.y += 1, Direction::Down if beam.position.y != grid.len() - 1 => beam.position.y += 1,
Direction::Right if beam.position.x != grid[0].len() - 1 => beam.position.x += 1, Direction::Right if beam.position.x != grid[0].len() - 1 => {
beam.position.x += 1
}
Direction::Left if beam.position.x != 0 => beam.position.x -= 1, Direction::Left if beam.position.x != 0 => beam.position.x -= 1,
_ => continue // If it hit the wall, skull issue, don't add it back to the vec _ => continue, // If it hit the wall, skull issue, don't add it back to the vec
} }
beams.push(beam); beams.push(beam);
@ -98,38 +145,58 @@ impl Day for Day16 {
// .join("\n") // .join("\n")
// ); // );
heated.into_iter().map(|(pos, _)| pos).sorted().dedup().count().to_string() heated
.into_iter()
.map(|(pos, _)| pos)
.sorted()
.dedup()
.count()
.to_string()
} }
// TODO Don't do this the stupid way, use DP/caching lol // TODO Don't do this the stupid way, use DP/caching lol
fn part2(&mut self) -> String { fn part2(&mut self) -> String {
let grid = self.input.lines().map(|l| l.chars().collect_vec()).collect_vec(); let grid = self
.input
.lines()
.map(|l| l.chars().collect_vec())
.collect_vec();
let mut max = 0usize; let mut max = 0usize;
for y in 0..grid.len() { for y in 0..grid.len() {
for x in 0..grid[0].len() { for x in 0..grid[0].len() {
let dir = let dir = if x == 0 {
if x == 0 { Direction::Left } Direction::Left
else if x == grid[0].len() - 1 { Direction::Right } } else if x == grid[0].len() - 1 {
else if y == 0 { Direction::Down } Direction::Right
else if y == grid.len() - 1 { Direction::Up } } else if y == 0 {
else { continue }; Direction::Down
} else if y == grid.len() - 1 {
Direction::Up
} else {
continue;
};
let mut heated = Vec::<(Position, Direction)>::new(); let mut heated = Vec::<(Position, Direction)>::new();
let mut beams = Vec::from([Beam { position: Position { x, y }, direction: dir }]); let mut beams = Vec::from([Beam {
position: Position { x, y },
direction: dir,
}]);
while beams.len() > 0 { while beams.len() > 0 {
// Step all beams // Step all beams
for mut beam in std::mem::take(&mut beams).into_iter() { for mut beam in std::mem::take(&mut beams).into_iter() {
let already_heated = heated.iter().filter(|(pos, dir)| pos == &beam.position && dir == &beam.direction).collect_vec(); let already_heated = heated
.iter()
.filter(|(pos, dir)| pos == &beam.position && dir == &beam.direction)
.collect_vec();
if already_heated.len() == 0 { if already_heated.len() == 0 {
heated.push((beam.position.clone(), beam.direction.clone())); heated.push((beam.position.clone(), beam.direction.clone()));
} else { } else {
continue; continue;
} }
match grid[beam.position.y][beam.position.x] { match grid[beam.position.y][beam.position.x] {
'/' => match beam.direction { '/' => match beam.direction {
Direction::Up => beam.direction = Direction::Right, Direction::Up => beam.direction = Direction::Right,
@ -145,29 +212,65 @@ impl Day for Day16 {
}, },
'|' => match beam.direction { '|' => match beam.direction {
Direction::Left | Direction::Right => { Direction::Left | Direction::Right => {
if beam.position.y != 0 { beams.push(Beam { position: Position { x: beam.position.x, y: beam.position.y - 1 }, direction: Direction::Up }) } if beam.position.y != 0 {
if beam.position.y != grid.len() - 1 { beams.push(Beam { position: Position { x: beam.position.x, y: beam.position.y + 1 }, direction: Direction::Down }) } beams.push(Beam {
continue position: Position {
}, x: beam.position.x,
_ => () y: beam.position.y - 1,
},
direction: Direction::Up,
})
}
if beam.position.y != grid.len() - 1 {
beams.push(Beam {
position: Position {
x: beam.position.x,
y: beam.position.y + 1,
},
direction: Direction::Down,
})
}
continue;
}
_ => (),
}, },
'-' => match beam.direction { '-' => match beam.direction {
Direction::Up | Direction::Down => { Direction::Up | Direction::Down => {
if beam.position.x != 0 { beams.push(Beam { position: Position { x: beam.position.x - 1, y: beam.position.y }, direction: Direction::Left }) } if beam.position.x != 0 {
if beam.position.x != grid[0].len() - 1 { beams.push(Beam { position: Position { x: beam.position.x + 1, y: beam.position.y }, direction: Direction::Right }) } beams.push(Beam {
continue position: Position {
}, x: beam.position.x - 1,
_ => () y: beam.position.y,
},
direction: Direction::Left,
})
}
if beam.position.x != grid[0].len() - 1 {
beams.push(Beam {
position: Position {
x: beam.position.x + 1,
y: beam.position.y,
},
direction: Direction::Right,
})
}
continue;
}
_ => (),
}, },
_ => () _ => (),
} }
match beam.direction { match beam.direction {
Direction::Up if beam.position.y != 0 => beam.position.y -= 1, Direction::Up if beam.position.y != 0 => beam.position.y -= 1,
Direction::Down if beam.position.y != grid.len() - 1 => beam.position.y += 1, Direction::Down if beam.position.y != grid.len() - 1 => {
Direction::Right if beam.position.x != grid[0].len() - 1 => beam.position.x += 1, beam.position.y += 1
}
Direction::Right if beam.position.x != grid[0].len() - 1 => {
beam.position.x += 1
}
Direction::Left if beam.position.x != 0 => beam.position.x -= 1, Direction::Left if beam.position.x != 0 => beam.position.x -= 1,
_ => continue // If it hit the wall, skull issue, don't add it back to the vec _ => continue, // If it hit the wall, skull issue, don't add it back to the vec
} }
beams.push(beam); beams.push(beam);
@ -184,8 +287,15 @@ impl Day for Day16 {
// .join("\n") // .join("\n")
// ); // );
let result = heated.into_iter().map(|(pos, _)| pos).sorted().dedup().count(); let result = heated
if max < result { max = result; } .into_iter()
.map(|(pos, _)| pos)
.sorted()
.dedup()
.count();
if max < result {
max = result;
}
} }
} }