Skip to content

Commit a3787b4

Browse files
committed
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 rust-lang/rfcs#2023
1 parent 89aef0c commit a3787b4

File tree

1 file changed

+41
-2
lines changed

1 file changed

+41
-2
lines changed

src/bin/03/main.rs

+41-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::{HashMap, HashSet};
22
use anyhow::Result;
33

4+
#[cfg(not(feature="timing"))]
45
fn main() -> Result<()> {
56
let mut misplaced_sum = 0;
67
let mut badge_sum = 0;
@@ -15,13 +16,34 @@ fn main() -> Result<()> {
1516
Ok(())
1617
}
1718

19+
// Benchmark for the intersection functions
20+
#[cfg(feature="timing")]
21+
fn main() -> Result<()> {
22+
use advent_2022::terminal::elapsed;
23+
let str_len = 10000;
24+
let vec_len = 1000;
25+
let group: Vec<_> = include_str!("example.txt").lines().take(3)
26+
.map(|e| e.chars().cycle().take(str_len).collect::<String>())
27+
.collect();
28+
29+
for i in 1..=group.len() {
30+
let repeated: Vec<_> = group[..i].iter().map(|s| s.as_str()).cycle().take(vec_len).collect();
31+
elapsed!(format!("all_1 subset {}", i), intersect_all_1(&repeated).len());
32+
elapsed!(format!("all_2 subset {}", i), intersect_all_2(&repeated).len());
33+
elapsed!(format!("all_3 subset {}", i), intersect_all_3(&repeated).len());
34+
println!();
35+
}
36+
37+
Ok(())
38+
}
39+
1840
fn misplaced_item(elf: &str) -> HashSet<char> {
1941
let left = &elf[..elf.len()/2];
2042
let right = &elf[elf.len()/2..];
2143
intersect_all_2(&[left, right])
2244
}
2345

24-
#[cfg(test)]
46+
#[cfg(any(test,feature="timing"))]
2547
fn intersect_all_1(inputs: &[&str]) -> HashSet<char> {
2648
let packs: Vec<_> = inputs.iter().map(|p| p.chars().collect::<HashSet<_>>()).collect();
2749
let all_items: HashSet<_> = inputs.iter().flat_map(|p| p.chars()).collect();
@@ -39,13 +61,29 @@ fn intersect_all_2(inputs: &[&str]) -> HashSet<char> {
3961
*v = keep;
4062
}
4163
}
42-
// could also set v=false here and not invert the keep variable, guessing it'd be slower
4364
intersection.retain(|_, &mut v| v == keep);
4465
keep = !keep;
4566
}
4667
intersection.into_keys().collect()
4768
}
4869

70+
// same as all_2 but utilizes .retain()'s mutable value; marginally slower than all_2
71+
#[cfg(any(test,feature="timing"))]
72+
fn intersect_all_3(inputs: &[&str]) -> HashSet<char> {
73+
if inputs.is_empty() { return HashSet::new(); }
74+
let mut inputs = inputs.iter();
75+
let mut intersection: HashMap<_,_> = inputs.next().expect("non-empty").chars().map(|c| (c, false)).collect();
76+
for input in inputs {
77+
for c in input.chars() {
78+
if let Some(v) = intersection.get_mut(&c) {
79+
*v = true;
80+
}
81+
}
82+
intersection.retain(|_, v| { let ret = *v; *v = false; ret });
83+
}
84+
intersection.into_keys().collect()
85+
}
86+
4987
fn score_set(common: &HashSet<char>) -> u32 {
5088
assert_eq!(common.len(), 1);
5189
score(*common.iter().next().expect("non-empty"))
@@ -90,5 +128,6 @@ mod tests {
90128
intersect_impls! {
91129
all_1: intersect_all_1,
92130
all_2: intersect_all_2,
131+
all_3: intersect_all_3,
93132
}
94133
}

0 commit comments

Comments
 (0)