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:
@@ -370,20 +370,25 @@ static void RenderRecentReceived(const AddressInfo& /* addr */,
|
||||
S.drawElement("tabs.balance", "recent-tx-icon-size").size * hs);
|
||||
ImU32 recvCol = Success();
|
||||
|
||||
// Collect matching transactions
|
||||
std::vector<const TransactionInfo*> recvs;
|
||||
for (const auto& tx : state.transactions) {
|
||||
if (tx.type != "receive" && tx.type != "mined") continue;
|
||||
recvs.push_back(&tx);
|
||||
}
|
||||
|
||||
// Grow the list to fill the dead space beneath the (fixed-height) receive 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.
|
||||
// Compute maxRows BEFORE the collect loop so the scan can stop early (below).
|
||||
float listH = std::max(rowH, ImGui::GetContentRegionAvail().y);
|
||||
size_t maxRows = std::max<size_t>(4, (size_t)std::floor(listH / rowH));
|
||||
if (recvs.size() > maxRows) recvs.resize(maxRows); // newest-first
|
||||
|
||||
// Collect matching transactions. state.transactions is newest-first, so the first
|
||||
// maxRows matches ARE exactly the rows we render — stop scanning once we have them
|
||||
// instead of filtering the entire history every frame (mirrors RenderSharedRecentTx
|
||||
// in balance_components.cpp).
|
||||
std::vector<const TransactionInfo*> recvs;
|
||||
recvs.reserve(maxRows);
|
||||
for (const auto& tx : state.transactions) {
|
||||
if (tx.type != "receive" && tx.type != "mined") continue;
|
||||
recvs.push_back(&tx);
|
||||
if (recvs.size() >= maxRows) break; // newest-first: prefix is all we show
|
||||
}
|
||||
|
||||
ImGui::BeginChild("##RecentReceivedRows", ImVec2(width, listH), false,
|
||||
ImGuiWindowFlags_NoBackground);
|
||||
|
||||
Reference in New Issue
Block a user