chore: don't benchmark binary crate; centralize assembly helpers

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Ia24cb1647df93034937a8fcd6cad895c6a6a6964
This commit is contained in:
raf 2026-03-27 13:49:26 +03:00
commit 1c781aff56
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
7 changed files with 203 additions and 103 deletions

9
Cargo.lock generated
View file

@ -417,9 +417,9 @@ checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]] [[package]]
name = "hotpath" name = "hotpath"
version = "0.13.0" version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51462b35dc551217e1d1dd3f8c5eebbb5df7103370b1e55d24c19a3411d290f7" checksum = "2fde50be006a0fe95cc2fd6d25d884aa6932218e4055d7df2fa0d95c386acf8d"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"cfg-if", "cfg-if",
@ -444,9 +444,9 @@ dependencies = [
[[package]] [[package]]
name = "hotpath-macros" name = "hotpath-macros"
version = "0.13.0" version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f022365c90cc04455f17a2b9ba5fe0e661cde1ab27434d382f1f8693fe124e7d" checksum = "dd884cee056e269e41e1127549458e1c4e309f31897ebbc1416982a74d40a5b5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -550,6 +550,7 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
name = "microfetch" name = "microfetch"
version = "0.4.13" version = "0.4.13"
dependencies = [ dependencies = [
"hotpath",
"microfetch-lib", "microfetch-lib",
] ]

View file

@ -5,11 +5,20 @@
//! What do you mean I wasted two whole hours to make the program only 100µs //! What do you mean I wasted two whole hours to make the program only 100µs
//! faster? //! faster?
//! //!
//! Supports `x86_64` and `aarch64` architectures. Riscv support will be //! Supports `x86_64`, `aarch64`, and `riscv64` architectures.
//! implemented when and ONLY WHEN I can be bothered to work on it.
use std::io; use std::io;
// Ensure we're compiling for a supported architecture.
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "riscv64"
)))]
compile_error!(
"Unsupported architecture: only x86_64, aarch64, and riscv64 are supported"
);
/// Direct syscall to open a file /// Direct syscall to open a file
/// ///
/// # Returns /// # Returns
@ -62,9 +71,24 @@ pub unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
fd as i32 fd as i32
} }
} }
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] #[cfg(target_arch = "riscv64")]
unsafe {
let fd: i64;
std::arch::asm!(
"ecall",
in("a7") 56i64, // SYS_openat
in("a0") -100i32, // AT_FDCWD
in("a1") path,
in("a2") flags,
in("a3") 0i32, // mode
lateout("a0") fd,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{ {
compile_error!("Unsupported architecture for inline assembly syscalls"); fd as i32
}
} }
} }
@ -122,9 +146,23 @@ pub unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
} }
} }
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] #[cfg(target_arch = "riscv64")]
unsafe {
let ret: i64;
std::arch::asm!(
"ecall",
in("a7") 63i64, // SYS_read
in("a0") fd,
in("a1") buf,
in("a2") count,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{ {
compile_error!("Unsupported architecture for inline assembly syscalls"); ret as isize
}
} }
} }
@ -183,9 +221,23 @@ pub unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
} }
} }
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] #[cfg(target_arch = "riscv64")]
unsafe {
let ret: i64;
std::arch::asm!(
"ecall",
in("a7") 64i64, // SYS_write
in("a0") fd,
in("a1") buf,
in("a2") count,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{ {
compile_error!("Unsupported architecture for inline assembly syscalls"); ret as isize
}
} }
} }
@ -231,9 +283,20 @@ pub unsafe fn sys_close(fd: i32) -> i32 {
} }
} }
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] #[cfg(target_arch = "riscv64")]
unsafe {
let ret: i64;
std::arch::asm!(
"ecall",
in("a7") 57i64, // SYS_close
in("a0") fd,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{ {
compile_error!("Unsupported architecture for inline assembly syscalls"); ret as i32
}
} }
} }
@ -300,9 +363,21 @@ pub unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
} }
} }
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] #[cfg(target_arch = "riscv64")]
unsafe {
let ret: i64;
std::arch::asm!(
"ecall",
in("a7") 160i64, // SYS_uname
in("a0") buf,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{ {
compile_error!("Unsupported architecture for inline assembly syscalls"); ret as i32
}
} }
} }
@ -381,9 +456,22 @@ pub unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
} }
} }
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] #[cfg(target_arch = "riscv64")]
unsafe {
let ret: i64;
std::arch::asm!(
"ecall",
in("a7") 43i64, // SYS_statfs
in("a0") path,
in("a1") buf,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{ {
compile_error!("Unsupported architecture for inline assembly syscalls"); ret as i32
}
} }
} }
@ -427,3 +515,85 @@ pub fn read_file_fast(path: &str, buffer: &mut [u8]) -> io::Result<usize> {
} }
} }
} }
/// Raw buffer for the `sysinfo(2)` syscall.
///
/// In the Linux ABI `uptime` is a `long` at offset 0. The remaining fields are
/// not needed, but are declared to give the struct its correct size (112 bytes
/// on 64-bit Linux).
///
/// The layout matches the kernel's `struct sysinfo` *exactly*:
/// `mem_unit` ends at offset 108, then 4 bytes of implicit padding to 112.
#[repr(C)]
pub struct SysInfo {
pub uptime: i64,
pub loads: [u64; 3],
pub totalram: u64,
pub freeram: u64,
pub sharedram: u64,
pub bufferram: u64,
pub totalswap: u64,
pub freeswap: u64,
pub procs: u16,
_pad: u16,
_pad2: u32, /* alignment padding to reach 8-byte boundary for
* totalhigh */
pub totalhigh: u64,
pub freehigh: u64,
pub mem_unit: u32,
// 4 bytes implicit trailing padding to reach 112 bytes total; no field
// needed
}
/// Direct `sysinfo(2)` syscall
///
/// # Returns
///
/// 0 on success, negative errno on error
///
/// # Safety
///
/// The caller must ensure that `info` points to a valid `SysInfo` buffer.
#[inline]
pub unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
#[cfg(target_arch = "x86_64")]
unsafe {
let ret: i64;
std::arch::asm!(
"syscall",
in("rax") 99_i64, // __NR_sysinfo
in("rdi") info,
out("rcx") _,
out("r11") _,
lateout("rax") ret,
options(nostack)
);
ret
}
#[cfg(target_arch = "aarch64")]
unsafe {
let ret: i64;
std::arch::asm!(
"svc #0",
in("x8") 179_i64, // __NR_sysinfo
in("x0") info,
lateout("x0") ret,
options(nostack)
);
ret
}
#[cfg(target_arch = "riscv64")]
unsafe {
let ret: i64;
std::arch::asm!(
"ecall",
in("a7") 179_i64, // __NR_sysinfo
in("a0") info,
lateout("a0") ret,
options(nostack)
);
ret
}
}

View file

@ -9,13 +9,12 @@ license.workspace = true
publish = false publish = false
[dependencies] [dependencies]
hotpath = { optional = true, version = "0.13.0" } hotpath = { optional = true, version = "0.14.0" }
microfetch-asm.workspace = true microfetch-asm.workspace = true
[features] [features]
hotpath = [ "dep:hotpath", "hotpath/hotpath" ] hotpath = [ "dep:hotpath", "hotpath/hotpath" ]
hotpath-alloc = [ "hotpath/hotpath-alloc" ] hotpath-alloc = [ "hotpath/hotpath-alloc" ]
hotpath-off = [ "hotpath/hotpath-off" ]
[dev-dependencies] [dev-dependencies]
criterion.workspace = true criterion.workspace = true

View file

@ -13,12 +13,14 @@ use std::{
pub use microfetch_asm as syscall; pub use microfetch_asm as syscall;
pub use microfetch_asm::{ pub use microfetch_asm::{
StatfsBuf, StatfsBuf,
SysInfo,
UtsNameBuf, UtsNameBuf,
read_file_fast, read_file_fast,
sys_close, sys_close,
sys_open, sys_open,
sys_read, sys_read,
sys_statfs, sys_statfs,
sys_sysinfo,
sys_uname, sys_uname,
sys_write, sys_write,
}; };

View file

@ -1,5 +1,7 @@
use std::{io, mem::MaybeUninit}; use std::{io, mem::MaybeUninit};
use crate::syscall::sys_sysinfo;
/// Faster integer to string conversion without the formatting overhead. /// Faster integer to string conversion without the formatting overhead.
#[inline] #[inline]
fn itoa(mut n: u64, buf: &mut [u8]) -> &str { fn itoa(mut n: u64, buf: &mut [u8]) -> &str {
@ -17,79 +19,6 @@ fn itoa(mut n: u64, buf: &mut [u8]) -> &str {
unsafe { std::str::from_utf8_unchecked(&buf[i..]) } unsafe { std::str::from_utf8_unchecked(&buf[i..]) }
} }
/// Raw buffer for the `sysinfo(2)` syscall.
///
/// In the Linux ABI `uptime` is a `long` at offset 0. The remaining fields are
/// not needed, but are declared to give the struct its correct size (112 bytes
/// on 64-bit Linux).
///
/// The layout matches the kernel's `struct sysinfo` *exactly*:
/// `mem_unit` ends at offset 108, then 4 bytes of implicit padding to 112.
#[repr(C)]
struct SysInfo {
uptime: i64,
loads: [u64; 3],
totalram: u64,
freeram: u64,
sharedram: u64,
bufferram: u64,
totalswap: u64,
freeswap: u64,
procs: u16,
_pad: u16,
_pad2: u32, // alignment padding to reach 8-byte boundary for totalhigh
totalhigh: u64,
freehigh: u64,
mem_unit: u32,
// 4 bytes implicit trailing padding to reach 112 bytes total; no field
// needed
}
/// Direct `sysinfo(2)` syscall using inline assembly
///
/// # Safety
///
/// The caller must ensure the sysinfo pointer is valid.
#[inline]
unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
#[cfg(target_arch = "x86_64")]
{
let ret: i64;
unsafe {
std::arch::asm!(
"syscall",
in("rax") 99_i64, // __NR_sysinfo
in("rdi") info,
out("rcx") _,
out("r11") _,
lateout("rax") ret,
options(nostack)
);
}
ret
}
#[cfg(target_arch = "aarch64")]
{
let ret: i64;
unsafe {
std::arch::asm!(
"svc #0",
in("x8") 179_i64, // __NR_sysinfo
in("x0") info,
lateout("x0") ret,
options(nostack)
);
}
ret
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
{
compile_error!("Unsupported architecture for inline assembly syscalls");
}
}
/// Gets the current system uptime. /// Gets the current system uptime.
/// ///
/// # Errors /// # Errors

View file

@ -11,12 +11,12 @@ repository = "https://github.com/notashelf/microfetch"
publish = false publish = false
[dependencies] [dependencies]
hotpath = { optional = true, version = "0.14.0" }
microfetch-lib.workspace = true microfetch-lib.workspace = true
[features] [features]
hotpath = [ "microfetch-lib/hotpath" ] hotpath = [ "dep:hotpath" ]
hotpath-alloc = [ "microfetch-lib/hotpath-alloc" ] hotpath-alloc = [ "hotpath/hotpath-alloc" ]
hotpath-off = [ "microfetch-lib/hotpath-off" ]
[lints] [lints]
workspace = true workspace = true

View file

@ -1,4 +1,3 @@
#[cfg_attr(feature = "hotpath", hotpath::main)]
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
microfetch_lib::run() microfetch_lib::run()
} }