Skip to content

perf: reuse result of BuildNewListFromBlock #6640

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

knst
Copy link
Collaborator

@knst knst commented Apr 21, 2025

Issue being fixed or feature implemented

Method BuildNewListFromBlock is called twice for each block during validation. These calculation are pretty heavy assuming that there's thousands of masternodes. It is not much for 1 block but that's a lot of CPU calculations during full re-index or initial indexation for 2M of blocks.

What was done?

Methods CalcCbTxMerkleRootMNList and CDeterministicMNManager::ProcessBlock during block calculation re-use same pre-calculated SML instead internal re-calculation.

How Has This Been Tested?

Benchmarks are done on branch including this changes: #6632

$ getblockcount
2256425
$ getblockhash 2250000
00000000000000062539a6cd3adece4a10598e729ef571bfded9d4b704af69f0
$ debug bench
$ invalidateblock 00000000000000062539a6cd3adece4a10598e729ef571bfded9d4b704af69f0
null
$ reconsiderblock 00000000000000062539a6cd3adece4a10598e729ef571bfded9d4b704af69f0

I run it several times in different days and results are differ, but it is consistently faster.
There's logs for 3 different runs, see sum of m_dmnman and CheckCbTxMerkleRoots.

Run time for ~5000blocks:

<PR+patches>
2025-04-16T19:31:08Z [bench]         - m_dmnman: 1.16ms [5.80s]     <---
2025-04-16T19:31:08Z [bench]           - GetTxPayload: 0.22ms [1.38s]
2025-04-16T19:31:08Z [bench]           - CalcCbTxMerkleRootMNList: 0.22ms [1.77s]
2025-04-16T19:31:08Z [bench]             - CachedGetQcHashesQcIndexedHashes: 0.65ms [5.90s]
2025-04-16T19:31:08Z [bench]             - Loop: 0.01ms [0.72s]
2025-04-16T19:31:08Z [bench]             - ComputeMerkleRoot: 0.05ms [0.31s]
2025-04-16T19:31:08Z [bench]           - CalcCbTxMerkleRootQuorums: 0.73ms [7.01s]
2025-04-16T19:31:08Z [bench]         - CheckCbTxMerkleRoots: 2.39ms [16.84s]     <---
2025-04-16T19:31:08Z [bench] - Connect block: 10.88ms [105.65s (17.59ms/blk)]


<develop+patches>
2025-04-16T20:02:37Z [bench]         - m_dmnman: 1.12ms [6.38s]     <---
2025-04-16T20:02:37Z [bench]           - GetTxPayload: 0.20ms [1.40s]
2025-04-16T20:02:37Z [bench]             - BuildNewListFromBlock: 0.33ms [2.97s]
2025-04-16T20:02:37Z [bench]             - CSimplifiedMNList: 1.08ms [6.56s]
2025-04-16T20:02:37Z [bench]           - CalcCbTxMerkleRootMNList: 1.79ms [11.86s]
2025-04-16T20:02:37Z [bench]             - CachedGetQcHashesQcIndexedHashes: 0.71ms [6.02s]
2025-04-16T20:02:37Z [bench]             - Loop: 0.02ms [0.74s]
2025-04-16T20:02:37Z [bench]             - ComputeMerkleRoot: 0.05ms [0.30s]
2025-04-16T20:02:37Z [bench]           - CalcCbTxMerkleRootQuorums: 0.80ms [7.14s]
2025-04-16T20:02:37Z [bench]         - CheckCbTxMerkleRoots: 2.80ms [20.46s]     <---
2025-04-16T20:02:37Z [bench] - Connect block: 7.35ms [111.00s (18.45ms/blk)]

Run time for 6000 blocks:

<PR+patches>
        - m_dmnman: 2.54ms [6.81s]        <---
          - GetTxPayload: 0.21ms [1.52s] 
          - CalcCbTxMerkleRootMNList: 0.75ms [2.66s]
            - CachedGetQcHashesQcIndexedHashes: 1.09ms [7.06s]
            - Loop: 0.02ms [0.73s]       
            - ComputeMerkleRoot: 0.05ms [0.30s]
          - CalcCbTxMerkleRootQuorums: 1.18ms [8.10s]
        - CheckCbTxMerkleRoots: 4.54ms [21.03s]     <---
...
- Connect block: 14.29ms [113.38s (17.61ms/blk)]
    
