fix(tui): allocate graph history lazily #63

Merged
Lochnair merged 1 commits from fix/tui-lazy-graph-history into main 2026-08-10 16:59:11 +00:00
+11 -1
View File
@@ -550,7 +550,7 @@ impl TuiTargetState {
session: None, session: None,
status: TargetStatus::Opening, status: TargetStatus::Opening,
negotiated: None, negotiated: None,
graph_history: VecDeque::with_capacity(HISTORY_LIMIT), graph_history: VecDeque::new(),
last_sample: None, last_sample: None,
chatgpt-codex-connector[bot] commented 2026-08-10 16:46:56 +00:00 (Migrated from github.com)
Review

P2 Badge Cap lazy growth at the history limit

For a target receiving at least 65,537 replies, VecDeque's geometric growth increases this buffer's capacity from 65,536 to 131,072; push_bounded later limits its length to 100,000 but never releases the 31,072 excess slots. Long-running active targets therefore retain more graph-history heap than the previous exact-limit allocation, with the overhead multiplied in multi-target runs, so grow the queue in capped increments or reserve only the final remainder as it approaches HISTORY_LIMIT.

AGENTS.md reference: AGENTS.md:L53-L58

Useful? React with 👍 / 👎.

**<sub><sub>![P2 Badge](https://img.shields.io/badge/P2-yellow?style=flat)</sub></sub> Cap lazy growth at the history limit** For a target receiving at least 65,537 replies, `VecDeque`'s geometric growth increases this buffer's capacity from 65,536 to 131,072; `push_bounded` later limits its length to 100,000 but never releases the 31,072 excess slots. Long-running active targets therefore retain more graph-history heap than the previous exact-limit allocation, with the overhead multiplied in multi-target runs, so grow the queue in capped increments or reserve only the final remainder as it approaches `HISTORY_LIMIT`. AGENTS.md reference: [AGENTS.md:L53-L58](https://github.com/Lochnair/irtt-rs/blob/61ed029988b39caec1b7fad1850c038c01ac4a06/AGENTS.md#L53-L58) Useful? React with 👍 / 👎.
Lochnair commented 2026-08-10 16:55:08 +00:00 (Migrated from github.com)
Review

Fixed in push_bounded: once capacity reaches half of limit, we now reserve_exact up to limit instead of letting the next geometric doubling overshoot it, so long-running targets no longer retain the extra ~31k slots.

🤖 Addressed by Claude Code

Fixed in `push_bounded`: once capacity reaches half of `limit`, we now `reserve_exact` up to `limit` instead of letting the next geometric doubling overshoot it, so long-running targets no longer retain the extra ~31k slots. _🤖 Addressed by [Claude Code](https://claude.com/claude-code)_
last_warning: None, last_warning: None,
stats: StatsCollector::new(stats_config), stats: StatsCollector::new(stats_config),
@@ -1019,6 +1019,11 @@ pub(super) struct LastSample {
fn push_bounded<T>(items: &mut VecDeque<T>, item: T, limit: usize) { fn push_bounded<T>(items: &mut VecDeque<T>, item: T, limit: usize) {
if items.len() == limit { if items.len() == limit {
items.pop_front(); items.pop_front();
} else if items.len() == items.capacity() && items.capacity() >= limit / 2 {
// Growth is naturally exponential; once we're within reach of `limit`,
// reserve exactly up to it instead of letting the next doubling step
// overshoot it for long-running, heavily populated buffers.
items.reserve_exact(limit - items.len());
} }
items.push_back(item); items.push_back(item);
} }
2
@@ -2225,6 +2230,11 @@ mod tests {
target.stats.snapshot().packets.unique_replies, target.stats.snapshot().packets.unique_replies,
HISTORY_LIMIT as u64 + 3 HISTORY_LIMIT as u64 + 3
); );
assert!(
target.graph_history.capacity() < HISTORY_LIMIT + HISTORY_LIMIT / 10,
"expected saturated history capacity to stay close to the limit, got {}",
target.graph_history.capacity()
);
} }
#[test] #[test]