Hmbown / Hmbown/Codewhale

v0.9.14: Eliminate per-frame/per-delta recomputation and clones in the TUI hot path

Open
#6,213 1 comment 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Rust
Stars
41k
Forks
3.6k
Avg merge
13h 59m
Merged PRs (30d)
299

Description

From `codewhale-ops/PERF-OPPORTUNITIES-20260915.md` §3 (T1, T4–T7) — per-frame and per-delta work in the TUI that scales with output size / conversation length / catalog size. Each item independently shippable; confirm the call path on [S] items.

- [ ] **T1 [V]** — `tui/src/tui/output_rows_cache.rs:107`: on a cache **hit** (the common case during streaming at up to 120 FPS) it returns `entry.rows.clone()` — every `String` + styled span per row. Caller `history/tool_output.rs:372` also runs `hash_str(output)` over the full output bytes per frame per cell to build the key. Fix: store `Arc>` and clone the Arc (the "caller iterates without a lock" property the doc comment wants is preserved); key the cache externally by `(tool-result id, width)` so the per-frame full-output hash disappears.
- [ ] **T4 [V]** — `core/engine/turn_loop.rs:5234–5262`: every `InputJsonDelta` pushes `partial_json`, then `parse_tool_input` runs the full repair ladder (`arg_repair::repair` + up to 3 `serde_json::from_str` fallbacks) over the **entire** accumulated buffer and `parsed.value.clone()`s the result. A 100KB `apply_patch` in ~2KB deltas = ~50 full parses + 50 deep clones per tool call, on the per-token path. Fix: mirror into `tool_state.input` only at `ContentBlockStop` (already the authoritative parse point), or every N bytes / on a debounce; `swap` the parsed value in instead of cloning. (The sibling O(n²) verbose-log copy at this site was already fixed — this is the surviving half.)
- [ ] **T5 [S]** — `core/engine/tool_catalog.rs:737–744,853–866`: `tool_search` runs `tool.input_schema.to_string().to_lowercase()` per deferred tool per query, plus `tool.name.to_lowercase()` inside the per-term scoring loop. The catalog is static between queries — build `haystack` + `name_lower` once at catalog construction (side map keyed by tool name).
- [ ] **T6 [S]** — `tui/views/mod.rs:2696–2729` `row_matches_filter`: ~11 `to_lowercase` allocations per settings row, several passes per interaction (`visible_items()` + `matching_row_indices()`). Compute lowered filter terms once per `update_filter`; cache a lowered search blob per `ConfigRow` at load.
- [ ] **T7 [S]** — smaller verified shapes:
- `tui/src/mcp.rs:1239` `resource_uri_matches_template` — regex built+compiled per template per URI inside an `.any()`. Cache compiled templates per server config.
- `tui/src/tui/footer_ui.rs:59` `friendly_subagent_progress` — re-parses the objective JSON on **every** `AgentProgress` event; the objective is immutable per agent. Compute once at spawn.
- `tui/src/client/anthropic.rs:800–838` — each SSE event parses to `serde_json::Value` then converts via `from_value` (double allocation per token). Deserialize each event type directly with a tagged enum.
- `tui/src/client/chat.rs:2159` — serializes the whole tool catalog to a `String` per prompt inspection; hash typed fields instead.

Reusable idioms: parse once at boundary events (not per delta); `Arc` handles for cache hits; precompute lowered/serialized lookup material at construction, not per query.

Contributor guide

Open the contributing guide

Research direction

Start with PERF-OPPORTUNITIES-20260915.md §3 and trace the listed entry points: output_rows_cache.rs, history/tool_output.rs, turn_loop.rs, tool_catalog.rs, tui/views/mod.rs, mcp.rs, footer_ui.rs, anthropic.rs, and chat.rs. Confirm each call path, then benchmark or inspect the hot paths to verify that repeated parsing, cloning, hashing, lowercasing, regex compilation, and serialization no longer occur at their current frequency.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cli, performance
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.