mirror of
https://github.com/javalsai/aoc.git
synced 2026-01-13 01:19:59 +01:00
perf: several optimizations
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
|||||||
# inputs
|
# inputs
|
||||||
*demo*.txt
|
*demo*.txt
|
||||||
input.txt
|
input.txt
|
||||||
|
*-large.txt
|
||||||
|
|
||||||
# binaries
|
# binaries
|
||||||
main
|
main
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user