Created
December 3, 2024 21:43
-
-
Save nrc/97a3b9f8144036a20572def9f9342ac3 to your computer and use it in GitHub Desktop.
Advent of Code 2024 day 3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![allow(internal_features)] | |
#![feature(duration_millis_float)] | |
#![feature(core_intrinsics)] | |
use std::time::Instant; | |
use std::intrinsics::{select_unpredictable, unlikely}; | |
#[global_allocator] | |
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; | |
const KET: u8 = b')'; | |
const COMMA: u8 = b','; | |
const MUL: [u8; 4] = [b'm', b'u', b'l', b'(']; | |
const ZERO: u8 = b'0'; | |
const NINE: u8 = b'9'; | |
const INPUT: &[u8; 19880] = include_bytes!("../input.txt"); | |
enum State { | |
N1, | |
N2, | |
None, | |
} | |
// big-endian, with trailing zeros to pad. `digits` indicates number of valid digits | |
#[inline(always)] | |
fn a_to_n(buf: [u8; 3], digits: usize) -> u64 { | |
// Bounds checks are optimised away, no need for `get_unchecked` | |
match digits { | |
1 => (buf[0] - ZERO) as u64, | |
2 => (buf[1] - ZERO) as u64 + ((buf[0] - ZERO) as u64 * 10), | |
3 => { | |
(buf[2] - ZERO) as u64 + ((buf[1] - ZERO) as u64 * 10) + ((buf[0] - ZERO) as u64 * 100) | |
} | |
_ => unsafe { std::hint::unreachable_unchecked() }, | |
} | |
} | |
#[inline(always)] | |
fn check_chunk(chunk: &[u8]) -> bool { | |
let chunk: u32 = unsafe { | |
u32::from_be_bytes([ | |
0, | |
*chunk.get_unchecked(1), | |
*chunk.get_unchecked(2), | |
*chunk.get_unchecked(3), | |
]) | |
}; | |
let mul = u32::from_be_bytes(MUL); | |
chunk == mul >> 8 | |
|| chunk & 0x0000ffff == mul >> 16 | |
|| chunk & 0x000000ff == mul >> 24 | |
} | |
#[inline(always)] | |
fn do_it() -> u64 { | |
let mut total: u64 = 0; | |
let mut state = State::None; | |
let mut nbuf: [u8; 3] = [ZERO; 3]; | |
let mut npos: usize = 0; | |
let mut n1: u64 = 0; | |
macro_rules! reset { | |
() => { | |
state = State::None; | |
npos = 0; | |
n1 = 0; | |
}; | |
} | |
let mut i = 0; | |
loop { | |
match state { | |
State::None => { | |
if unlikely(i >= INPUT.len() - 7) { | |
break; | |
} | |
let chunk = unsafe { INPUT.get_unchecked(i..i + 4) }; | |
if chunk == MUL { | |
state = State::N1; | |
i += 4; | |
} else if i % 4 == 0 { | |
i += select_unpredictable(check_chunk(chunk), 1, 4); | |
} else { | |
i += 1; | |
} | |
} | |
State::N1 => { | |
if unlikely(i >= INPUT.len()) { | |
break; | |
} | |
let input = unsafe { *INPUT.get_unchecked(i) }; | |
i += 1; | |
if input == COMMA { | |
n1 = a_to_n(nbuf, npos); | |
state = State::N2; | |
npos = 0; | |
} else if npos == 3 || input < ZERO || input > NINE { | |
reset!(); | |
} else { | |
unsafe { *nbuf.get_unchecked_mut(npos) = input }; | |
npos += 1; | |
} | |
} | |
State::N2 => { | |
if unlikely(i >= INPUT.len()) { | |
break; | |
} | |
let input = unsafe { *INPUT.get_unchecked(i) }; | |
i += 1; | |
if input == KET { | |
let n2 = a_to_n(nbuf, npos); | |
total += n1 * n2; | |
reset!(); | |
} else if npos == 3 || input < ZERO || input > NINE { | |
reset!(); | |
} else { | |
unsafe { *nbuf.get_unchecked_mut(npos) = input }; | |
npos += 1; | |
} | |
} | |
} | |
} | |
total | |
} | |
fn main() { | |
let mut times = Vec::with_capacity(50); | |
let mut result = 0; | |
for i in 0..70 { | |
let start = Instant::now(); | |
result = do_it(); | |
if i < 20 { | |
continue; | |
} | |
let time = Instant::now() - start; | |
times.push(time.as_millis_f64()); | |
} | |
assert_eq!(result, 183788984); | |
let mean = times.iter().sum::<f64>() / 50.0; | |
let min: f64 = times.into_iter().reduce(f64::min).unwrap(); | |
println!("min: {min}, mean: {mean}"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment