perf(market): precompute the address-picker selection flag once
The portfolio address picker's sort comparator looked up each address in the selected-address set on every comparison, two hash lookups per call, and the per-row draw repeated the lookup again. On a long address list that is a lot of redundant hashing every frame. - Each filtered candidate now carries its selected flag, looked up once when the list is built. The comparator reads the stored flag, so the sort produces the same ordering with no lookups of its own. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -660,7 +660,13 @@ static void pfDrawAddressSection(App* app)
|
||||
}
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
std::string search = s_pfEdit.search;
|
||||
std::vector<const AddressInfo*> filtered;
|
||||
// Selected-address set, built once (membership test for the sort flag and the per-row check below).
|
||||
std::unordered_set<std::string> selSet(s_pfEdit.addrs.begin(), s_pfEdit.addrs.end());
|
||||
// Each candidate carries its "selected" flag, looked up ONCE (the old comparator did two hash lookups per
|
||||
// comparison). Same keys + same comparison outcomes => std::sort yields the same permutation as before.
|
||||
struct PfPick { const AddressInfo* a; bool sel; };
|
||||
std::vector<PfPick> filtered;
|
||||
filtered.reserve(state.addresses.size());
|
||||
for (const auto& a : state.addresses) {
|
||||
if (s_pfEdit.typeFilter == 1 && a.type != "shielded") continue;
|
||||
if (s_pfEdit.typeFilter == 2 && a.type != "transparent") continue;
|
||||
@@ -669,20 +675,15 @@ static void pfDrawAddressSection(App* app)
|
||||
std::string hay = a.address + " " + app->getAddressLabel(a.address);
|
||||
if (!util::containsIgnoreCase(hay, search)) continue;
|
||||
}
|
||||
filtered.push_back(&a);
|
||||
filtered.push_back({&a, selSet.count(a.address) != 0});
|
||||
}
|
||||
// Build the selected-address set once — the sort comparator and the per-row membership test below
|
||||
// both consulted it via a linear PortfolioEntryContains scan (O(n) each) every frame.
|
||||
std::unordered_set<std::string> selSet(s_pfEdit.addrs.begin(), s_pfEdit.addrs.end());
|
||||
std::sort(filtered.begin(), filtered.end(), [&](const AddressInfo* x, const AddressInfo* y) {
|
||||
bool sx = selSet.count(x->address) != 0;
|
||||
bool sy = selSet.count(y->address) != 0;
|
||||
if (sx != sy) return sx;
|
||||
return x->balance > y->balance;
|
||||
std::sort(filtered.begin(), filtered.end(), [](const PfPick& x, const PfPick& y) {
|
||||
if (x.sel != y.sel) return x.sel;
|
||||
return x.a->balance > y.a->balance;
|
||||
});
|
||||
if (selAllClicked)
|
||||
for (const AddressInfo* a : filtered)
|
||||
if (data::PortfolioEntryAdd(s_pfEdit.addrs, a->address)) selSet.insert(a->address);
|
||||
for (const PfPick& p : filtered)
|
||||
if (data::PortfolioEntryAdd(s_pfEdit.addrs, p.a->address)) selSet.insert(p.a->address);
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
ImDrawList* ldl = material::BeginFadeScrollChild("##pfAddrList", s_pfEdit.addrFade,
|
||||
ImVec2(Layout::spacingMd(), Layout::spacingSm()), dp);
|
||||
@@ -690,8 +691,8 @@ static void pfDrawAddressSection(App* app)
|
||||
float lw = ImGui::GetContentRegionAvail().x;
|
||||
if (filtered.empty())
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("portfolio_no_addr_match"));
|
||||
for (const AddressInfo* ap : filtered) {
|
||||
const AddressInfo& a = *ap;
|
||||
for (const PfPick& pk : filtered) {
|
||||
const AddressInfo& a = *pk.a;
|
||||
bool inSet = selSet.count(a.address) != 0;
|
||||
ImVec2 rmn = ImGui::GetCursorScreenPos();
|
||||
ImVec2 rmx(rmn.x + lw, rmn.y + rowH);
|
||||
|
||||
Reference in New Issue
Block a user