Skip to content

Commit 11a7264

Browse files
authored
Merge pull request #22 from NotAShelf/notashelf/push-uysmruuoyvnz
various: reduce allocations where available
2 parents 2a6fe2a + 789ece8 commit 11a7264

File tree

9 files changed

+268
-65
lines changed

9 files changed

+268
-65
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: Hotpath Comment
2+
3+
on:
4+
workflow_run:
5+
workflows: ["Hotpath Profile"]
6+
types:
7+
- completed
8+
9+
permissions:
10+
pull-requests: write
11+
12+
jobs:
13+
comment:
14+
runs-on: ubuntu-latest
15+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
16+
17+
steps:
18+
- name: Download profiling results
19+
uses: actions/download-artifact@v4
20+
with:
21+
name: hotpath-results
22+
github-token: ${{ secrets.GITHUB_TOKEN }}
23+
run-id: ${{ github.event.workflow_run.id }}
24+
25+
- name: Read PR number
26+
id: pr
27+
run: echo "number=$(cat pr_number.txt)" >> $GITHUB_OUTPUT
28+
29+
- name: Setup Rust
30+
uses: actions-rust-lang/setup-rust-toolchain@v1
31+
32+
- name: Install hotpath CLI
33+
run: cargo install hotpath
34+
35+
- name: Post timing comparison comment
36+
env:
37+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38+
run: |
39+
hotpath profile-pr \
40+
--repo ${{ github.repository }} \
41+
--pr-number ${{ steps.pr.outputs.number }} \
42+
--head-json head-timing.json \
43+
--base-json base-timing.json \
44+
--mode timing \
45+
--title "⏱️ Hotpath Timing Profile"
46+
47+
- name: Post allocation comparison comment
48+
env:
49+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50+
run: |
51+
hotpath profile-pr \
52+
--repo ${{ github.repository }} \
53+
--pr-number ${{ steps.pr.outputs.number }} \
54+
--head-json head-alloc.json \
55+
--base-json base-alloc.json \
56+
--mode alloc \
57+
--title "📊 Hotpath Allocation Profile"
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Hotpath Profile
2+
3+
on:
4+
pull_request:
5+
branches: [ "main" ]
6+
7+
env:
8+
CARGO_TERM_COLOR: always
9+
10+
jobs:
11+
profile:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Checkout PR HEAD
16+
uses: actions/checkout@v4
17+
18+
- name: Setup Rust
19+
uses: actions-rust-lang/setup-rust-toolchain@v1
20+
21+
- name: Run timing profiling on HEAD
22+
env:
23+
HOTPATH_JSON: "true"
24+
run: |
25+
cargo run --features='hotpath' 2>&1 | grep '^{"hotpath_profiling_mode"' > head-timing.json
26+
27+
- name: Run allocation profiling on HEAD
28+
env:
29+
HOTPATH_JSON: "true"
30+
run: |
31+
cargo run --features='hotpath,hotpath-alloc-count-total' 2>&1 | grep '^{"hotpath_profiling_mode"' > head-alloc.json
32+
33+
- name: Checkout base branch
34+
uses: actions/checkout@v4
35+
with:
36+
ref: ${{ github.event.pull_request.base.sha }}
37+
38+
- name: Run timing profiling on base
39+
env:
40+
HOTPATH_JSON: "true"
41+
run: |
42+
cargo run --features='hotpath' 2>&1 | grep '^{"hotpath_profiling_mode"' > base-timing.json
43+
44+
- name: Run allocation profiling on base
45+
env:
46+
HOTPATH_JSON: "true"
47+
run: |
48+
cargo run --features='hotpath,hotpath-alloc-count-total' 2>&1 | grep '^{"hotpath_profiling_mode"' > base-alloc.json
49+
50+
- name: Save PR number
51+
run: echo "${{ github.event.number }}" > pr_number.txt
52+
53+
- name: Upload profiling results
54+
uses: actions/upload-artifact@v4
55+
with:
56+
name: hotpath-results
57+
path: |
58+
head-timing.json
59+
head-alloc.json
60+
base-timing.json
61+
base-alloc.json
62+
pr_number.txt
63+
retention-days: 1

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "microfetch"
3-
version = "0.4.9"
3+
version = "0.4.10"
44
edition = "2024"
55

66
[lib]

src/colors.rs

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,45 @@ impl Colors {
3737
}
3838

3939
pub static COLORS: LazyLock<Colors> = LazyLock::new(|| {
40-
// check for NO_COLOR once at startup
40+
// Check for NO_COLOR once at startup
4141
let is_no_color = env::var("NO_COLOR").is_ok();
4242
Colors::new(is_no_color)
4343
});
4444

4545
#[must_use]
4646
#[cfg_attr(feature = "hotpath", hotpath::measure)]
4747
pub fn print_dots() -> String {
48-
format!(
49-
"{} {} {} {} {} {} {}",
50-
COLORS.blue,
51-
COLORS.cyan,
52-
COLORS.green,
53-
COLORS.yellow,
54-
COLORS.red,
55-
COLORS.magenta,
56-
COLORS.reset,
57-
)
48+
// Pre-calculate capacity: 6 color codes + " " (glyph + 2 spaces) per color
49+
const GLYPH: &str = "";
50+
let capacity = COLORS.blue.len()
51+
+ COLORS.cyan.len()
52+
+ COLORS.green.len()
53+
+ COLORS.yellow.len()
54+
+ COLORS.red.len()
55+
+ COLORS.magenta.len()
56+
+ COLORS.reset.len()
57+
+ (GLYPH.len() + 2) * 6;
58+
59+
let mut result = String::with_capacity(capacity);
60+
result.push_str(COLORS.blue);
61+
result.push_str(GLYPH);
62+
result.push_str(" ");
63+
result.push_str(COLORS.cyan);
64+
result.push_str(GLYPH);
65+
result.push_str(" ");
66+
result.push_str(COLORS.green);
67+
result.push_str(GLYPH);
68+
result.push_str(" ");
69+
result.push_str(COLORS.yellow);
70+
result.push_str(GLYPH);
71+
result.push_str(" ");
72+
result.push_str(COLORS.red);
73+
result.push_str(GLYPH);
74+
result.push_str(" ");
75+
result.push_str(COLORS.magenta);
76+
result.push_str(GLYPH);
77+
result.push_str(" ");
78+
result.push_str(COLORS.reset);
79+
80+
result
5881
}