<develop+patches>
        - m_dmnman: 2.17ms [6.64s]        <---
          - GetTxPayload: 0.24ms [1.45s] 
            - BuildNewListFromBlock: 0.61ms [3.05s]
            - CSimplifiedMNList: 2.78ms [7.95s]
          - CalcCbTxMerkleRootMNList: 3.87ms [13.83s]
            - CachedGetQcHashesQcIndexedHashes: 0.69ms [6.80s]
            - Loop: 0.02ms [0.71s]       
            - ComputeMerkleRoot: 0.05ms [0.29s]
          - CalcCbTxMerkleRootQuorums: 0.76ms [7.81s]
        - CheckCbTxMerkleRoots: 4.89ms [23.09s]     <---
...
- Connect block: 16.90ms [114.69s (17.80ms/blk)]

Run time for 9000 blocks:

<PR+patches>
        - m_dmnman: 1.96ms [10.34s]     <---
          - GetTxPayload: 0.23ms [1.98s]
          - CalcCbTxMerkleRootMNList: 0.41ms [3.75s]
            - CachedGetQcHashesQcIndexedHashes: 0.81ms [10.01s]
            - Loop: 0.02ms [0.96s]
            - ComputeMerkleRoot: 0.04ms [0.40s]
          - CalcCbTxMerkleRootQuorums: 0.89ms [11.39s]
        - CheckCbTxMerkleRoots: 3.48ms [30.06s]     <---
...
- Connect block: 15.21ms [152.76s (18.04ms/blk)]

<develop+patches>
        - m_dmnman: 2.04ms [11.14s]     <---
          - GetTxPayload: 0.20ms [2.00s]
            - BuildNewListFromBlock: 1.09ms [4.23s]
            - CSimplifiedMNList: 3.08ms [13.82s]
          - CalcCbTxMerkleRootMNList: 4.88ms [24.59s]
            - CachedGetQcHashesQcIndexedHashes: 0.87ms [10.22s]
            - Loop: 0.01ms [0.93s]
            - ComputeMerkleRoot: 0.05ms [0.39s]
          - CalcCbTxMerkleRootQuorums: 0.94ms [11.56s] 
        - CheckCbTxMerkleRoots: 6.03ms [38.16s]     <---
...
- Connect block: 24.45ms [157.15s (18.55ms/blk)]

Breaking Changes

N/A

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone (for repository code-owners and collaborators only)

@knst knst added this to the 23 milestone Apr 21, 2025
Copy link

coderabbitai bot commented Apr 21, 2025

Walkthrough

The changes across the codebase refactor how masternode list Merkle root calculations and validations are performed. Functions related to checking and calculating the Merkle root for masternode lists (CheckCbTxMerkleRoots and CalcCbTxMerkleRootMNList) are updated to accept a pre-built CSimplifiedMNList instead of internally constructing the list using various manager and view objects. This leads to changes in function signatures in both implementation and header files, removing dependencies on CDeterministicMNManager, llmq::CQuorumSnapshotManager, and CCoinsViewCache, and instead requiring the caller to build and provide the necessary masternode list.

Consequently, call sites in components such as the special transaction processor, miner, and test setup are updated to explicitly construct the deterministic masternode list and convert it to a simplified list before invoking Merkle root calculations or checks. The CDeterministicMNManager::ProcessBlock method is also refactored to accept a pre-built masternode list, removing its internal construction logic and the fJustCheck parameter. No changes are made to the overall control flow or error handling semantics, but the interfaces and dependencies between components are streamlined and made more explicit.

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cdbe900 and 2f74b8d.

📒 Files selected for processing (7)
  • src/evo/cbtx.cpp (4 hunks)
  • src/evo/cbtx.h (2 hunks)
  • src/evo/deterministicmns.cpp (2 hunks)
  • src/evo/deterministicmns.h (1 hunks)
  • src/evo/specialtxman.cpp (2 hunks)
  • src/node/miner.cpp (2 hunks)
  • src/test/util/setup_common.cpp (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/evo/specialtxman.cpp
  • src/test/util/setup_common.cpp
  • src/evo/deterministicmns.cpp
  • src/node/miner.cpp
  • src/evo/cbtx.cpp
  • src/evo/cbtx.h
  • src/evo/deterministicmns.h
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: mac-build / Build source
  • GitHub Check: linux64_sqlite-build / Build source
  • GitHub Check: linux64_ubsan-build / Build source
  • GitHub Check: linux64-build / Build source
  • GitHub Check: linux64_fuzz-build / Build source
  • GitHub Check: linux64_multiprocess-build / Build source
  • GitHub Check: linux64_tsan-build / Build source
  • GitHub Check: linux64_nowallet-build / Build source
  • GitHub Check: win64-build / Build source
  • GitHub Check: arm-linux-build / Build source

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/evo/deterministicmns.cpp (1)

600-605: Fix code formatting

There's a formatting issue in this section according to the Clang format check. Please run the formatter to fix the spacing.

-bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindex,
-                                           BlockValidationState& state, const CCoinsViewCache& view,
-                                           llmq::CQuorumSnapshotManager& qsnapman,
-                                           const CDeterministicMNList& newList,
-                                           std::optional<MNListUpdates>& updatesRet)
+bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindex,
+                                           BlockValidationState& state, const CCoinsViewCache& view,
+                                           llmq::CQuorumSnapshotManager& qsnapman,
+                                           const CDeterministicMNList& newList,
+                                           std::optional<MNListUpdates>& updatesRet)
🧰 Tools
🪛 GitHub Actions: Clang Diff Format Check

[error] 600-610: Clang format differences found. Code formatting does not match the expected style.

src/evo/cbtx.h (1)

86-92: Fix code formatting

There's a formatting issue in this section according to the Clang format check. Please run the formatter to fix the spacing.

-bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex,
-                          const llmq::CQuorumBlockProcessor& quorum_block_processor,
-                          CSimplifiedMNList&& sml,
-                          BlockValidationState& state);
-bool CalcCbTxMerkleRootMNList(uint256& merkleRootRet, CSimplifiedMNList&& sml, BlockValidationState& state);
+bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex,
+                          const llmq::CQuorumBlockProcessor& quorum_block_processor,
+                          CSimplifiedMNList&& sml,
+                          BlockValidationState& state);
+bool CalcCbTxMerkleRootMNList(uint256& merkleRootRet, CSimplifiedMNList&& sml, BlockValidationState& state);
🧰 Tools
🪛 GitHub Actions: Clang Diff Format Check

[error] 86-90: Clang format differences found. Code formatting does not match the expected style.

src/evo/cbtx.cpp (1)

61-67: Fix code formatting

There's a formatting issue in this section according to the Clang format check. Please run the formatter to fix the spacing.

-bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex,
-                          const llmq::CQuorumBlockProcessor& quorum_block_processor,
-                          CSimplifiedMNList&& sml,
-                          BlockValidationState& state)
+bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex,
+                          const llmq::CQuorumBlockProcessor& quorum_block_processor,
+                          CSimplifiedMNList&& sml,
+                          BlockValidationState& state)
🧰 Tools
🪛 GitHub Actions: Clang Diff Format Check

[error] 61-70: Clang format differences found. Code formatting does not match the expected style.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e584f6 and cdbe900.

