Skip to content

perf(ui/state): borrow &str key in compute_grouped_rows, drop N per-connection String clones#410

Open
obchain wants to merge 1 commit into
domcyrus:mainfrom
obchain:perf/grouped-rows-borrow-key
Open

perf(ui/state): borrow &str key in compute_grouped_rows, drop N per-connection String clones#410
obchain wants to merge 1 commit into
domcyrus:mainfrom
obchain:perf/grouped-rows-borrow-key

Conversation

@obchain

@obchain obchain commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Closes #409

What

compute_grouped_rows is called every render frame when grouping is active. It bucketed connections into a HashMap<String, Vec<&Connection>>, cloning conn.process_name once per connection to produce an owned String key:

// before
let mut groups: HashMap<String, Vec<&Connection>> = HashMap::new();
for conn in connections {
    let key = conn
        .process_name
        .clone()                                     // one String clone per connection
        .unwrap_or_else(|| "<unknown>".to_string()); // one alloc for the fallback
    groups.entry(key).or_default().push(conn);
}

With N visible connections this produced N String allocations — one per connection — that existed only as map keys and were dropped after building the group stats. The connections: &'a [Connection] slice is already live for the full lifetime 'a, so the process name can be borrowed directly:

// after
let mut groups: HashMap<&'a str, Vec<&'a Connection>> = HashMap::new();
for conn in connections {
    let key = conn.process_name.as_deref().unwrap_or("<unknown>"); // zero alloc
    groups.entry(key).or_default().push(conn);
}

The downstream group_stats intermediate changes from Vec<(String, …)> to Vec<(&'a str, …)>. GroupedRow still stores process_name: String, so the name.clone() calls at row-push sites become name.to_owned() — the same allocation count there, but the N per-connection clones in the grouping loop are eliminated.

expanded_groups.contains(&name) (where name: String) becomes expanded_groups.contains(name) (where name: &str), valid via String: Borrow<str>.

Impact

Per grouped render: −N String allocations where N is the number of visible connections. With 100 connections across 10 groups, that is 100 clone + 100 heap-alloc + 100 drop cycles saved every frame.

Verification

cargo build --workspace            # clean
cargo test --workspace --lib       # 78 passed, 0 failed
cargo fmt --check                  # clean
cargo clippy --all-targets -- -D warnings  # clean

compute_grouped_rows is called every render frame when grouping is active.
It grouped connections into a HashMap<String, Vec<&Connection>>, cloning
conn.process_name once per connection to produce the owned key — N
String allocs that existed only as map keys and were dropped immediately
after building the stats.

Since connections: &'a [Connection] is already live for the lifetime 'a,
the process name can be borrowed directly:

    let key = conn.process_name.as_deref().unwrap_or("<unknown>");

HashMap changes to HashMap<&'a str, Vec<&'a Connection>>.  The downstream
group_stats Vec changes from Vec<(String, …)> to Vec<(&'a str, …)>.
GroupedRow still stores process_name: String, so the name.clone() calls
at row-push sites become name.to_owned() — same allocation count there,
but the N per-connection clones in the grouping loop are eliminated.

Closes domcyrus#409
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(ui/state): borrow process name as &str key in compute_grouped_rows, drop N per-connection String clones

1 participant