From d1dcb5134a9b13f9a6151512110936427d955f06 Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Tue, 7 Dec 2021 22:28:46 -0700 Subject: [PATCH] misc optimizations --- Cargo.toml | 5 + flamegraph.svg | 414 +++++++++++++++++++++++++++++++++++++++++++++++++ src/day02.rs | 25 +-- src/day03.rs | 4 +- src/day05.rs | 71 ++++++--- src/day07.rs | 6 +- src/main.rs | 39 +++-- 7 files changed, 516 insertions(+), 48 deletions(-) create mode 100644 flamegraph.svg diff --git a/Cargo.toml b/Cargo.toml index bb5bf12..04fca26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,8 @@ edition = "2021" anyhow = "1.0" cached = "0.26.2" itertools = "0.10.1" + +[profile.release] +lto = true +codegen-units = 1 +panic = "abort" diff --git a/flamegraph.svg b/flamegraph.svg new file mode 100644 index 0000000..676837a --- /dev/null +++ b/flamegraph.svg @@ -0,0 +1,414 @@ +Flame Graph Reset ZoomSearch aoc2021`<core::iter::adapters::flatten::Flatten<I> as core::iter::traits::iterator::Iterator>::next (17 samples, 0.05%)libsystem_malloc.dylib`malloc_zone_realloc (7 samples, 0.02%)libsystem_malloc.dylib`szone_realloc (5 samples, 0.01%)aoc2021`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (9 samples, 0.02%)aoc2021`alloc::raw_vec::finish_grow (9 samples, 0.02%)libsystem_malloc.dylib`realloc (8 samples, 0.02%)aoc2021`core::str::iter::SplitInternal<P>::next (205 samples, 0.55%)libsystem_platform.dylib`_platform_memcmp (24 samples, 0.06%)aoc2021`aoc2021::day01::run (391 samples, 1.05%)libsystem_platform.dylib`_platform_memcmp (15 samples, 0.04%)aoc2021`aoc2021::day02::parse_line (22 samples, 0.06%)aoc2021`DYLD-STUB$$memcmp (7 samples, 0.02%)aoc2021`aoc2021::day02::parse_line (173 samples, 0.46%)aoc2021`core::str::iter::SplitInternal<P>::next (411 samples, 1.10%)libsystem_platform.dylib`_platform_memcmp (20 samples, 0.05%)aoc2021`aoc2021::day02::run (649 samples, 1.74%)libsystem_platform.dylib`_platform_memcmp (14 samples, 0.04%)aoc2021`DYLD-STUB$$memcmp (13 samples, 0.03%)libsystem_malloc.dylib`tiny_try_realloc_in_place (6 samples, 0.02%)libsystem_malloc.dylib`malloc_zone_realloc (14 samples, 0.04%)libsystem_malloc.dylib`szone_realloc (13 samples, 0.03%)aoc2021`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (23 samples, 0.06%)aoc2021`alloc::raw_vec::finish_grow (22 samples, 0.06%)libsystem_malloc.dylib`realloc (19 samples, 0.05%)libsystem_malloc.dylib`szone_size (4 samples, 0.01%)libsystem_malloc.dylib`tiny_size (4 samples, 0.01%)aoc2021`core::str::iter::SplitInternal<P>::next (288 samples, 0.77%)libsystem_platform.dylib`_platform_memcmp (22 samples, 0.06%)libsystem_malloc.dylib`small_malloc_should_clear (11 samples, 0.03%)libsystem_malloc.dylib`small_malloc_from_free_list (7 samples, 0.02%)libsystem_malloc.dylib`_malloc_zone_malloc (30 samples, 0.08%)libsystem_malloc.dylib`szone_malloc_should_clear (27 samples, 0.07%)libsystem_malloc.dylib`tiny_malloc_should_clear (16 samples, 0.04%)libsystem_malloc.dylib`tiny_malloc_from_free_list (8 samples, 0.02%)libsystem_malloc.dylib`tiny_free_list_add_ptr (5 samples, 0.01%)libsystem_malloc.dylib`free (7 samples, 0.02%)libsystem_malloc.dylib`szone_size (6 samples, 0.02%)libsystem_malloc.dylib`tiny_size (4 samples, 0.01%)libsystem_malloc.dylib`free_tiny (14 samples, 0.04%)libsystem_malloc.dylib`tiny_free_no_lock (10 samples, 0.03%)aoc2021`aoc2021::day03::find_oxygen_or_co2 (799 samples, 2.14%)a..libsystem_platform.dylib`_platform_memcmp (11 samples, 0.03%)aoc2021`core::str::iter::SplitInternal<P>::next (467 samples, 1.25%)libsystem_platform.dylib`_platform_memcmp (25 samples, 0.07%)libsystem_malloc.dylib`_malloc_zone_calloc (4 samples, 0.01%)aoc2021`aoc2021::day03::run (1,798 samples, 4.81%)aoc202..libsystem_platform.dylib`_platform_memcmp (16 samples, 0.04%)aoc2021`<core::str::iter::SplitWhitespace as core::iter::traits::iterator::Iterator>::next (378 samples, 1.01%)aoc2021`core::str::iter::SplitInternal<P>::next (263 samples, 0.70%)libsystem_platform.dylib`_platform_memcmp (16 samples, 0.04%)aoc2021`<core::iter::adapters::flatten::Flatten<I> as core::iter::traits::iterator::Iterator>::next (928 samples, 2.48%)ao..libsystem_platform.dylib`_platform_memcmp (7 samples, 0.02%)aoc2021`<core::str::iter::SplitWhitespace as core::iter::traits::iterator::Iterator>::next (20 samples, 0.05%)libsystem_malloc.dylib`free_tiny (6 samples, 0.02%)libsystem_malloc.dylib`set_tiny_meta_header_in_use (4 samples, 0.01%)libsystem_malloc.dylib`small_try_realloc_in_place (9 samples, 0.02%)libsystem_malloc.dylib`szone_malloc_should_clear (6 samples, 0.02%)libsystem_malloc.dylib`small_malloc_should_clear (5 samples, 0.01%)libsystem_malloc.dylib`tiny_try_realloc_in_place (9 samples, 0.02%)libsystem_malloc.dylib`szone_realloc (40 samples, 0.11%)libsystem_malloc.dylib`malloc_zone_realloc (42 samples, 0.11%)libsystem_malloc.dylib`realloc (53 samples, 0.14%)libsystem_malloc.dylib`szone_size (8 samples, 0.02%)libsystem_malloc.dylib`tiny_size (7 samples, 0.02%)aoc2021`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (56 samples, 0.15%)aoc2021`alloc::raw_vec::finish_grow (56 samples, 0.15%)aoc2021`core::str::iter::SplitInternal<P>::next (32 samples, 0.09%)libsystem_malloc.dylib`set_tiny_meta_header_in_use (21 samples, 0.06%)libsystem_malloc.dylib`_malloc_zone_malloc (119 samples, 0.32%)libsystem_malloc.dylib`szone_malloc_should_clear (109 samples, 0.29%)libsystem_malloc.dylib`tiny_malloc_should_clear (99 samples, 0.26%)libsystem_malloc.dylib`tiny_malloc_from_free_list (23 samples, 0.06%)libsystem_malloc.dylib`szone_size (14 samples, 0.04%)libsystem_malloc.dylib`tiny_size (11 samples, 0.03%)libsystem_malloc.dylib`free (28 samples, 0.07%)libsystem_malloc.dylib`tiny_free_list_remove_ptr (4 samples, 0.01%)libsystem_malloc.dylib`tiny_free_list_add_ptr (30 samples, 0.08%)libsystem_malloc.dylib`free_tiny (95 samples, 0.25%)libsystem_malloc.dylib`tiny_free_no_lock (67 samples, 0.18%)libsystem_malloc.dylib`tiny_free_list_remove_ptr (11 samples, 0.03%)aoc2021`aoc2021::day04::find_winning_score (13,921 samples, 37.22%)aoc2021`aoc2021::day04::find_winning_scorelibsystem_platform.dylib`_platform_memmove$VARIANT$Haswell (13 samples, 0.03%)aoc2021`<core::str::iter::SplitWhitespace as core::iter::traits::iterator::Iterator>::next (155 samples, 0.41%)aoc2021`core::str::iter::SplitInternal<P>::next (269 samples, 0.72%)libsystem_platform.dylib`_platform_memcmp (10 samples, 0.03%)aoc2021`<core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::next (586 samples, 1.57%)libsystem_platform.dylib`_platform_memcmp (6 samples, 0.02%)aoc2021`<core::str::iter::SplitWhitespace as core::iter::traits::iterator::Iterator>::next (5 samples, 0.01%)aoc2021`DYLD-STUB$$free (5 samples, 0.01%)libsystem_malloc.dylib`malloc_zone_realloc (12 samples, 0.03%)libsystem_malloc.dylib`szone_realloc (12 samples, 0.03%)aoc2021`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (14 samples, 0.04%)aoc2021`alloc::raw_vec::finish_grow (14 samples, 0.04%)libsystem_malloc.dylib`realloc (14 samples, 0.04%)libsystem_platform.dylib`_platform_bzero$VARIANT$Haswell (8 samples, 0.02%)libsystem_malloc.dylib`_malloc_zone_calloc (11 samples, 0.03%)libsystem_malloc.dylib`szone_malloc_should_clear (11 samples, 0.03%)libsystem_malloc.dylib`small_malloc_should_clear (11 samples, 0.03%)libsystem_malloc.dylib`small_malloc_should_clear (6 samples, 0.02%)libsystem_malloc.dylib`small_malloc_from_free_list (6 samples, 0.02%)libsystem_malloc.dylib`small_free_list_remove_ptr (8 samples, 0.02%)libsystem_malloc.dylib`small_free_list_remove_ptr_no_clear (5 samples, 0.01%)libsystem_malloc.dylib`small_free_list_add_ptr (202 samples, 0.54%)libsystem_malloc.dylib`small_free_list_remove_ptr (76 samples, 0.20%)libsystem_malloc.dylib`_malloc_zone_malloc (2,300 samples, 6.15%)libsyste..libsystem_malloc.dylib`szone_malloc_should_clear (2,110 samples, 5.64%)libsyst..libsystem_malloc.dylib`small_malloc_should_clear (2,043 samples, 5.46%)libsyst..libsystem_malloc.dylib`small_malloc_from_free_list (582 samples, 1.56%)libsystem_malloc.dylib`small_free_list_remove_ptr_no_clear (127 samples, 0.34%)libsystem_malloc.dylib`default_zone_malloc (265 samples, 0.71%)libsystem_malloc.dylib`small_size (44 samples, 0.12%)libsystem_malloc.dylib`szone_size (55 samples, 0.15%)libsystem_malloc.dylib`tiny_size (30 samples, 0.08%)libsystem_malloc.dylib`free (191 samples, 0.51%)libsystem_malloc.dylib`tiny_size (7 samples, 0.02%)libsystem_malloc.dylib`small_free_list_add_ptr (135 samples, 0.36%)libsystem_malloc.dylib`small_free_list_find_by_ptr (20 samples, 0.05%)libsystem_malloc.dylib`small_free_list_remove_ptr (37 samples, 0.10%)libsystem_malloc.dylib`free_small (792 samples, 2.12%)l..libsystem_malloc.dylib`small_free_list_remove_ptr_no_clear (151 samples, 0.40%)libsystem_malloc.dylib`small_free_list_add_ptr (11 samples, 0.03%)libsystem_malloc.dylib`small_free_list_find_by_ptr (8 samples, 0.02%)libsystem_malloc.dylib`small_free_list_remove_ptr (7 samples, 0.02%)libsystem_malloc.dylib`szone_malloc (7 samples, 0.02%)libsystem_malloc.dylib`szone_malloc_should_clear (6 samples, 0.02%)libsystem_malloc.dylib`szone_size (6 samples, 0.02%)aoc2021`aoc2021::day05::count_overlapped_cells (17,916 samples, 47.90%)aoc2021`aoc2021::day05::count_overlapped_cellslibsystem_platform.dylib`_platform_memmove$VARIANT$Haswell (4,248 samples, 11.36%)libsystem_platfor..aoc2021`core::str::iter::SplitInternal<P>::next (13 samples, 0.03%)libsystem_malloc.dylib`_malloc_zone_malloc (18 samples, 0.05%)libsystem_malloc.dylib`free (4 samples, 0.01%)libsystem_malloc.dylib`malloc (20 samples, 0.05%)libsystem_malloc.dylib`szone_free_definite_size (35 samples, 0.09%)aoc2021`aoc2021::day05::run (18,652 samples, 49.87%)aoc2021`aoc2021::day05::runlibsystem_platform.dylib`_platform_memmove$VARIANT$Haswell (20 samples, 0.05%)aoc2021`hashbrown::map::make_hash (84 samples, 0.22%)aoc2021`core::hash::impls::_<impl core::hash::Hash for isize>::hash (48 samples, 0.13%)libsystem_pthread.dylib`pthread_mutex_lock (42 samples, 0.11%)aoc2021`aoc2021::day06::count_babies (193 samples, 0.52%)libsystem_pthread.dylib`pthread_mutex_unlock (34 samples, 0.09%)aoc2021`core::slice::sort::recurse (371 samples, 0.99%)aoc2021`core::slice::sort::recurse (311 samples, 0.83%)aoc2021`core::slice::sort::recurse (199 samples, 0.53%)aoc2021`core::slice::sort::recurse (86 samples, 0.23%)aoc2021`core::slice::sort::recurse (25 samples, 0.07%)aoc2021`aoc2021::day07::find_cheapest_location (1,319 samples, 3.53%)aoc..libsystem_platform.dylib`_platform_memmove$VARIANT$Haswell (4 samples, 0.01%)libsystem_malloc.dylib`malloc_zone_realloc (14 samples, 0.04%)libsystem_malloc.dylib`szone_realloc (11 samples, 0.03%)libsystem_malloc.dylib`tiny_try_realloc_in_place (5 samples, 0.01%)aoc2021`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (19 samples, 0.05%)aoc2021`alloc::raw_vec::finish_grow (19 samples, 0.05%)libsystem_malloc.dylib`realloc (17 samples, 0.05%)aoc2021`core::str::iter::SplitInternal<P>::next (158 samples, 0.42%)libsystem_platform.dylib`_platform_memcmp (16 samples, 0.04%)aoc2021`aoc2021::utils::parse_by_char (289 samples, 0.77%)aoc2021`aoc2021::day07::run (1,614 samples, 4.32%)aoc20..aoc2021`core::str::iter::SplitInternal<P>::next (4 samples, 0.01%)libsystem_malloc.dylib`szone_malloc_should_clear (4 samples, 0.01%)libsystem_malloc.dylib`tiny_malloc_should_clear (4 samples, 0.01%)libsystem_malloc.dylib`_malloc_zone_malloc (5 samples, 0.01%)libsystem_malloc.dylib`malloc_zone_realloc (8 samples, 0.02%)libsystem_malloc.dylib`szone_realloc (8 samples, 0.02%)aoc2021`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (14 samples, 0.04%)aoc2021`alloc::raw_vec::finish_grow (14 samples, 0.04%)libsystem_malloc.dylib`realloc (9 samples, 0.02%)aoc2021`core::str::iter::SplitInternal<P>::next (24 samples, 0.06%)aoc2021`aoc2021::utils::parse_by_char (58 samples, 0.16%)aoc2021`core::str::iter::SplitInternal<P>::next (50 samples, 0.13%)libsystem_pthread.dylib`pthread_mutex_lock (5 samples, 0.01%)aoc2021`main (37,395 samples, 99.98%)aoc2021`mainaoc2021`std::sys_common::backtrace::__rust_begin_short_backtrace (37,395 samples, 99.98%)aoc2021`std::sys_common::backtrace::__rust_begin_short_backtraceaoc2021`aoc2021::main (37,393 samples, 99.97%)aoc2021`aoc2021::mainall (37,403 samples, 100%)0x1 (37,403 samples, 100.00%)0x1libdyld.dylib`start (37,403 samples, 100.00%)libdyld.dylib`startlibsystem_kernel.dylib`__exit (8 samples, 0.02%) \ No newline at end of file diff --git a/src/day02.rs b/src/day02.rs index 72a39dc..1c1b66c 100644 --- a/src/day02.rs +++ b/src/day02.rs @@ -1,18 +1,23 @@ use anyhow::Result; -fn parse_line(s: &str) -> (&str, i32) { - let parsed: Vec<&str> = s.splitn(2, ' ').collect(); - let instruction = parsed[0]; - let quantity = parsed[1].parse::().unwrap(); +fn parse_line(s: &str) -> (char, i32) { + let mut parsed = s.split_ascii_whitespace(); + let instruction = match parsed.next().unwrap() { + "forward" => 'f', + "down" => 'd', + "up" => 'u', + _ => unreachable!(), + }; + let quantity = parsed.next().unwrap().parse::().unwrap(); (instruction, quantity) } fn follow_course(s: &str) -> i32 { let (mut pos, mut depth) = (0, 0); s.lines().for_each(|line| match parse_line(line) { - ("forward", d) => pos += d, - ("down", d) => depth += d, - ("up", d) => depth -= d, + ('f', d) => pos += d, + ('d', d) => depth += d, + ('u', d) => depth -= d, _ => unreachable!(), }); pos * depth @@ -21,12 +26,12 @@ fn follow_course(s: &str) -> i32 { fn follow_course_better(s: &str) -> i32 { let (mut pos, mut depth, mut aim) = (0, 0, 0); s.lines().for_each(|line| match parse_line(line) { - ("forward", d) => { + ('f', d) => { pos += d; depth += aim * d; } - ("down", d) => aim += d, - ("up", d) => aim -= d, + ('d', d) => aim += d, + ('u', d) => aim -= d, _ => unreachable!(), }); pos * depth diff --git a/src/day03.rs b/src/day03.rs index 27964e0..f4b1c0f 100644 --- a/src/day03.rs +++ b/src/day03.rs @@ -39,8 +39,8 @@ fn find_oxygen_or_co2(s: &str, oxygen: bool) -> usize { .rev() .map(|bit| { // split current set into ones/zeroes for current bit - let mut ones = vec![]; - let mut zeroes = vec![]; + let mut ones = Vec::with_capacity(kept.len()); + let mut zeroes = Vec::with_capacity(kept.len()); kept.iter().for_each(|x| { if x & (1 << bit) != 0 { ones.push(*x); diff --git a/src/day05.rs b/src/day05.rs index b178f89..9a501c3 100644 --- a/src/day05.rs +++ b/src/day05.rs @@ -12,17 +12,40 @@ impl LineSegment { self.start.0 == self.end.0 || self.start.1 == self.end.1 } - fn rasterize(&self) -> Vec<(isize, isize)> { - let xstep = (self.end.0 - self.start.0).signum(); - let ystep = (self.end.1 - self.start.1).signum(); - let mut ret = vec![self.start]; - let (mut x, mut y) = self.start; - while (x, y) != self.end { - x += xstep; - y += ystep; - ret.push((x, y)); + fn rasterize(&self) -> LineSegmentPixelIter { + let dx = self.end.0 - self.start.0; + let dy = self.end.1 - self.start.1; + LineSegmentPixelIter { + x: self.start.0, + y: self.start.1, + xstep: dx.signum(), + ystep: dy.signum(), + length: dx.abs().max(dy.abs()) + 1, + i: 0, + } + } +} + +struct LineSegmentPixelIter { + x: isize, + y: isize, + xstep: isize, + ystep: isize, + length: isize, + i: isize, +} + +impl Iterator for LineSegmentPixelIter { + type Item = (isize, isize); + + fn next(&mut self) -> Option { + if self.i < self.length { + let ret = (self.x + self.i * self.xstep, self.y + self.i * self.ystep); + self.i += 1; + Some(ret) + } else { + None } - ret } } @@ -50,21 +73,23 @@ fn parse_lines(input: &str) -> Vec { } fn count_overlapped_cells(segments: &[LineSegment], includ_diag: bool) -> usize { - let mut marks: Vec<(isize, isize)> = segments + let mut cells: Vec> = vec![vec![0; 1000]; 1000]; + segments .iter() .filter(|s| s.is_horizontal_or_vertical() || includ_diag) - .map(|s| s.rasterize()) - .flatten() - .collect(); - // sort so we can find duplicates - // **NOTE** IterTools has `.duplicates()` but it's significantly slower (more than 2x) than this - marks.sort_unstable(); - marks[..] - .iter() - .zip(&marks[1..]) - .filter(|(a, b)| a == b) - .unique() - .count() + .map(|s| { + s.rasterize() + .map(|(x, y)| -> usize { + cells[x as usize][y as usize] += 1; + if cells[x as usize][y as usize] == 2 { + 1 + } else { + 0 + } + }) + .sum::() + }) + .sum::() } pub(crate) fn run() -> Result<(usize, usize)> { diff --git a/src/day07.rs b/src/day07.rs index 4fab6d4..651c9bc 100644 --- a/src/day07.rs +++ b/src/day07.rs @@ -24,7 +24,8 @@ fn compute_fuel(positions: &[isize], target: isize, cheap_fuel: bool) -> usize { fn find_cheapest_location(positions: &[isize], cheap_fuel: bool) -> usize { // find start point - let mut position = get_median(positions); + let start = get_median(positions); + let mut position = start; let mut fuel = compute_fuel(positions, position, cheap_fuel); // search up @@ -36,6 +37,9 @@ fn find_cheapest_location(positions: &[isize], cheap_fuel: bool) -> usize { position += 1; fuel = new_fuel; } + if position != start { + return fuel; + } // search down loop { diff --git a/src/main.rs b/src/main.rs index 4e99ff9..a380ad8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use std::env; + use anyhow::Result; mod day01; @@ -9,19 +11,32 @@ mod day06; mod day07; mod utils; -fn main() -> Result<()> { - let dayfns = [ - day01::run, - day02::run, - day03::run, - day04::run, - day05::run, - day06::run, - day07::run, - ]; +// used to generate more runs when profiling +const RUNS: usize = 1; - for (i, f) in dayfns.iter().enumerate() { - println!("Day {:02} -> {:?}", i, f()?); +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + + for _ in 0..RUNS { + for (i, f) in [ + day01::run, + day02::run, + day03::run, + day04::run, + day05::run, + day06::run, + day07::run, + ] + .iter() + .enumerate() + { + if args.len() == 1 || args.contains(&(i + 1).to_string()) { + let result = f()?; + if RUNS == 1 { + println!("Day {:02} -> {:?}", i + 1, result); + } + } + } } Ok(())