perf: memoize per-frame render hot paths (console, transactions, recent lists)
Immediate-mode render functions re-run every frame; these rebuilt O(N) state each time even when nothing changed. From a 6-lens perf audit, each finding verified on a hot-path basis: - Console: ConsoleModel gains revision(); the full-model filter scan and the glyph-by-glyph text-layout pass (BuildConsoleLayout) rebuild only when the model / filter / wrap-width / zoom change — previously it re-shaped up to 10,000 lines every frame even when idle/scrolled. clear() force-invalidates the memo mid-render (no OOB on the just-emptied visible set). - Transactions: the summary-card totals memoize behind the tab's existing FNV-1a fingerprint (also folds away a now-duplicate O(N) display-key pass). - Send / Receive recent lists: early-exit the prefix scan (state.transactions is kept newest-first) instead of filtering the whole tx history every frame. - network_refresh_service: O(new x total) txid find-and-replace -> hash map. - Sidebar unconfirmed-tx badge cached on last_tx_update + tx count; the daemon-memory probe (/proc scan on Linux, popen on macOS) throttled to ~1.5s. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1120,20 +1120,25 @@ static void RenderRecentSends(const WalletState& state, float width, ImFont* cap
|
||||
S.drawElement("tabs.balance", "recent-tx-icon-size").size * hs);
|
||||
ImU32 sendCol = Error();
|
||||
|
||||
// Collect matching transactions
|
||||
std::vector<const TransactionInfo*> sends;
|
||||
for (const auto& tx : state.transactions) {
|
||||
if (tx.type != "send" && tx.type != "shield") continue;
|
||||
sends.push_back(&tx);
|
||||
}
|
||||
|
||||
// Grow the list to fill the dead space beneath the (fixed-height) compose card:
|
||||
// fit as many newest-first rows as the remaining region can show, instead of a
|
||||
// fixed 4. The child scrolls if the real history exceeds what fits, so nothing is
|
||||
// lost. A floor of 4 keeps the section substantial when the region is short.
|
||||
// Row budget is hoisted above the collect loop (it only needs the remaining region
|
||||
// height + row height, no per-row state) so the scan can early-exit.
|
||||
float listH = std::max(rowH, ImGui::GetContentRegionAvail().y);
|
||||
size_t maxRows = std::max<size_t>(4, (size_t)std::floor(listH / rowH));
|
||||
if (sends.size() > maxRows) sends.resize(maxRows); // newest-first
|
||||
|
||||
// Collect matching transactions. state.transactions is newest-first, so scan only a
|
||||
// bounded prefix and stop once maxRows matches are gathered (mirrors the early-exit in
|
||||
// balance_components.cpp:RenderSharedRecentTx) instead of filtering the whole history.
|
||||
std::vector<const TransactionInfo*> sends;
|
||||
sends.reserve(maxRows);
|
||||
for (const auto& tx : state.transactions) {
|
||||
if (tx.type != "send" && tx.type != "shield") continue;
|
||||
sends.push_back(&tx);
|
||||
if (sends.size() == maxRows) break; // newest-first: enough rows to fill the region
|
||||
}
|
||||
|
||||
ImGui::BeginChild("##RecentSendRows", ImVec2(width, listH), false,
|
||||
ImGuiWindowFlags_NoBackground);
|
||||
|
||||
Reference in New Issue
Block a user