src/desktop.rs

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,37 @@
1+
use std::fmt::Write;
2+
13
#[must_use]
24
#[cfg_attr(feature = "hotpath", hotpath::measure)]
35
pub fn get_desktop_info() -> String {
46
// Retrieve the environment variables and handle Result types
57
let desktop_env = std::env::var("XDG_CURRENT_DESKTOP");
6-
let display_backend_result = std::env::var("XDG_SESSION_TYPE");
8+
let display_backend = std::env::var("XDG_SESSION_TYPE");
79

8-
// Capitalize the first letter of the display backend value
9-
let mut display_backend = display_backend_result.unwrap_or_default();
10-
if let Some(c) = display_backend.as_mut_str().get_mut(0..1) {
11-
c.make_ascii_uppercase();
12-
}
10+
let desktop_str = match desktop_env {
11+
Err(_) => "Unknown",
12+
Ok(ref s) if s.starts_with("none+") => &s[5..],
13+
Ok(ref s) => s.as_str(),
14+
};
1315

14-
// Trim "none+" from the start of desktop_env if present
15-
// Use "Unknown" if desktop_env is empty or has an error
16-
let desktop_env = match desktop_env {
17-
Err(_) => "Unknown".to_owned(),
18-
Ok(s) => s.trim_start_matches("none+").to_owned(),
16+
let backend_str = match display_backend {
17+
Err(_) => "Unknown",
18+
Ok(ref s) if s.is_empty() => "Unknown",
19+
Ok(ref s) => s.as_str(),
1920
};
2021

21-
// Handle the case where display_backend might be empty after capitalization
22-
let display_backend = if display_backend.is_empty() {
23-
"Unknown"
24-
} else {
25-
&display_backend
22+
// Pre-calculate capacity: desktop_len + " (" + backend_len + ")"
23+
// Capitalize first char needs temporary allocation only if backend exists
24+
let mut result =
25+
String::with_capacity(desktop_str.len() + backend_str.len() + 3);
26+
result.push_str(desktop_str);
27+
result.push_str(" (");
28+
29+
// Capitalize first character of backend
30+
if let Some(first_char) = backend_str.chars().next() {
31+
let _ = write!(result, "{}", first_char.to_ascii_uppercase());
32+
result.push_str(&backend_str[first_char.len_utf8()..]);
2633
}
27-
.to_owned();
2834

29-
format!("{desktop_env} ({display_backend})")
35+
result.push(')');
36+
result
3037
}

src/release.rs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,39 @@
11
use std::{
2+
fmt::Write as _,
23
fs::File,
3-
io::{self, BufRead, BufReader},
4+
io::{self, Read},
45
};
56

67
use nix::sys::utsname::UtsName;
78

89
#[must_use]
910
#[cfg_attr(feature = "hotpath", hotpath::measure)]
1011
pub fn get_system_info(utsname: &UtsName) -> String {
11-
format!(
12-
"{} {} ({})",
13-
utsname.sysname().to_str().unwrap_or("Unknown"),
14-
utsname.release().to_str().unwrap_or("Unknown"),
15-
utsname.machine().to_str().unwrap_or("Unknown")
16-
)
12+
let sysname = utsname.sysname().to_str().unwrap_or("Unknown");
13+
let release = utsname.release().to_str().unwrap_or("Unknown");
14+
let machine = utsname.machine().to_str().unwrap_or("Unknown");
15+
16+
// Pre-allocate capacity: sysname + " " + release + " (" + machine + ")"
17+
let capacity = sysname.len() + 1 + release.len() + 2 + machine.len() + 1;
18+
let mut result = String::with_capacity(capacity);
19+
20+
write!(result, "{sysname} {release} ({machine})").unwrap();
21+
result
1722
}
1823

24+
/// Gets the pretty name of the OS from `/etc/os-release`.
25+
///
26+
/// # Errors
27+
///
28+
/// Returns an error if `/etc/os-release` cannot be read.
1929
#[cfg_attr(feature = "hotpath", hotpath::measure)]
2030
pub fn get_os_pretty_name() -> Result<String, io::Error> {
21-
let file = File::open("/etc/os-release")?;
22-
let reader = BufReader::new(file);
31+
// We use a stack-allocated buffer here, which seems to perform MUCH better
32+
// than `BufReader`. In hindsight, I should've seen this coming.
33+
let mut buffer = String::with_capacity(1024);
34+
File::open("/etc/os-release")?.read_to_string(&mut buffer)?;
2335

24-
for line in reader.lines() {
25-
let line = line?;
36+
for line in buffer.lines() {
2637
if let Some(pretty_name) = line.strip_prefix("PRETTY_NAME=") {
2738
if let Some(trimmed) = pretty_name
2839
.strip_prefix('"')

0 commit comments

Comments
 (0)