perf: do linear lookup instead building 2 heavy Hash-Maps#7176
perf: do linear lookup instead building 2 heavy Hash-Maps#7176knst wants to merge 3 commits intodashpay:developfrom
Conversation
…-Maps which used almost never It also fixes a bug with possible UB and dangling pointer to masternode if list has been re-created
|
WalkthroughThis pull request removes two MasternodeData index maps (m_by_service and m_by_protx) and updates callers to iterate m_entries (linear lookup). It deletes src/qt/util.h and adds GUIUtil::MakeQByteArray in src/qt/guiutil.h, replacing usages of the removed util::make_array. It also removes the GOV::getObjLocalValidity declaration and its GOVImpl implementation. Several Qt UI files (clientfeeds, proposallist, rpcconsole) were adapted to use shared_ptr-based MasternodeEntry lookups and QByteArray construction via the new helper. Sequence Diagram(s)(Skipped — changes are refactoring and API removal without a new multi-component sequential control flow.) Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/qt/proposallist.cpp (1)
552-557:⚠️ Potential issue | 🟡 MinorFix clang-format diff in the failedMessages block
CI flagged formatting differences for Lines 552-559. Please run clang-format (or the repo formatting script) on this block.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/qt/proposallist.cpp` around lines 552 - 557, The failedMessages.append block in proposallist.cpp (inside the "Sign vote via wallet interface" branch where walletModel->wallet().signGovernanceVote is called) is misformatted; run the project's clang-format or formatting script on that block (the failedMessages.append(...) and surrounding braces/continue) so indentation, line breaks and brace placement match the repo style and resolve the CI clang-format diff.
🧹 Nitpick comments (1)
src/qt/guiutil.h (1)
534-538: Add safeguards toMakeQByteArraytemplate to prevent misuse with non-byte containersThe template accepts any type with
.data()and.size(), butQByteArrayinterprets the size as a byte count. While the current call site inrpcconsole.cppsafely passesstd::vector<unsigned char>(where element count equals byte count), adding a static assertion and explicit narrowing cast would prevent future misuse with containers likestd::vector<int>.♻️ Suggested hardening
template <typename T1> inline QByteArray MakeQByteArray(const T1& data) { - return QByteArray(reinterpret_cast<const char*>(data.data()), data.size()); + static_assert(sizeof(*data.data()) == 1, "MakeQByteArray expects byte-sized elements"); + return QByteArray(reinterpret_cast<const char*>(data.data()), + static_cast<int>(data.size())); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/qt/guiutil.h` around lines 534 - 538, MakeQByteArray currently accepts any type with .data() and .size(), but QByteArray expects a byte count; add a compile-time guard and an explicit narrowing cast to avoid misuse with non-byte element types. In MakeQByteArray add a static_assert that sizeof(typename T1::value_type) == 1 (or that value_type is unsigned char/char/uint8_t) to ensure element size is one byte, and change the size argument to QByteArray to use an explicit static_cast<int>(data.size()) so you don't pass a size_t directly; keep the reinterpret_cast<const char*>(data.data()) for the pointer.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/qt/clientfeeds.h`:
- Around line 151-155: MasternodeData's m_counts member (type
interfaces::MnList::Counts) lacks the {} initializer for consistency with
similar structs; update the MasternodeData definition to initialize m_counts
with {} (matching CreditPoolData and InstantSendData) so the line declaring
m_counts uses m_counts{} to explicitly value-initialize it.
---
Outside diff comments:
In `@src/qt/proposallist.cpp`:
- Around line 552-557: The failedMessages.append block in proposallist.cpp
(inside the "Sign vote via wallet interface" branch where
walletModel->wallet().signGovernanceVote is called) is misformatted; run the
project's clang-format or formatting script on that block (the
failedMessages.append(...) and surrounding braces/continue) so indentation, line
breaks and brace placement match the repo style and resolve the CI clang-format
diff.
---
Nitpick comments:
In `@src/qt/guiutil.h`:
- Around line 534-538: MakeQByteArray currently accepts any type with .data()
and .size(), but QByteArray expects a byte count; add a compile-time guard and
an explicit narrowing cast to avoid misuse with non-byte element types. In
MakeQByteArray add a static_assert that sizeof(typename T1::value_type) == 1 (or
that value_type is unsigned char/char/uint8_t) to ensure element size is one
byte, and change the size argument to QByteArray to use an explicit
static_cast<int>(data.size()) so you don't pass a size_t directly; keep the
reinterpret_cast<const char*>(data.data()) for the pointer.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/Makefile.qt.includesrc/interfaces/node.hsrc/node/interfaces.cppsrc/qt/clientfeeds.cppsrc/qt/clientfeeds.hsrc/qt/guiutil.hsrc/qt/proposallist.cppsrc/qt/rpcconsole.cppsrc/qt/util.h
💤 Files with no reviewable changes (4)
- src/interfaces/node.h
- src/qt/util.h
- src/Makefile.qt.include
- src/node/interfaces.cpp
| struct MasternodeData { | ||
| bool m_valid{false}; | ||
| int m_list_height{0}; | ||
| interfaces::MnList::Counts m_counts{}; | ||
| QHash<QByteArray, const MasternodeEntry*> m_by_service{}; | ||
| std::vector<std::shared_ptr<MasternodeEntry>> m_entries{}; | ||
| Uint256HashMap<const MasternodeEntry*> m_by_protx{}; | ||
| interfaces::MnList::Counts m_counts; | ||
| std::vector<std::shared_ptr<MasternodeEntry>> m_entries; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n src/qt/clientfeeds.h | sed -n '145,160p'Repository: dashpay/dash
Length of output: 489
🏁 Script executed:
rg -n -A5 'struct MasternodeData' srcRepository: dashpay/dash
Length of output: 694
🏁 Script executed:
rg -n 'MasternodeData\s*[({]' src --type=cpp --type=cc --type=h --type=hppRepository: dashpay/dash
Length of output: 82
🏁 Script executed:
rg -n 'interfaces::MnList::Counts' src --type=cpp --type=cc --type=h --type=hpp -B2 -A2Repository: dashpay/dash
Length of output: 82
🏁 Script executed:
rg -n 'MasternodeData' src --type=cpp --type=h -B2 -A5 | head -80Repository: dashpay/dash
Length of output: 3045
🏁 Script executed:
fd . src -name '*.h' -o -name '*.cpp' | xargs grep -l 'MnList::Counts' | head -5Repository: dashpay/dash
Length of output: 286
🏁 Script executed:
rg -n 'namespace.*MnList\|struct.*Counts\|class.*Counts' src --type=cpp --type=h -A3Repository: dashpay/dash
Length of output: 38
🏁 Script executed:
find src -name '*.h' -o -name '*.cpp' | xargs grep -l 'Counts\s*[{=:]' | head -10Repository: dashpay/dash
Length of output: 158
🏁 Script executed:
rg 'struct Counts|class Counts' src --type=cpp --type=h -A10Repository: dashpay/dash
Length of output: 4075
🏁 Script executed:
rg 'MnList::Counts' src --type=cpp --type=h -B3 -A3Repository: dashpay/dash
Length of output: 383
🏁 Script executed:
rg -n 'm_counts' src/qt --type=cpp --type=h -B2 -A2Repository: dashpay/dash
Length of output: 3934
🏁 Script executed:
cat -n src/qt/masternodelist.cpp | sed -n '200,225p'Repository: dashpay/dash
Length of output: 1095
🏁 Script executed:
rg -n 'ret\.' src/qt/masternodelist.cpp | grep -A2 -B2 'MasternodeData ret'Repository: dashpay/dash
Length of output: 38
🏁 Script executed:
cat -n src/interfaces/node.h | sed -n '1,50p' | grep -A20 'struct Counts'Repository: dashpay/dash
Length of output: 38
🏁 Script executed:
rg -n 'struct Counts' src/interfaces/node.h -A12Repository: dashpay/dash
Length of output: 576
Add initializer for consistency with similar structures in the same file.
Line 154 omits the {} initializer on m_counts. While the interfaces::MnList::Counts type safely zero-initializes all its members (defined in src/interfaces/node.h), other similar data structures in src/qt/clientfeeds.h (CreditPoolData at line 117 and InstantSendData at line 135) explicitly initialize their m_counts members with {}. For consistency and clarity:
Suggested fix
- interfaces::MnList::Counts m_counts;
+ interfaces::MnList::Counts m_counts{};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| struct MasternodeData { | |
| bool m_valid{false}; | |
| int m_list_height{0}; | |
| interfaces::MnList::Counts m_counts{}; | |
| QHash<QByteArray, const MasternodeEntry*> m_by_service{}; | |
| std::vector<std::shared_ptr<MasternodeEntry>> m_entries{}; | |
| Uint256HashMap<const MasternodeEntry*> m_by_protx{}; | |
| interfaces::MnList::Counts m_counts; | |
| std::vector<std::shared_ptr<MasternodeEntry>> m_entries; | |
| struct MasternodeData { | |
| bool m_valid{false}; | |
| int m_list_height{0}; | |
| interfaces::MnList::Counts m_counts{}; | |
| std::vector<std::shared_ptr<MasternodeEntry>> m_entries; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/qt/clientfeeds.h` around lines 151 - 155, MasternodeData's m_counts
member (type interfaces::MnList::Counts) lacks the {} initializer for
consistency with similar structs; update the MasternodeData definition to
initialize m_counts with {} (matching CreditPoolData and InstantSendData) so the
line declaring m_counts uses m_counts{} to explicitly value-initialize it.
| const auto addr_key{util::make_array(stats->nodeStats.addr.GetKey())}; | ||
| const MasternodeEntry* dmn = [&]() -> const MasternodeEntry* { | ||
| const auto addr_key{GUIUtil::MakeQByteArray(stats->nodeStats.addr.GetKey())}; | ||
| const std::shared_ptr<MasternodeEntry> dmn = [&]() -> const std::shared_ptr<MasternodeEntry> { |
There was a problem hiding this comment.
Consider using MasternodeEntryList alias
Issue being fixed or feature implemented
Currently, for each update of masternode list 3 list are generated: list of shared_pointers, hash-map by service, hash-map by protx.
Creating these hash-maps creates overhead for every user for every masternode update. It causes excessive battery use, excessive RAM usage while these lookups happens in rare conditions (only for masternode owners during voting; only when "Peer Detail" is open).
It's better to do linear lookup once over all masternode in the specific scenario, rather than build these heavy objects for every update of list.
Lookup during voting may looks scary due to potential O(N^2). Though, it's not N^2, but N*M (where M is amount of masternodes that belongs to single Owner, M<<N). Secondly, N is relatively small and voting is not assumed to be rapid-fast action (milliseconds); it's okay if user will wait extra 10ms in the worst case scenario for each its vote.
What was done?
Code is updated to do linear lookup instead building heavy hash-maps for every update.
Current implementation (in develop) has a bug with dangling pointer and potentially could cause UB: #7118 (comment)
Fixed by this PR
How Has This Been Tested?
N/A
Breaking Changes
N/A
Checklist: