perf: several optimizations

This commit is contained in:
2025-12-02 02:25:40 +01:00
parent 888556b657
commit d44431c42a
4 changed files with 70 additions and 34 deletions

1
.gitignore vendored
View File

@@ -1,6 +1,7 @@
# inputs # inputs
*demo*.txt *demo*.txt
input.txt input.txt
*-large.txt
# binaries # binaries
main main

View File

@@ -1,24 +1,20 @@
use std::{hint::unreachable_unchecked, io::BufRead}; type SmolI = i16;
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "Rust" fn challenge_isize(buf: &[u8]) -> isize { extern "Rust" fn challenge_isize(buf: &[u8]) -> isize {
let mut count = 0; let mut count = 0;
let mut pos = 50; let mut pos = 50;
for ln in buf.lines() { let buf = unsafe { str::from_utf8_unchecked(buf.get_unchecked(..(buf.len() - 1))) };
let ln = unsafe { ln.unwrap_unchecked() };
let (dir, amt) = unsafe { (ln.as_bytes().get_unchecked(0), ln.get_unchecked(1..)) };
let amt: i16 = unsafe { amt.parse().unwrap_unchecked() }; for ln in buf.split('\n') {
match dir { let (dir, amt) = unsafe { (*ln.as_bytes().get_unchecked(0), ln.get_unchecked(1..)) };
b'L' => pos -= amt,
b'R' => pos += amt,
_ => unsafe { unreachable_unchecked() },
}
let amt: SmolI = unsafe { amt.parse().unwrap_unchecked() };
pos += ((((dir == b'R') as SmolI) << 1) - 1) * amt;
pos %= 100; pos %= 100;
count += (pos != 0) as i16; count += (pos == 0) as SmolI;
} }
count.into() count as isize
} }

View File

@@ -1,24 +1,19 @@
use std::{hint::unreachable_unchecked, io::BufRead}; type SmolI = i32;
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
unsafe extern "Rust" fn challenge_isize(buf: &[u8]) -> isize { unsafe extern "Rust" fn challenge_isize(buf: &[u8]) -> isize {
let mut count = 0; let mut count = 0;
let mut pos = 50; let mut pos = 50;
for ln in buf.lines() { let buf = unsafe { str::from_utf8_unchecked(buf.get_unchecked(..(buf.len() - 1))) };
let ln = unsafe { ln.unwrap_unchecked() };
let (dir, amt) = (ln.as_bytes()[0], &ln[1..]);
let amt: isize = unsafe { amt.parse().unwrap_unchecked() }; for ln in buf.split('\n') {
if amt == 0 { let (dir, amt) = unsafe { (*ln.as_bytes().get_unchecked(0), ln.get_unchecked(1..)) };
continue;
} let amt: SmolI = unsafe { amt.parse().unwrap_unchecked() };
if amt == 0 { continue; }
let prev_pos = pos; let prev_pos = pos;
match dir { pos += ((((dir == b'R') as SmolI) << 1) - 1) * amt;
b'L' => pos -= amt,
b'R' => pos += amt,
_ => unsafe { unreachable_unchecked() },
}
count += if pos < 0 { count += if pos < 0 {
if prev_pos == 0 { if prev_pos == 0 {
@@ -33,5 +28,5 @@ unsafe extern "Rust" fn challenge_isize(buf: &[u8]) -> isize {
pos = pos.rem_euclid(100); pos = pos.rem_euclid(100);
} }
count count as isize
} }

View File

@@ -2,12 +2,12 @@
use std::{ use std::{
env, env,
ffi::{CStr, OsStr}, ffi::OsStr,
fs::File, fs::File,
io::{self, Read}, io::{self, Read},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::{Command, Stdio}, process::{Command, Stdio},
time::Instant, time::{self, Instant},
}; };
pub mod dl { pub mod dl {
@@ -133,6 +133,19 @@ pub mod loader {
} }
impl FnVariant { impl FnVariant {
/// Ideally the fn would be put on the heap, but that requires customly allocating
/// executable memory, a DST pain other stuff I can't be concerned with rn.
pub const fn make_noop_stub_isize() -> Self {
unsafe extern "Rust" fn __stub(_: &[u8]) -> isize {
0
}
FnVariant::Isize(__stub)
}
/// # Safety
///
/// Calls external arbitrary FNs.
pub unsafe fn call(&self, buf: &[u8]) -> FnRetVariant { pub unsafe fn call(&self, buf: &[u8]) -> FnRetVariant {
use FnRetVariant as R; use FnRetVariant as R;
use FnVariant as V; use FnVariant as V;
@@ -146,10 +159,7 @@ pub mod loader {
} }
} }
/// # Safety pub fn load_fn_from(handle: &Handle) -> Option<FnVariant> {
///
/// Can return arbitrary fn's depending on the handle's symbols.
pub unsafe fn load_fn_from(handle: &Handle) -> Option<FnVariant> {
macro_rules! untry { macro_rules! untry {
($e:expr) => { ($e:expr) => {
match $e { match $e {
@@ -162,6 +172,8 @@ pub mod loader {
use ChallengeFn as C; use ChallengeFn as C;
use FnVariant as V; use FnVariant as V;
// SAFETY: This is actually safe as it just creates the fn ptr, no weird fn with weird
// drop.
untry!(unsafe { handle.symfn::<C<isize>>(c"challenge_isize") }.map(V::Isize)); untry!(unsafe { handle.symfn::<C<isize>>(c"challenge_isize") }.map(V::Isize));
untry!(unsafe { handle.symfn::<C<usize>>(c"challenge_isize") }.map(V::Usize)); untry!(unsafe { handle.symfn::<C<usize>>(c"challenge_isize") }.map(V::Usize));
untry!(unsafe { handle.symfn::<C<(usize, usize)>>(c"challenge_isize") }.map(V::UsizeDuple)); untry!(unsafe { handle.symfn::<C<(usize, usize)>>(c"challenge_isize") }.map(V::UsizeDuple));
@@ -171,7 +183,21 @@ pub mod loader {
} }
} }
const EXPORT_NAME: &CStr = c"challenge"; pub mod performer {
use std::time::{Duration, Instant};
pub fn get_avg_runt(runs: u32, run: fn()) -> std::time::Duration {
let mut total_t = Duration::ZERO;
for _ in 0..runs {
let t = Instant::now();
run();
total_t += t.elapsed();
}
total_t / runs
}
}
pub type ChallengeFn = unsafe extern "Rust" fn(&[u8]) -> isize; pub type ChallengeFn = unsafe extern "Rust" fn(&[u8]) -> isize;
fn main() -> io::Result<()> { fn main() -> io::Result<()> {
@@ -182,6 +208,11 @@ fn main() -> io::Result<()> {
return Ok(()); return Ok(());
}; };
let (noop_overhead_cold, noop_overhead_hot) = measure_noop_overhead();
println!(
"\x1b[34minf: noop fn takes {noop_overhead_hot:#?} hot and {noop_overhead_cold:#?} cold\x1b[0m",
);
let rs_path = PathBuf::from(&day); let rs_path = PathBuf::from(&day);
assert_eq!(rs_path.extension(), Some(OsStr::new("rs"))); assert_eq!(rs_path.extension(), Some(OsStr::new("rs")));
let so_path = rs_path.with_extension("so"); let so_path = rs_path.with_extension("so");
@@ -191,7 +222,7 @@ fn main() -> io::Result<()> {
let input = buffer_input(&input)?; let input = buffer_input(&input)?;
let challenge = dl::open(so_path).expect("Couldn't load dyn library"); let challenge = dl::open(so_path).expect("Couldn't load dyn library");
let challenge_main = unsafe { loader::load_fn_from(&challenge) }.expect(concat!( let challenge_main = loader::load_fn_from(&challenge).expect(concat!(
"Didn't find any appropiate symbol in the compiled .so file. Make sure there is one and is ", "Didn't find any appropiate symbol in the compiled .so file. Make sure there is one and is ",
stringify!(unsafe extern "Rust" fn(&[u8]) -> isize) stringify!(unsafe extern "Rust" fn(&[u8]) -> isize)
)); ));
@@ -208,6 +239,19 @@ fn main() -> io::Result<()> {
Ok(()) Ok(())
} }
const STUB_ISIZE: loader::FnVariant = loader::FnVariant::make_noop_stub_isize();
fn measure_noop_overhead() -> (time::Duration, time::Duration) {
let timer = Instant::now();
// SAFETY: completely safe, not arbitrary fn but our stub
unsafe { STUB_ISIZE.call(&[]) };
let cold = timer.elapsed();
let hot = performer::get_avg_runt(u16::MAX.into(), || unsafe {
STUB_ISIZE.call(&[]);
});
(cold, hot)
}
fn compile(rs: &Path, so: &Path, rustc: Option<&str>) -> io::Result<()> { fn compile(rs: &Path, so: &Path, rustc: Option<&str>) -> io::Result<()> {
if env::var("REBUILD").is_err() { if env::var("REBUILD").is_err() {
let rsf = File::open(rs)?; let rsf = File::open(rs)?;