From 93af37257486ad4e6ad9483d3a21263b98a15255 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 4 Dec 2023 20:35:59 +0100 Subject: [PATCH] properly speedup part2 --- 2023/day01/src/lib.rs | 2 +- 2023/day04/src/lib.rs | 52 ++++++++++++++++++++++++++------------- 2023/day04/src/p2cache.rs | 31 +++++++++++++++++++++++ helper/src/lib.rs | 41 +++++++++++++++++++----------- 4 files changed, 94 insertions(+), 32 deletions(-) create mode 100644 2023/day04/src/p2cache.rs diff --git a/2023/day01/src/lib.rs b/2023/day01/src/lib.rs index 94ee6de..2f76185 100644 --- a/2023/day01/src/lib.rs +++ b/2023/day01/src/lib.rs @@ -20,7 +20,7 @@ helper::define_variants! { basic => crate::part1; } part2 { - naive => crate::naive::part2; + naive => crate::naive::part2, sample_count=1000; zero_alloc => crate::zero_alloc::part2; branchless => |i| unsafe { crate::branchless::part2(i) }; no_lines => |i| unsafe { crate::no_lines::part2(i) }; diff --git a/2023/day04/src/lib.rs b/2023/day04/src/lib.rs index f3a0284..cbd9fd0 100644 --- a/2023/day04/src/lib.rs +++ b/2023/day04/src/lib.rs @@ -1,3 +1,5 @@ +mod p2cache; + use helper::{Day, Variants}; pub fn main() { @@ -9,10 +11,11 @@ struct Day04; helper::define_variants! { day => crate::Day04; part1 { - basic => crate::part1; + basic => crate::part1, sample_count=1000; } part2 { - basic => crate::part2; + basic => crate::part2, sample_count=100; + cache => crate::p2cache::part2, sample_count=1000; } } @@ -26,21 +29,25 @@ impl Day for Day04 { } } +fn line_match_count(line: &str) -> usize { + let mut numbers = line.split(':').nth(1).unwrap().split("|"); + let winning = numbers + .next() + .unwrap() + .split_whitespace() + .collect::>(); + let you_have = numbers.next().unwrap().split_whitespace(); + + you_have + .filter(|have| winning.iter().any(|w| w == have)) + .count() +} + fn part1(input: &str) -> u64 { input .lines() .map(|line| { - let mut numbers = line.split(':').nth(1).unwrap().split("|"); - let winning = numbers - .next() - .unwrap() - .split_whitespace() - .collect::>(); - let you_have = numbers.next().unwrap().split_whitespace(); - - let win_amount = you_have - .filter(|have| winning.iter().any(|w| w == have)) - .count(); + let win_amount = line_match_count(line); if win_amount > 0 { 1 << (win_amount - 1) @@ -51,8 +58,19 @@ fn part1(input: &str) -> u64 { .sum() } -fn part2(_input: &str) -> u64 { - 0 +fn part2(input: &str) -> u64 { + let lines = input.lines().map(line_match_count).collect::>(); + let mut processed = 0; + + let mut todo = (0..lines.len()).collect::>(); + + while let Some(line) = todo.pop() { + let matches = lines[line]; + todo.extend((line + 1)..(line + 1 + matches)); + processed += 1; + } + + processed } helper::tests! { @@ -62,8 +80,8 @@ helper::tests! { default => 24733; } part2 { - small => 0; - default => 0; + small => 30; + default => 5422730; } } helper::benchmarks! {} diff --git a/2023/day04/src/p2cache.rs b/2023/day04/src/p2cache.rs new file mode 100644 index 0000000..6c69c7d --- /dev/null +++ b/2023/day04/src/p2cache.rs @@ -0,0 +1,31 @@ +pub fn part2(input: &str) -> u64 { + let lines = input + .lines() + .map(crate::line_match_count) + .collect::>(); + + let mut cache = vec![0; lines.len()]; + + let mut processed = 0; + + // By iterating backwards, we ensure the cache is always populated for every subsequent line. + for (i, result) in lines.iter().copied().enumerate().rev() { + let before = processed; + processed += 1; // Every card gives us one card. + // Now, let's see how many cards this card will expand to. + for expand in (i + 1)..((i + 1) + result) { + #[cfg(debug_assertions)] + eprintln!( + "{} expands to {} which is worth {}", + i + 1, + expand + 1, + cache[expand] + ); + // Since the value is bigger than i, it must be cached! + processed += cache[expand]; + } + cache[i] = processed - before; + } + + processed +} diff --git a/helper/src/lib.rs b/helper/src/lib.rs index 1d5aef6..adeaff1 100644 --- a/helper/src/lib.rs +++ b/helper/src/lib.rs @@ -72,20 +72,20 @@ macro_rules! define_variants { ( day => $day:ty; part1 { - $( $name1:ident => $func1:expr; )* + $( $name1:ident => $func1:expr $(, sample_count=$sample_count1:expr)? ; )* } part2 { - $( $name2:ident => $func2:expr; )* + $( $name2:ident => $func2:expr $(, sample_count=$sample_count2:expr)? ; )* } ) => { macro_rules! part1_variants { ($macro:ident) => { - $crate::$macro! { $day; $( ($name1, $func1) ),* } + $crate::$macro! { $day; $( ($name1, $func1, [ $( sample_count=$sample_count1, )? ]) ),* } }; } macro_rules! part2_variants { ($macro:ident) => { - $crate::$macro! { $day; $( ($name2, $func2) ),* } + $crate::$macro! { $day; $( ($name2, $func2, [ $( sample_count=$sample_count2, )? ]) ),* } }; } }; @@ -93,7 +93,7 @@ macro_rules! define_variants { #[macro_export] macro_rules! construct_variants { - ( $day:ty; $( ($name:ident, $func:expr) ),*) => { + ( $day:ty; $( ($name:ident, $func:expr, [ $($_:tt)* ]) ),*) => { $crate::Variants { variants: vec![$( $crate::Variant::new(stringify!($name), $func) @@ -105,7 +105,6 @@ macro_rules! construct_variants { #[macro_export] macro_rules! benchmarks { () => { - #[::divan::bench_group(sample_count = 10_000)] mod bench { mod part1 { part1_variants! { _define_benchmarks } @@ -122,15 +121,29 @@ macro_rules! benchmarks { } #[macro_export] -macro_rules! _define_benchmarks { - ($day:ty; $( ($name:ident, $func:expr) ),*) => { - $( - #[::divan::bench] - fn $name(bencher: ::divan::Bencher) { - let input = include_str!("../input.txt"); - let input = <$day as $crate::Day>::pad_input(input); +macro_rules! _bench_sample_count { + (;$($tt:tt)*) => { + #[::divan::bench(sample_count = 10_000)] + $($tt)* + }; + ($sample_count:expr; $($tt:tt)*) => { + #[::divan::bench(sample_count = $sample_count)] + $($tt)* + }; +} - bencher.with_inputs(|| input.as_ref()).bench_values($func); +#[macro_export] +macro_rules! _define_benchmarks { + ($day:ty; $( ($name:ident, $func:expr, [ $(sample_count=$sample_count:expr,)? ]) ),*) => { + $( + $crate::_bench_sample_count! { + $($sample_count)?; + fn $name(bencher: ::divan::Bencher) { + let input = include_str!("../input.txt"); + let input = <$day as $crate::Day>::pad_input(input); + + bencher.with_inputs(|| input.as_ref()).bench_values($func); + } } )* };