From 907112f2d158c4ad278789b373afa85d2c199daf Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 15 Aug 2024 18:32:02 +0800 Subject: [PATCH 1/3] micro optimizations (#8) * perf: break early after parsing required meminfo Also a match statement for compiler magic. * perf: pre-allocate strings when reading files * refactor: remove duplicate .to_string() * perf: try to print everything in one syscall println! sends a syscall for each line. * perf: get rid of duplicate uname syscall * perf: simplify first letter capitalization * refactor: directly use key in match statement --- src/desktop.rs | 23 ++++++++--------------- src/main.rs | 12 ++++++++---- src/release.rs | 22 +++++++++++----------- src/system.rs | 37 +++++++++++++++++++++++++------------ 4 files changed, 52 insertions(+), 42 deletions(-) diff --git a/src/desktop.rs b/src/desktop.rs index bf0da35..f35cd2a 100644 --- a/src/desktop.rs +++ b/src/desktop.rs @@ -1,21 +1,13 @@ pub fn get_desktop_info() -> String { - fn capitalize_first_letter(s: &str) -> String { - if s.is_empty() { - return String::new(); - } - - let mut chars = s.chars(); - let first_char = chars.next().unwrap().to_uppercase().to_string(); - let rest: String = chars.collect(); - first_char + &rest - } - // Retrieve the environment variables and handle Result types let desktop_env = std::env::var("XDG_CURRENT_DESKTOP"); let display_backend_result = std::env::var("XDG_SESSION_TYPE"); // Capitalize the first letter of the display backend value - let display_backend = capitalize_first_letter(display_backend_result.as_deref().unwrap_or("")); + let mut display_backend = display_backend_result.unwrap_or_default(); + if let Some(c) = display_backend.as_mut_str().get_mut(0..1) { + c.make_ascii_uppercase(); + } // Trim "none+" from the start of desktop_env if present // Use "Unknown" if desktop_env is empty or has an error @@ -26,10 +18,11 @@ pub fn get_desktop_info() -> String { // Handle the case where display_backend might be empty after capitalization let display_backend = if display_backend.is_empty() { - "Unknown".to_string() + "Unknown" } else { - display_backend - }; + &display_backend + } + .to_string(); format!("{desktop_env} ({display_backend})") } diff --git a/src/main.rs b/src/main.rs index aace49d..3da592e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,8 @@ mod release; mod system; mod uptime; +use std::io::Write; + use crate::colors::{print_dots, BLUE, CYAN, RESET}; use crate::desktop::get_desktop_info; use crate::release::{get_os_pretty_name, get_system_info}; @@ -15,10 +17,11 @@ use color_eyre::Report; fn main() -> Result<(), Report> { color_eyre::install()?; + let utsname = nix::sys::utsname::uname()?; let fields = Fields { - user_info: get_username_and_hostname(), + user_info: get_username_and_hostname(&utsname), os_name: get_os_pretty_name()?, - kernel_version: get_system_info()?, + kernel_version: get_system_info(&utsname)?, shell: get_shell(), desktop: get_desktop_info(), uptime: get_current()?, @@ -61,7 +64,7 @@ fn print_system_info(fields: &Fields) { colors, } = fields; - println!( + let _ = std::io::stdout().write_all(format!( " {CYAN} ▟█▖ {BLUE}▝█▙ ▗█▛ {user_info} ~{RESET} {CYAN} ▗▄▄▟██▄▄▄▄▄{BLUE}▝█▙█▛ {CYAN}▖ {CYAN} {BLUE}System{RESET}  {os_name} @@ -71,5 +74,6 @@ fn print_system_info(fields: &Fields) { {BLUE} ▟█▛{CYAN}▗█▖ {CYAN}▟█▛ {CYAN} {BLUE}Desktop{RESET}  {desktop} {BLUE} ▝█▛ {CYAN}██▖{BLUE}▗▄▄▄▄▄▄▄▄▄▄▄ {CYAN}󰍛 {BLUE}Memory{RESET}  {memory_usage} {BLUE} ▝ {CYAN}▟█▜█▖{BLUE}▀▀▀▀▀██▛▀▀▘ {CYAN}󱥎 {BLUE}Storage (/){RESET}  {storage} - {CYAN} ▟█▘ ▜█▖ {BLUE}▝█▛ {CYAN} {BLUE}Colors{RESET}  {colors}"); + {CYAN} ▟█▘ ▜█▖ {BLUE}▝█▛ {CYAN} {BLUE}Colors{RESET}  {colors} +").as_bytes()); } diff --git a/src/release.rs b/src/release.rs index 4c6a9b8..7600d0e 100644 --- a/src/release.rs +++ b/src/release.rs @@ -1,9 +1,11 @@ use color_eyre::Result; -use std::fs::read_to_string; -use std::io; +use nix::sys::utsname::UtsName; +use std::{ + fs::File, + io::{self, Read}, +}; -pub fn get_system_info() -> nix::Result { - let utsname = nix::sys::utsname::uname()?; +pub fn get_system_info(utsname: &UtsName) -> nix::Result { Ok(format!( "{} {} ({})", utsname.sysname().to_str().unwrap_or("Unknown"), @@ -13,15 +15,13 @@ pub fn get_system_info() -> nix::Result { } pub fn get_os_pretty_name() -> Result { - let os_release_content = read_to_string("/etc/os-release")?; + let mut os_release_content = String::with_capacity(1024); + File::open("/etc/os-release")?.read_to_string(&mut os_release_content)?; + let pretty_name = os_release_content .lines() .find(|line| line.starts_with("PRETTY_NAME=")) - .map(|line| { - line.trim_start_matches("PRETTY_NAME=") - .trim_matches('"') - .to_string() - }); + .map(|line| line.trim_start_matches("PRETTY_NAME=").trim_matches('"')); - Ok(pretty_name.unwrap_or("Unknown".to_string())) + Ok(pretty_name.unwrap_or("Unknown").to_string()) } diff --git a/src/system.rs b/src/system.rs index 964aec1..74d774f 100644 --- a/src/system.rs +++ b/src/system.rs @@ -1,15 +1,21 @@ use color_eyre::Result; -use nix::sys::statvfs::statvfs; +use nix::sys::{statvfs::statvfs, utsname::UtsName}; -use std::env; -use std::io::{self}; +use std::{ + env, + fs::File, + io::{self, Read}, +}; use crate::colors::{CYAN, GREEN, RED, RESET, YELLOW}; -pub fn get_username_and_hostname() -> String { +pub fn get_username_and_hostname(utsname: &UtsName) -> String { let username = env::var("USER").unwrap_or("unknown_user".to_string()); - let hostname = nix::unistd::gethostname().unwrap_or("unknown_host".to_string().into()); - let hostname = hostname.to_string_lossy(); + let hostname = utsname + .nodename() + .to_str() + .unwrap_or("unknown_host") + .to_string(); format!("{YELLOW}{username}{RED}@{GREEN}{hostname}") } @@ -45,13 +51,20 @@ pub fn get_memory_usage() -> Result { let mut total_memory_kb = 0.0; let mut available_memory_kb = 0.0; - for line in std::fs::read_to_string("/proc/meminfo")?.lines() { + let mut meminfo = String::with_capacity(2048); + File::open("/proc/meminfo")?.read_to_string(&mut meminfo)?; + + for line in meminfo.lines() { let mut split = line.split_whitespace(); - let key = split.next().unwrap_or(""); - if key == "MemTotal:" { - total_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0); - } else if key == "MemAvailable:" { - available_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0); + 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; + } + _ => (), } } From f1e8ca87730237469cec5c51d52ee2a5f54cae31 Mon Sep 17 00:00:00 2001 From: vali Date: Thu, 15 Aug 2024 17:43:39 +0000 Subject: [PATCH 2/3] add benchmarking for individual functions (#9) * add benchmarking * update README to include benchmarking of individual functions * README: formatting * README: add link to Criterion's getting_started * shell.nix: add gnuplot Since Criterion.rs uses gnuplot to generate nice plots, add it to the shell. * Cargo.toml: fixed microfetch binary name * benchmark.rs: fix benchmark function's name * Update README.md --------- Co-authored-by: raf --- Cargo.lock | 572 +++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 15 ++ README.md | 6 + benches/benchmark.rs | 27 ++ nix/shell.nix | 3 + src/lib.rs | 5 + 6 files changed, 628 insertions(+) create mode 100644 benches/benchmark.rs create mode 100644 src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 1f39265..d798c09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,33 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + [[package]] name = "backtrace" version = "0.3.71" @@ -38,6 +65,18 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.1.7" @@ -56,6 +95,58 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d8838454fda655dafd3accb2b6e2bea645b9e4078abe84a22ceb947235c5cc" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + [[package]] name = "color-eyre" version = "0.6.3" @@ -69,6 +160,79 @@ dependencies = [ "owo-colors", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "eyre" version = "0.6.12" @@ -85,18 +249,75 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "indenter" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + [[package]] name = "memchr" version = "2.7.4" @@ -108,6 +329,7 @@ name = "microfetch" version = "0.3.5" dependencies = [ "color-eyre", + "criterion", "nix", ] @@ -132,6 +354,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.32.2" @@ -147,14 +378,355 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "oorandom" +version = "11.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" + [[package]] name = "owo-colors" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "plotters" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" + +[[package]] +name = "plotters-svg" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.207" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.207" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index 347b0bc..9918e33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,10 +3,25 @@ name = "microfetch" version = "0.3.5" edition = "2021" +[lib] +name = "microfetch_lib" +path = "src/lib.rs" + +[[bin]] +name = "microfetch" +path = "src/main.rs" + [dependencies] nix = { version = "0.29", features = ["fs", "hostname", "feature"] } color-eyre = { version = "0.6", default-features = false } +[dev-dependencies] +criterion = "0.5" + +[[bench]] +name = "benchmark" +harness = false + [profile.dev] opt-level = 3 diff --git a/README.md b/README.md index 88d7d39..d1fb3ca 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,12 @@ there is. The only downside of using Rust is introducing more "bloated" dependency trees and increasing build times. The latter is easily mitigated with Nix's binary cache, though._ +[Criterion.rs]: https://github.com/bheisler/criterion.rs +[Getting Started Guide]: https://bheisler.github.io/criterion.rs/book/getting_started.html + +To benchmark individual functions, [Criterion.rs] is used. See Criterion's +[Getting Started Guide] for details or just run `cargo bench` to benchmark +all features of Microfetch ## Customizing You can't\*. diff --git a/benches/benchmark.rs b/benches/benchmark.rs new file mode 100644 index 0000000..7706617 --- /dev/null +++ b/benches/benchmark.rs @@ -0,0 +1,27 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use microfetch_lib::colors::print_dots; +use microfetch_lib::desktop::get_desktop_info; +use microfetch_lib::release::{get_os_pretty_name, get_system_info}; +use microfetch_lib::system::{ + get_memory_usage, get_root_disk_usage, get_shell, get_username_and_hostname, +}; +use microfetch_lib::uptime::get_current; + +fn main_benchmark(c: &mut Criterion) { + let utsname = nix::sys::utsname::uname().expect("lol"); + c.bench_function("user_info", |b| { + b.iter(|| get_username_and_hostname(&utsname)) + }); + c.bench_function("os_name", |b| b.iter(get_os_pretty_name)); + c.bench_function("kernel_version", |b| b.iter(|| get_system_info(&utsname))); + c.bench_function("shell", |b| b.iter(get_shell)); + + c.bench_function("desktop", |b| b.iter(get_desktop_info)); + c.bench_function("uptime", |b| b.iter(get_current)); + c.bench_function("memory_usage", |b| b.iter(get_memory_usage)); + c.bench_function("storage", |b| b.iter(get_root_disk_usage)); + c.bench_function("colors", |b| b.iter(print_dots)); +} + +criterion_group!(benches, main_benchmark); +criterion_main!(benches); diff --git a/nix/shell.nix b/nix/shell.nix index 5efe65c..2350dbd 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -7,6 +7,7 @@ rustc, gcc, rustPlatform, + gnuplot, }: mkShell { strictDeps = true; @@ -19,6 +20,8 @@ mkShell { rust-analyzer-unwrapped rustfmt clippy + + gnuplot # For Criterion.rs plots ]; env.RUST_SRC_PATH = "${rustPlatform.rustLibSrc}"; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..4ba33f8 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,5 @@ +pub mod colors; +pub mod desktop; +pub mod release; +pub mod system; +pub mod uptime; From 3bb2a0bd10f871aa1f5732c87619f183820426a2 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Thu, 15 Aug 2024 20:45:46 +0300 Subject: [PATCH 3/3] update demo again, again Use a better resolution, strip, use new RAM calculation --- .github/assets/demo.png | Bin 20668 -> 21010 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/.github/assets/demo.png b/.github/assets/demo.png index 8a40cf788c6465a340d72d05b68f1870868139aa..137b84523c9ce01d2015e020926ae200c44b5957 100644 GIT binary patch literal 21010 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 literal 20668 zcmZ5{WmuF^*EI-Aw{(YeBQUfeAt4RY-Q6H5-5@Pe3P>|__s}ifF(BRD@ZIS1yx)7h z{21n%nLEz8_t|^xwblt&l$Sz7CPIdRfkBi0Ag&Ap^CB5|KZf`Mc)d|J{Q(1`7$_|+ zs_HSnKcy?H0!z^DDAH54F&Amuh$D|SqQlVmIh$5Vx?hk$gQbeI?l@StEBgnhYCr^4 zz)NbB!S7Si%Cx1CjXP8Ub~)SQ^t(pnKO7AzK*7Ol#82d-4{)Q-Cu}${Fy*h39Dz%I z0KvdeKw)6M1i`_)gTTVTAR)lO(2&5uV59%vFH^*vZaV^~i`&D8&i#ff4`0AA6CvSa z%L7Nzqz5H90LMO&0*5D3LB$}_*Sl}sDqT;FRPla7)G|lkaJNZ4YPwDKZN&%k;aX!w zx)dEO?5{4IWNv;@kSoegFDq-qN6%0DKChNk21^0;6NAjOB`MS|XzFP~Jsj7+a6_BK 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