📒 Files selected for processing (7)
  • src/evo/cbtx.cpp (4 hunks)
  • src/evo/cbtx.h (2 hunks)
  • src/evo/deterministicmns.cpp (2 hunks)
  • src/evo/deterministicmns.h (1 hunks)
  • src/evo/specialtxman.cpp (2 hunks)
  • src/node/miner.cpp (2 hunks)
  • src/test/util/setup_common.cpp (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/evo/specialtxman.cpp (3)
src/evo/deterministicmns.h (7)
  • pindex (371-371)
  • pindex (585-585)
  • pindex (587-587)
  • pindex (596-596)
  • pindex (609-609)
  • block (582-584)
  • block (590-592)
src/evo/specialtxman.h (3)
  • block (72-73)
  • block (75-75)
  • block (77-77)
src/evo/cbtx.cpp (2)
  • CheckCbTxMerkleRoots (63-117)
  • CheckCbTxMerkleRoots (63-66)
🪛 GitHub Actions: Clang Diff Format Check
src/evo/specialtxman.cpp

[error] 7-20: Clang format differences found. Code formatting does not match the expected style.

src/evo/deterministicmns.h

[error] 580-590: Clang format differences found. Code formatting does not match the expected style.

src/evo/deterministicmns.cpp

[error] 600-610: Clang format differences found. Code formatting does not match the expected style.

src/evo/cbtx.cpp

[error] 61-70: Clang format differences found. Code formatting does not match the expected style.

src/evo/cbtx.h

[error] 86-90: Clang format differences found. Code formatting does not match the expected style.

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: arm-linux-build / Build source
  • GitHub Check: linux64_nowallet-build / Build source
  • GitHub Check: linux64_sqlite-build / Build source
  • GitHub Check: linux64_ubsan-build / Build source
  • GitHub Check: linux64-build / Build source
  • GitHub Check: linux64_fuzz-build / Build source
  • GitHub Check: win64-build / Build source
  • GitHub Check: x86_64-apple-darwin / Build depends
🔇 Additional comments (14)
src/test/util/setup_common.cpp (2)

54-54: Added include for simplified masternode list header.

The include is necessary to support the optimization where a simplified masternode list is constructed from the deterministic masternode list.


490-494: Optimize by reusing the built masternode list.

The code now builds the deterministic masternode list once and reuses it when calculating the masternode list merkle root, which is in line with the PR's performance optimization goal. The implementation includes appropriate error checking with the assertion.

src/node/miner.cpp (2)

31-31: Added include for deterministicmns header.

The include is necessary to support the optimization changes that follow.


228-233: Performance improvement by reusing the built masternode list.

Instead of recalculating the deterministic masternode list inside CalcCbTxMerkleRootMNList, this implementation builds it once and passes it to the function. This aligns with the PR's goal of reducing redundant computations during block validation, particularly important given the large number of masternodes. The error handling is properly done with clear error messages.

src/evo/specialtxman.cpp (2)

178-190: Optimized implementation to build and reuse the masternode list.

This implementation explicitly builds the deterministic masternode list once and reuses it for both the ProcessBlock call and the CheckCbTxMerkleRoots call. This aligns with the PR's performance optimization goal and matches the updated interface of the ProcessBlock method. The code properly handles errors and state passing.


196-196: Use the pre-built masternode list for checking merkle roots.

The function now passes a CSimplifiedMNList constructed from the pre-built deterministic masternode list instead of having CheckCbTxMerkleRoots build it internally. This completes the optimization by reusing the same list for both operations.

src/evo/deterministicmns.cpp (2)

601-605: Process block function signature refactored to accept pre-calculated deterministic MN list

The function signature now accepts a pre-built CDeterministicMNList as a parameter instead of recalculating it internally. This is the main performance optimization of this PR - eliminating the redundant call to BuildNewListFromBlock.


614-623: Refactored code flow to use externally provided MN list

The function no longer builds the new list internally, but instead uses the provided newList to compute the diff against the old list. This change aligns with the PR objective of reusing the pre-calculated list during block validation.

src/evo/cbtx.h (2)

88-91: Modified CheckCbTxMerkleRoots to accept pre-calculated simplified MN list

The function signature has been updated to take a pre-built CSimplifiedMNList as an rvalue reference parameter instead of constructing it internally. This matches the PR objectives by eliminating duplicate construction of the masternode list.


92-92: Simplified CalcCbTxMerkleRootMNList function signature

The function has been significantly simplified to accept only the output parameter for the Merkle root, a pre-built simplified MN list, and the validation state. Dependencies on block, previous index, deterministic MN manager, quorum snapshot manager, and coins view cache have been removed.

src/evo/cbtx.cpp (4)

63-66: CheckCbTxMerkleRoots function signature updated to match header

The implementation now accepts a pre-built CSimplifiedMNList as an rvalue reference parameter, consistent with the header file changes and PR optimization objective.


90-90: Using pre-calculated simplified MN list for Merkle root calculation

The code now passes the externally built simplified MN list to CalcCbTxMerkleRootMNList using std::move to avoid unnecessary copying, which is efficient and aligns with the optimization goal.


119-119: Simplified CalcCbTxMerkleRootMNList function signature

The function implementation now matches the updated header declaration, accepting only the necessary parameters: the output Merkle root, the pre-built simplified MN list, and the validation state.


143-144: Updated benchmark logging

The benchmark logging correctly shows the timing for the Merkle root calculation only, without including the time to build the simplified MN list (which is now done outside this function).

@@ -10,6 +10,7 @@
#include <evo/cbtx.h>
#include <evo/creditpool.h>
#include <evo/deterministicmns.h>
#include <evo/simplifiedmns.h>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Added include for simplified masternode list header.

The inclusion is necessary to support the use of CSimplifiedMNList in the updated code.

There's a Clang format issue in the file that should be fixed. The GitHub Actions pipeline shows a formatting error for lines 7-20.

@knst knst force-pushed the perf-BuildNewListFromBlock branch from cdbe900 to 2f74b8d Compare April 21, 2025 08:09
@knst knst requested review from UdjinM6 and PastaPastaPasta April 21, 2025 11:18
Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

light ACK 2f74b8d

Copy link
Member

@PastaPastaPasta PastaPastaPasta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 2f74b8d

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants