Skip to content

2020 day12 performance #85

Open
TerjeWiigMathisen wants to merge 4 commits into
maneatingape:mainfrom
TerjeWiigMathisen:2020_day12_performance
Open

2020 day12 performance #85
TerjeWiigMathisen wants to merge 4 commits into
maneatingape:mainfrom
TerjeWiigMathisen:2020_day12_performance

Conversation

@TerjeWiigMathisen

Copy link
Copy Markdown
Contributor

Description

2020 / day12 8x speedup from a custom single-pass parser
Please ignore the spurious day06 commit, I don't know how to get rid of it!

Type of change

  • Performance improvement
  • Bug fix
  • Other

Checklist

  • Pull request title and commit messages are clear and informative.
  • Documentation has been updated if necessary.
  • Code style matches the existing code. This one is somewhat subjective, but try to "fit in" by
    using the same naming conventions. Code should be portable, avoiding any
    architecture-specific intrinsics.
  • Tests pass cargo test
  • Code is formatted cargo fmt -- `find . -name "*.rs"`
  • Code is linted cargo clippy --all-targets --all-features

Formatting and linting also can be executed by running just
(if installed) on the command line at the project root.

@maneatingape maneatingape left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I get a roughly 3x speedup.

We can re-use existing parsing utility methods to get the same effect with less code, while keeping the core idea.

Comment thread src/year2020/day06.rs
Comment thread src/year2020/day12.rs
_ => unreachable!(),
}
pub fn parse(input: &str) -> Input {
let mut inp = input.to_string();

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parse.rs does exactly this byte by byte parsing, exposed as 2 neat typed iterators (one for unsigned number and one for signed) so that you can write:

use crate::util::parse::*;

let amounts = input.input.signed();

Since we also need the direction command use the nifty methods on Iterator to compose 2 iterators into a single iterator over a 2-tuple, e.g.

    let first = input.bytes().filter(u8::is_ascii_uppercase);
    let second = input.iter_signed();

We can then use this (without an intermediate vec) in your core loop that combines part one and part two, e.g. something like:

    let mut part_one = ORIGIN;
    let mut part_two = ORIGIN;
    let mut direction = RIGHT;
    let mut waypoint = Point::new(10, -1);

    for (command, amount) in first.zip(second) {
        match command {
            b'N' => {
                part_one.y -= amount;
                waypoint.y -= amount;
            }
           ...

Comment thread src/year2020/day12.rs
}

fn rotate(point: Point, amount: i32) -> Point {
match amount.rem_euclid(360) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can omit the rem_euclid if the class from L reflect the rotation e.g. direction = rotate(direction, 360 - amount);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants