mirror of
https://github.com/NotAShelf/microfetch.git
synced 2026-04-12 12:57:41 +00:00
chore: don't benchmark binary crate; centralize assembly helpers
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ia24cb1647df93034937a8fcd6cad895c6a6a6964
This commit is contained in:
parent
1408ca9f38
commit
1c781aff56
7 changed files with 203 additions and 103 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
|
@ -417,9 +417,9 @@ checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
|||
|
||||
[[package]]
|
||||
name = "hotpath"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51462b35dc551217e1d1dd3f8c5eebbb5df7103370b1e55d24c19a3411d290f7"
|
||||
checksum = "2fde50be006a0fe95cc2fd6d25d884aa6932218e4055d7df2fa0d95c386acf8d"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"cfg-if",
|
||||
|
|
@ -444,9 +444,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hotpath-macros"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f022365c90cc04455f17a2b9ba5fe0e661cde1ab27434d382f1f8693fe124e7d"
|
||||
checksum = "dd884cee056e269e41e1127549458e1c4e309f31897ebbc1416982a74d40a5b5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -550,6 +550,7 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
|||
name = "microfetch"
|
||||
version = "0.4.13"
|
||||
dependencies = [
|
||||
"hotpath",
|
||||
"microfetch-lib",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -5,11 +5,20 @@
|
|||
//! What do you mean I wasted two whole hours to make the program only 100µs
|
||||
//! faster?
|
||||
//!
|
||||
//! Supports `x86_64` and `aarch64` architectures. Riscv support will be
|
||||
//! implemented when and ONLY WHEN I can be bothered to work on it.
|
||||
//! Supports `x86_64`, `aarch64`, and `riscv64` architectures.
|
||||
|
||||
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
|
||||
///
|
||||
/// # Returns
|
||||
|
|
@ -62,9 +71,24 @@ pub unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
|
|||
fd as i32
|
||||
}
|
||||
}
|
||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||
{
|
||||
compile_error!("Unsupported architecture for inline assembly syscalls");
|
||||
#[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)]
|
||||
{
|
||||
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")))]
|
||||
{
|
||||
compile_error!("Unsupported architecture for inline assembly syscalls");
|
||||
#[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)]
|
||||
{
|
||||
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")))]
|
||||
{
|
||||
compile_error!("Unsupported architecture for inline assembly syscalls");
|
||||
#[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)]
|
||||
{
|
||||
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")))]
|
||||
{
|
||||
compile_error!("Unsupported architecture for inline assembly syscalls");
|
||||
#[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)]
|
||||
{
|
||||
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")))]
|
||||
{
|
||||
compile_error!("Unsupported architecture for inline assembly syscalls");
|
||||
#[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)]
|
||||
{
|
||||
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")))]
|
||||
{
|
||||
compile_error!("Unsupported architecture for inline assembly syscalls");
|
||||
#[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)]
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,13 +9,12 @@ license.workspace = true
|
|||
publish = false
|
||||
|
||||
[dependencies]
|
||||
hotpath = { optional = true, version = "0.13.0" }
|
||||
hotpath = { optional = true, version = "0.14.0" }
|
||||
microfetch-asm.workspace = true
|
||||
|
||||
[features]
|
||||
hotpath = [ "dep:hotpath", "hotpath/hotpath" ]
|
||||
hotpath-alloc = [ "hotpath/hotpath-alloc" ]
|
||||
hotpath-off = [ "hotpath/hotpath-off" ]
|
||||
|
||||
[dev-dependencies]
|
||||
criterion.workspace = true
|
||||
|
|
|
|||
|
|
@ -13,12 +13,14 @@ use std::{
|
|||
pub use microfetch_asm as syscall;
|
||||
pub use microfetch_asm::{
|
||||
StatfsBuf,
|
||||
SysInfo,
|
||||
UtsNameBuf,
|
||||
read_file_fast,
|
||||
sys_close,
|
||||
sys_open,
|
||||
sys_read,
|
||||
sys_statfs,
|
||||
sys_sysinfo,
|
||||
sys_uname,
|
||||
sys_write,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use std::{io, mem::MaybeUninit};
|
||||
|
||||
use crate::syscall::sys_sysinfo;
|
||||
|
||||
/// Faster integer to string conversion without the formatting overhead.
|
||||
#[inline]
|
||||
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..]) }
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// # Errors
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ repository = "https://github.com/notashelf/microfetch"
|
|||
publish = false
|
||||
|
||||
[dependencies]
|
||||
hotpath = { optional = true, version = "0.14.0" }
|
||||
microfetch-lib.workspace = true
|
||||
|
||||
[features]
|
||||
hotpath = [ "microfetch-lib/hotpath" ]
|
||||
hotpath-alloc = [ "microfetch-lib/hotpath-alloc" ]
|
||||
hotpath-off = [ "microfetch-lib/hotpath-off" ]
|
||||
hotpath = [ "dep:hotpath" ]
|
||||
hotpath-alloc = [ "hotpath/hotpath-alloc" ]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#[cfg_attr(feature = "hotpath", hotpath::main)]
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
microfetch_lib::run()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue