From a3787b448c0b03ff044b8f42c56c11f657309a04 Mon Sep 17 00:00:00 2001 From: Michael Diamond Date: Sat, 3 Dec 2022 13:14:49 -0800 Subject: [PATCH] Day 3: Add basic benchmark for different intersection functions. Using a fold is noticably slower than maintaining a mutable map of seen values. See also https://github.com/rust-lang/rfcs/issues/2023 --- src/bin/03/main.rs | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/bin/03/main.rs b/src/bin/03/main.rs index c2d3aee..6f9ed75 100644 --- a/src/bin/03/main.rs +++ b/src/bin/03/main.rs @@ -1,6 +1,7 @@ use std::collections::{HashMap, HashSet}; use anyhow::Result; +#[cfg(not(feature="timing"))] fn main() -> Result<()> { let mut misplaced_sum = 0; let mut badge_sum = 0; @@ -15,13 +16,34 @@ fn main() -> Result<()> { Ok(()) } +// Benchmark for the intersection functions +#[cfg(feature="timing")] +fn main() -> Result<()> { + use advent_2022::terminal::elapsed; + let str_len = 10000; + let vec_len = 1000; + let group: Vec<_> = include_str!("example.txt").lines().take(3) + .map(|e| e.chars().cycle().take(str_len).collect::()) + .collect(); + + for i in 1..=group.len() { + let repeated: Vec<_> = group[..i].iter().map(|s| s.as_str()).cycle().take(vec_len).collect(); + elapsed!(format!("all_1 subset {}", i), intersect_all_1(&repeated).len()); + elapsed!(format!("all_2 subset {}", i), intersect_all_2(&repeated).len()); + elapsed!(format!("all_3 subset {}", i), intersect_all_3(&repeated).len()); + println!(); + } + + Ok(()) +} + fn misplaced_item(elf: &str) -> HashSet { let left = &elf[..elf.len()/2]; let right = &elf[elf.len()/2..]; intersect_all_2(&[left, right]) } -#[cfg(test)] +#[cfg(any(test,feature="timing"))] fn intersect_all_1(inputs: &[&str]) -> HashSet { let packs: Vec<_> = inputs.iter().map(|p| p.chars().collect::>()).collect(); let all_items: HashSet<_> = inputs.iter().flat_map(|p| p.chars()).collect(); @@ -39,13 +61,29 @@ fn intersect_all_2(inputs: &[&str]) -> HashSet { *v = keep; } } - // could also set v=false here and not invert the keep variable, guessing it'd be slower intersection.retain(|_, &mut v| v == keep); keep = !keep; } intersection.into_keys().collect() } +// same as all_2 but utilizes .retain()'s mutable value; marginally slower than all_2 +#[cfg(any(test,feature="timing"))] +fn intersect_all_3(inputs: &[&str]) -> HashSet { + if inputs.is_empty() { return HashSet::new(); } + let mut inputs = inputs.iter(); + let mut intersection: HashMap<_,_> = inputs.next().expect("non-empty").chars().map(|c| (c, false)).collect(); + for input in inputs { + for c in input.chars() { + if let Some(v) = intersection.get_mut(&c) { + *v = true; + } + } + intersection.retain(|_, v| { let ret = *v; *v = false; ret }); + } + intersection.into_keys().collect() +} + fn score_set(common: &HashSet) -> u32 { assert_eq!(common.len(), 1); score(*common.iter().next().expect("non-empty")) @@ -90,5 +128,6 @@ mod tests { intersect_impls! { all_1: intersect_all_1, all_2: intersect_all_2, + all_3: intersect_all_3, } }