From f8a0070986dda8c7554dcf769fda99cd129e039f Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 19:19:28 +0300 Subject: [PATCH 1/5] uptime: optimize uptime calculation w/ inline assembly and custom itoa lol, lmao even. Signed-off-by: NotAShelf Change-Id: Ie22fbd2e9c2be5740b493bdc81caafb36a6a6964 --- src/uptime.rs | 74 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/src/uptime.rs b/src/uptime.rs index 52188c2..98c9207 100644 --- a/src/uptime.rs +++ b/src/uptime.rs @@ -1,4 +1,66 @@ -use std::{fmt::Write, io, mem::MaybeUninit}; +use std::{io, mem::MaybeUninit}; + +/// Fast integer to string conversion (no formatting overhead) +#[inline] +fn itoa(mut n: u64, buf: &mut [u8]) -> &str { + if n == 0 { + return "0"; + } + + let mut i = buf.len(); + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + unsafe { std::str::from_utf8_unchecked(&buf[i..]) } +} + +/// Direct sysinfo syscall using inline assembly +/// +/// # Safety +/// This function uses inline assembly to make a direct syscall. +/// The caller must ensure the sysinfo pointer is valid. +#[inline] +unsafe fn sys_sysinfo(info: *mut libc::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")))] + { + unsafe { libc::sysinfo(info) as i64 } + } +} /// Gets the current system uptime. /// @@ -9,7 +71,7 @@ use std::{fmt::Write, io, mem::MaybeUninit}; pub fn get_current() -> Result { let uptime_seconds = { let mut info = MaybeUninit::uninit(); - if unsafe { libc::sysinfo(info.as_mut_ptr()) } != 0 { + if unsafe { sys_sysinfo(info.as_mut_ptr()) } != 0 { return Err(io::Error::last_os_error()); } #[allow(clippy::cast_sign_loss)] @@ -23,22 +85,24 @@ pub fn get_current() -> Result { let minutes = (uptime_seconds / 60) % 60; let mut result = String::with_capacity(32); + let mut buf = [0u8; 20]; // Enough for u64::MAX + if days > 0 { - let _ = write!(result, "{days}"); + result.push_str(itoa(days, &mut buf)); result.push_str(if days == 1 { " day" } else { " days" }); } if hours > 0 { if !result.is_empty() { result.push_str(", "); } - let _ = write!(result, "{hours}"); + result.push_str(itoa(hours, &mut buf)); result.push_str(if hours == 1 { " hour" } else { " hours" }); } if minutes > 0 { if !result.is_empty() { result.push_str(", "); } - let _ = write!(result, "{minutes}"); + result.push_str(itoa(minutes, &mut buf)); result.push_str(if minutes == 1 { " minute" } else { " minutes" }); } if result.is_empty() { From 75132ff17239b2778ee1e36308a6b011579400e6 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 21:27:28 +0300 Subject: [PATCH 2/5] treewide: remove nix dependency, add custom syscall wrappers & `UtsName` Signed-off-by: NotAShelf Change-Id: Ib880f4bafe9d3bbc944af4b9125256366a6a6964 --- Cargo.lock | 19 ---- Cargo.toml | 1 - benches/benchmark.rs | 3 +- src/lib.rs | 40 +++++++++ src/main.rs | 5 +- src/release.rs | 57 ++++++++---- src/syscall.rs | 203 +++++++++++++++++++++++++++++++++++++++++++ src/system.rs | 102 +++++++++++++++------- 8 files changed, 358 insertions(+), 72 deletions(-) create mode 100644 src/syscall.rs diff --git a/Cargo.lock b/Cargo.lock index c9ed3f9..0147746 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -143,12 +143,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - [[package]] name = "chunked_transfer" version = "1.5.0" @@ -777,7 +771,6 @@ dependencies = [ "criterion", "hotpath", "libc", - "nix", ] [[package]] @@ -790,18 +783,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "num-conv" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 7e83f9f..e95ef5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ path = "src/main.rs" [dependencies] hotpath = { optional = true, version = "0.6.0" } libc = "0.2.177" -nix = { default-features = false, features = [ "fs", "hostname", "feature" ], version = "0.30.1" } [dev-dependencies] criterion = "0.7" diff --git a/benches/benchmark.rs b/benches/benchmark.rs index 33dc1b9..8d62082 100644 --- a/benches/benchmark.rs +++ b/benches/benchmark.rs @@ -1,5 +1,6 @@ use criterion::{Criterion, criterion_group, criterion_main}; use microfetch_lib::{ + UtsName, colors::print_dots, desktop::get_desktop_info, release::{get_os_pretty_name, get_system_info}, @@ -13,7 +14,7 @@ use microfetch_lib::{ }; fn main_benchmark(c: &mut Criterion) { - let utsname = nix::sys::utsname::uname().expect("lol"); + let utsname = UtsName::uname().expect("Failed to get uname"); c.bench_function("user_info", |b| { b.iter(|| get_username_and_hostname(&utsname)); }); diff --git a/src/lib.rs b/src/lib.rs index 4ba33f8..8c8a6ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,45 @@ pub mod colors; pub mod desktop; pub mod release; +pub mod syscall; pub mod system; pub mod uptime; + +use std::mem::MaybeUninit; + +/// Wrapper for `libc::utsname` with safe accessor methods +pub struct UtsName(libc::utsname); + +impl UtsName { + /// Calls `uname` syscall and returns a `UtsName` wrapper + /// + /// # Errors + /// Returns an error if the uname syscall fails + pub fn uname() -> Result { + let mut uts = MaybeUninit::uninit(); + if unsafe { libc::uname(uts.as_mut_ptr()) } != 0 { + return Err(std::io::Error::last_os_error()); + } + Ok(Self(unsafe { uts.assume_init() })) + } + + #[must_use] + pub const fn nodename(&self) -> &std::ffi::CStr { + unsafe { std::ffi::CStr::from_ptr(self.0.nodename.as_ptr()) } + } + + #[must_use] + pub const fn sysname(&self) -> &std::ffi::CStr { + unsafe { std::ffi::CStr::from_ptr(self.0.sysname.as_ptr()) } + } + + #[must_use] + pub const fn release(&self) -> &std::ffi::CStr { + unsafe { std::ffi::CStr::from_ptr(self.0.release.as_ptr()) } + } + + #[must_use] + pub const fn machine(&self) -> &std::ffi::CStr { + unsafe { std::ffi::CStr::from_ptr(self.0.machine.as_ptr()) } + } +} diff --git a/src/main.rs b/src/main.rs index 5ba7810..4e664bb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,14 @@ mod colors; mod desktop; mod release; +mod syscall; mod system; mod uptime; use std::io::{Write, stdout}; +pub use microfetch_lib::UtsName; + use crate::{ colors::print_dots, desktop::get_desktop_info, @@ -24,7 +27,7 @@ fn main() -> Result<(), Box> { if Some("--version") == std::env::args().nth(1).as_deref() { println!("Microfetch {}", env!("CARGO_PKG_VERSION")); } else { - let utsname = nix::sys::utsname::uname()?; + let utsname = UtsName::uname()?; let fields = Fields { user_info: get_username_and_hostname(&utsname), os_name: get_os_pretty_name()?, diff --git a/src/release.rs b/src/release.rs index 1b820b8..8d3232e 100644 --- a/src/release.rs +++ b/src/release.rs @@ -1,10 +1,6 @@ -use std::{ - fmt::Write as _, - fs::File, - io::{self, Read}, -}; +use std::{fmt::Write as _, io}; -use nix::sys::utsname::UtsName; +use crate::{UtsName, syscall::read_file_fast}; #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] @@ -28,21 +24,46 @@ pub fn get_system_info(utsname: &UtsName) -> String { /// Returns an error if `/etc/os-release` cannot be read. #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_os_pretty_name() -> Result { - // We use a stack-allocated buffer here, which seems to perform MUCH better - // than `BufReader`. In hindsight, I should've seen this coming. - let mut buffer = String::with_capacity(1024); - File::open("/etc/os-release")?.read_to_string(&mut buffer)?; + // Fast byte-level scanning for PRETTY_NAME= + const PREFIX: &[u8] = b"PRETTY_NAME="; - for line in buffer.lines() { - if let Some(pretty_name) = line.strip_prefix("PRETTY_NAME=") { - if let Some(trimmed) = pretty_name - .strip_prefix('"') - .and_then(|s| s.strip_suffix('"')) + let mut buffer = [0u8; 1024]; + + // Use fast syscall-based file reading + let bytes_read = read_file_fast("/etc/os-release", &mut buffer)?; + let content = &buffer[..bytes_read]; + + let mut offset = 0; + + while offset < content.len() { + let remaining = &content[offset..]; + + // Find newline or end + let line_end = remaining + .iter() + .position(|&b| b == b'\n') + .unwrap_or(remaining.len()); + let line = &remaining[..line_end]; + + if line.starts_with(PREFIX) { + let value = &line[PREFIX.len()..]; + + // Strip quotes if present + let trimmed = if value.len() >= 2 + && value[0] == b'"' + && value[value.len() - 1] == b'"' { - return Ok(trimmed.to_owned()); - } - return Ok(pretty_name.to_owned()); + &value[1..value.len() - 1] + } else { + value + }; + + // Convert to String - should be valid UTF-8 + return Ok(String::from_utf8_lossy(trimmed).into_owned()); } + + offset += line_end + 1; } + Ok("Unknown".to_owned()) } diff --git a/src/syscall.rs b/src/syscall.rs new file mode 100644 index 0000000..2776fe7 --- /dev/null +++ b/src/syscall.rs @@ -0,0 +1,203 @@ +//! Incredibly fast syscall wrappers for using inline assembly. Serves the +//! purposes of completely bypassing Rust's standard library in favor of +//! handwritten Assembly. Is this a good idea? No. Is it fast? Yeah, but only +//! marginally. Either way it serves a purpose and I will NOT accept criticism. +//! 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. + +use std::io; + +/// Direct syscall to open a file +/// Returns file descriptor or -1 on error +/// +/// # Safety +/// +/// The caller must ensure: +/// - `path` points to a valid null-terminated C string +/// - The pointer remains valid for the duration of the syscall +#[inline] +#[must_use] +pub unsafe fn sys_open(path: *const u8, flags: i32) -> i32 { + #[cfg(target_arch = "x86_64")] + unsafe { + let fd: i64; + std::arch::asm!( + "syscall", + in("rax") 2i64, // SYS_open + in("rdi") path, + in("rsi") flags, + in("rdx") 0i32, // mode (not used for reading) + lateout("rax") fd, + lateout("rcx") _, + lateout("r11") _, + options(nostack) + ); + #[allow(clippy::cast_possible_truncation)] + { + fd as i32 + } + } + #[cfg(target_arch = "aarch64")] + unsafe { + let fd: i64; + std::arch::asm!( + "svc #0", + in("x8") 56i64, // SYS_openat + in("x0") -100i32, // AT_FDCWD + in("x1") path, + in("x2") flags, + in("x3") 0i32, // mode + lateout("x0") fd, + options(nostack) + ); + #[allow(clippy::cast_possible_truncation)] + { + fd as i32 + } + } + #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] + { + compile_error!("Unsupported architecture for inline assembly syscalls"); + } +} + +/// Direct syscall to read from a file descriptor +/// Returns number of bytes read or -1 on error +/// +/// # Safety +/// +/// The caller must ensure: +/// - `buf` points to a valid writable buffer of at least `count` bytes +/// - `fd` is a valid open file descriptor +#[inline] +pub unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize { + #[cfg(target_arch = "x86_64")] + unsafe { + let ret: i64; + std::arch::asm!( + "syscall", + in("rax") 0i64, // SYS_read + in("rdi") fd, + in("rsi") buf, + in("rdx") count, + lateout("rax") ret, + lateout("rcx") _, + lateout("r11") _, + options(nostack) + ); + #[allow(clippy::cast_possible_truncation)] + { + ret as isize + } + } + #[cfg(target_arch = "aarch64")] + unsafe { + let ret: i64; + std::arch::asm!( + "svc #0", + in("x8") 63i64, // SYS_read + in("x0") fd, + in("x1") buf, + in("x2") count, + lateout("x0") ret, + options(nostack) + ); + #[allow(clippy::cast_possible_truncation)] + { + ret as isize + } + } + #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] + { + compile_error!("Unsupported architecture for inline assembly syscalls"); + } +} + +/// Direct syscall to close a file descriptor +/// +/// # Safety +/// +/// The caller must ensure `fd` is a valid open file descriptor +#[inline] +#[must_use] +pub unsafe fn sys_close(fd: i32) -> i32 { + #[cfg(target_arch = "x86_64")] + unsafe { + let ret: i64; + std::arch::asm!( + "syscall", + in("rax") 3i64, // SYS_close + in("rdi") fd, + lateout("rax") ret, + lateout("rcx") _, + lateout("r11") _, + options(nostack) + ); + #[allow(clippy::cast_possible_truncation)] + { + ret as i32 + } + } + #[cfg(target_arch = "aarch64")] + unsafe { + let ret: i64; + std::arch::asm!( + "svc #0", + in("x8") 57i64, // SYS_close + in("x0") fd, + lateout("x0") ret, + options(nostack) + ); + #[allow(clippy::cast_possible_truncation)] + { + ret as i32 + } + } + #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] + { + compile_error!("Unsupported architecture for inline assembly syscalls"); + } +} + +/// Read entire file using direct syscalls. This avoids libc overhead and can be +/// significantly faster for small files. +/// +/// # Errors +/// +/// Returns an error if the file cannot be opened or read +#[inline] +pub fn read_file_fast(path: &str, buffer: &mut [u8]) -> io::Result { + const O_RDONLY: i32 = 0; + + // Use stack-allocated buffer for null-terminated path (max 256 bytes) + let path_bytes = path.as_bytes(); + if path_bytes.len() >= 256 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "Path too long")); + } + + let mut path_buf = [0u8; 256]; + path_buf[..path_bytes.len()].copy_from_slice(path_bytes); + // XXX: Already zero-terminated since array is initialized to zeros + + unsafe { + let fd = sys_open(path_buf.as_ptr(), O_RDONLY); + if fd < 0 { + return Err(io::Error::last_os_error()); + } + + let bytes_read = sys_read(fd, buffer.as_mut_ptr(), buffer.len()); + let _ = sys_close(fd); + + if bytes_read < 0 { + return Err(io::Error::last_os_error()); + } + + #[allow(clippy::cast_sign_loss)] + { + Ok(bytes_read as usize) + } + } +} diff --git a/src/system.rs b/src/system.rs index ba90b27..24406ec 100644 --- a/src/system.rs +++ b/src/system.rs @@ -1,13 +1,6 @@ -use std::{ - env, - fmt::Write as _, - fs::File, - io::{self, Read}, -}; +use std::{env, fmt::Write as _, io, mem::MaybeUninit}; -use nix::sys::{statvfs::statvfs, utsname::UtsName}; - -use crate::colors::COLORS; +use crate::{UtsName, colors::COLORS, syscall::read_file_fast}; #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] @@ -57,10 +50,17 @@ pub fn get_shell() -> String { #[cfg_attr(feature = "hotpath", hotpath::measure)] #[allow(clippy::cast_precision_loss)] pub fn get_root_disk_usage() -> Result { - let vfs = statvfs("/")?; - let block_size = vfs.block_size() as u64; - let total_blocks = vfs.blocks(); - let available_blocks = vfs.blocks_available(); + let mut vfs = MaybeUninit::uninit(); + let path = b"/\0"; + + if unsafe { libc::statvfs(path.as_ptr().cast(), vfs.as_mut_ptr()) } != 0 { + return Err(io::Error::last_os_error()); + } + + let vfs = unsafe { vfs.assume_init() }; + let block_size = vfs.f_bsize; + let total_blocks = vfs.f_blocks; + let available_blocks = vfs.f_bavail; let total_size = block_size * total_blocks; let used_size = total_size - (block_size * available_blocks); @@ -81,6 +81,20 @@ pub fn get_root_disk_usage() -> Result { Ok(result) } +/// Fast integer parsing without stdlib overhead +#[inline] +fn parse_u64_fast(s: &[u8]) -> u64 { + let mut result = 0u64; + for &byte in s { + if byte.is_ascii_digit() { + result = result * 10 + u64::from(byte - b'0'); + } else { + break; + } + } + result +} + /// Gets the system memory usage information. /// /// # Errors @@ -90,30 +104,54 @@ pub fn get_root_disk_usage() -> Result { pub fn get_memory_usage() -> Result { #[cfg_attr(feature = "hotpath", hotpath::measure)] fn parse_memory_info() -> Result<(f64, f64), io::Error> { - let mut total_memory_kb = 0.0; - let mut available_memory_kb = 0.0; - let mut meminfo = String::with_capacity(2048); + let mut total_memory_kb = 0u64; + let mut available_memory_kb = 0u64; + let mut buffer = [0u8; 2048]; - File::open("/proc/meminfo")?.read_to_string(&mut meminfo)?; + // Use fast syscall-based file reading + let bytes_read = read_file_fast("/proc/meminfo", &mut buffer)?; + let meminfo = &buffer[..bytes_read]; - for line in meminfo.lines() { - let mut split = line.split_whitespace(); - match split.next().unwrap_or_default() { - "MemTotal:" => { - total_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0); - }, - "MemAvailable:" => { - available_memory_kb = - split.next().unwrap_or("0").parse().unwrap_or(0.0); - // MemTotal comes before MemAvailable, stop parsing - break; - }, - _ => (), + // Fast scanning for MemTotal and MemAvailable + let mut offset = 0; + let mut found_total = false; + let mut found_available = false; + + while offset < meminfo.len() && (!found_total || !found_available) { + let remaining = &meminfo[offset..]; + + // Find newline or end + let line_end = remaining + .iter() + .position(|&b| b == b'\n') + .unwrap_or(remaining.len()); + let line = &remaining[..line_end]; + + if line.starts_with(b"MemTotal:") { + // Skip "MemTotal:" and whitespace + let mut pos = 9; + while pos < line.len() && line[pos].is_ascii_whitespace() { + pos += 1; + } + total_memory_kb = parse_u64_fast(&line[pos..]); + found_total = true; + } else if line.starts_with(b"MemAvailable:") { + // Skip "MemAvailable:" and whitespace + let mut pos = 13; + while pos < line.len() && line[pos].is_ascii_whitespace() { + pos += 1; + } + available_memory_kb = parse_u64_fast(&line[pos..]); + found_available = true; } + + offset += line_end + 1; } - let total_memory_gb = total_memory_kb / 1024.0 / 1024.0; - let available_memory_gb = available_memory_kb / 1024.0 / 1024.0; + #[allow(clippy::cast_precision_loss)] + let total_memory_gb = total_memory_kb as f64 / 1024.0 / 1024.0; + #[allow(clippy::cast_precision_loss)] + let available_memory_gb = available_memory_kb as f64 / 1024.0 / 1024.0; let used_memory_gb = total_memory_gb - available_memory_gb; Ok((used_memory_gb, total_memory_gb)) From 07afedd0cc4a49630b23ea830fe56c847eff06da Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 21:42:38 +0300 Subject: [PATCH 3/5] docs: update binary size Signed-off-by: NotAShelf Change-Id: I1a9189f90666d7efc010a7255c287bd86a6a6964 --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5ae0f72..a40e444 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ on your system: it is pretty _[fast](#benchmarks)_... - Fast - Really fast - Minimal dependencies -- Tiny binary (~410kb) +- Tiny binary (~370kb) - Actually really fast - Cool NixOS logo (other, inferior, distros are not supported) - Reliable detection of following info: @@ -160,7 +160,8 @@ performance regressions. > [!NOTE] > You will need a Nerdfonts patched font installed, and for your terminal -> emulator to support said font. Microfetch uses nerdfonts glyphs by default. +> emulator to support said font. Microfetch uses nerdfonts glyphs by default, +> but this can be changed by [patching the program](#customizing). Microfetch is packaged in [nixpkgs](https://github.com/nixos/nixpkgs). It can be installed by adding `pkgs.microfetch` to your `environment.systemPackages`. From 6f8d1ffa83fec677001e48de51c3a930885d647d Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 21:48:15 +0300 Subject: [PATCH 4/5] nix: include benches in source filter; fix build Signed-off-by: NotAShelf Change-Id: Id78ee5f62a5168feef09b5f8713b107c6a6a6964 --- nix/package.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nix/package.nix b/nix/package.nix index 5bb387c..dca3cc2 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -20,6 +20,7 @@ in (fs.fileFilter (file: builtins.any file.hasExt ["rs"]) (s + /src)) (s + /Cargo.lock) (s + /Cargo.toml) + (s + /benches) ]; }; From 353b78e688ddffd86540bf336b52568499f980ec Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Tue, 18 Nov 2025 00:07:26 +0300 Subject: [PATCH 5/5] ci: fix hotpath-comment usage Signed-off-by: NotAShelf Change-Id: Idc163948f012efb07fc7a6a952af54b36a6a6964 --- .github/workflows/hotpath-comment.yml | 46 +++++++++++---------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/.github/workflows/hotpath-comment.yml b/.github/workflows/hotpath-comment.yml index 395a533..8f83d00 100644 --- a/.github/workflows/hotpath-comment.yml +++ b/.github/workflows/hotpath-comment.yml @@ -13,7 +13,7 @@ jobs: comment: runs-on: ubuntu-latest if: ${{ github.event.workflow_run.conclusion == 'success' }} - + steps: - name: Download profiling results uses: actions/download-artifact@v4 @@ -21,37 +21,29 @@ jobs: name: hotpath-results github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id }} - + - name: Read PR number id: pr run: echo "number=$(cat pr_number.txt)" >> $GITHUB_OUTPUT - + - name: Setup Rust uses: actions-rust-lang/setup-rust-toolchain@v1 - + - name: Install hotpath CLI run: cargo install hotpath - + - name: Post timing comparison comment - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - hotpath profile-pr \ - --repo ${{ github.repository }} \ - --pr-number ${{ steps.pr.outputs.number }} \ - --head-json head-timing.json \ - --base-json base-timing.json \ - --mode timing \ - --title "⏱️ Hotpath Timing Profile" - - - name: Post allocation comparison comment - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - hotpath profile-pr \ - --repo ${{ github.repository }} \ - --pr-number ${{ steps.pr.outputs.number }} \ - --head-json head-alloc.json \ - --base-json base-alloc.json \ - --mode alloc \ - --title "📊 Hotpath Allocation Profile" + run: | + hotpath profile-pr \ + --head-metrics head-timing.json \ + --base-metrics base-timing.json \ + --github-token ${{ secrets.GITHUB_TOKEN }} \ + --pr-number ${{ steps.pr.outputs.number }} + + - name: Post allocation comparison comment + run: | + hotpath profile-pr \ + --head-metrics head-alloc.json \ + --base-metrics base-alloc.json \ + --github-token ${{ secrets.GITHUB_TOKEN }} \ + --pr-number ${{ steps.pr.outputs.number }}