Skip to content

Commit 73ec87c

Browse files
PastaPastaPastaknst
authored andcommitted
Merge dashpay#6640: perf: reuse result of BuildNewListFromBlock
2f74b8d perf: reuse result of BuildNewListFromBlock (Konstantin Akimov) Pull request description: ## 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: dashpay#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: - [x] I have performed a self-review of my own code - [x] 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 - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: light ACK 2f74b8d PastaPastaPasta: utACK 2f74b8d Tree-SHA512: 61a69501ff096bfdc903abd0ca79a6aeb98b6e71ddde5773639914586232c869d1588471c7232ee10753f2a8bd044668928ce0272dfc569646412525e12cae48
1 parent 49177f4 commit 73ec87c

File tree

7 files changed

+47
-57
lines changed

7 files changed

+47
-57
lines changed

src/evo/cbtx.cpp

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
#include <consensus/validation.h>
66
#include <evo/cbtx.h>
7-
#include <evo/deterministicmns.h>
87
#include <evo/simplifiedmns.h>
98
#include <evo/specialtx.h>
109
#include <llmq/blockprocessor.h>
@@ -61,9 +60,9 @@ bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidati
6160
}
6261

6362
// This can only be done after the block has been fully processed, as otherwise we won't have the finished MN list
64-
bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, CDeterministicMNManager& dmnman,
65-
llmq::CQuorumSnapshotManager& qsnapman, const llmq::CQuorumBlockProcessor& quorum_block_processor,
66-
BlockValidationState& state, const CCoinsViewCache& view)
63+
bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex,
64+
const llmq::CQuorumBlockProcessor& quorum_block_processor, CSimplifiedMNList&& sml,
65+
BlockValidationState& state)
6766
{
6867
if (block.vtx[0]->nType != TRANSACTION_COINBASE) {
6968
return true;
@@ -87,7 +86,7 @@ bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, CDeter
8786
static int64_t nTimeMerkleQuorum = 0;
8887

8988
uint256 calculatedMerkleRoot;
90-
if (!CalcCbTxMerkleRootMNList(block, pindex->pprev, calculatedMerkleRoot, state, dmnman, qsnapman, view)) {
89+
if (!CalcCbTxMerkleRootMNList(calculatedMerkleRoot, std::move(sml), state)) {
9190
// pass the state returned by the function above
9291
return false;
9392
}
@@ -116,31 +115,13 @@ bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, CDeter
116115
return true;
117116
}
118117

119-
bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet,
120-
BlockValidationState& state, CDeterministicMNManager& dmnman,
121-
llmq::CQuorumSnapshotManager& qsnapman, const CCoinsViewCache& view)
118+
bool CalcCbTxMerkleRootMNList(uint256& merkleRootRet, CSimplifiedMNList&& sml, BlockValidationState& state)
122119
{
123120
try {
124-
static std::atomic<int64_t> nTimeDMN = 0;
125-
static std::atomic<int64_t> nTimeSMNL = 0;
126121
static std::atomic<int64_t> nTimeMerkle = 0;
127122

128123
int64_t nTime1 = GetTimeMicros();
129124

130-
CDeterministicMNList tmpMNList;
131-
if (!dmnman.BuildNewListFromBlock(block, pindexPrev, state, view, tmpMNList, qsnapman, false)) {
132-
// pass the state returned by the function above
133-
return false;
134-
}
135-
136-
int64_t nTime2 = GetTimeMicros(); nTimeDMN += nTime2 - nTime1;
137-
LogPrint(BCLog::BENCHMARK, " - BuildNewListFromBlock: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeDMN * 0.000001);
138-
139-
CSimplifiedMNList sml(tmpMNList);
140-
141-
int64_t nTime3 = GetTimeMicros(); nTimeSMNL += nTime3 - nTime2;
142-
LogPrint(BCLog::BENCHMARK, " - CSimplifiedMNList: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeSMNL * 0.000001);
143-
144125
static Mutex cached_mutex;
145126
static CSimplifiedMNList smlCached GUARDED_BY(cached_mutex);
146127
static uint256 merkleRootCached GUARDED_BY(cached_mutex);
@@ -158,8 +139,10 @@ bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev
158139
bool mutated = false;
159140
merkleRootRet = sml.CalcMerkleRoot(&mutated);
160141

161-
int64_t nTime4 = GetTimeMicros(); nTimeMerkle += nTime4 - nTime3;
162-
LogPrint(BCLog::BENCHMARK, " - CalcMerkleRoot: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeMerkle * 0.000001);
142+
int64_t nTime2 = GetTimeMicros();
143+
nTimeMerkle += nTime2 - nTime1;
144+
LogPrint(BCLog::BENCHMARK, " - CalcMerkleRoot: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1),
145+
nTimeMerkle * 0.000001);
163146

164147
smlCached = std::move(sml);
165148
merkleRootCached = merkleRootRet;

src/evo/cbtx.h

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@
1414
class BlockValidationState;
1515
class CBlock;
1616
class CBlockIndex;
17-
class CCoinsViewCache;
18-
class CDeterministicMNManager;
1917
class TxValidationState;
18+
class CSimplifiedMNList;
2019

2120
namespace llmq {
2221
class CChainLocksHandler;
2322
class CQuorumBlockProcessor;
24-
class CQuorumSnapshotManager;
2523
}// namespace llmq
2624

2725
// Forward declaration from core_io to get rid of circular dependency
@@ -87,12 +85,10 @@ template<> struct is_serializable_enum<CCbTx::Version> : std::true_type {};
8785

8886
bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state);
8987

90-
bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, CDeterministicMNManager& dmnman,
91-
llmq::CQuorumSnapshotManager& qsnapman, const llmq::CQuorumBlockProcessor& quorum_block_processor,
92-
BlockValidationState& state, const CCoinsViewCache& view);
93-
bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet,
94-
BlockValidationState& state, CDeterministicMNManager& dmnman,
95-
llmq::CQuorumSnapshotManager& qsnapman, const CCoinsViewCache& view);
88+
bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex,
89+
const llmq::CQuorumBlockProcessor& quorum_block_processor, CSimplifiedMNList&& sml,
90+
BlockValidationState& state);
91+
bool CalcCbTxMerkleRootMNList(uint256& merkleRootRet, CSimplifiedMNList&& sml, BlockValidationState& state);
9692
bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPrev,
9793
const llmq::CQuorumBlockProcessor& quorum_block_processor, uint256& merkleRootRet,
9894
BlockValidationState& state);

src/evo/deterministicmns.cpp

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash)
551551

552552
bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindex,
553553
BlockValidationState& state, const CCoinsViewCache& view,
554-
llmq::CQuorumSnapshotManager& qsnapman, bool fJustCheck,
554+
llmq::CQuorumSnapshotManager& qsnapman, const CDeterministicMNList& newList,
555555
std::optional<MNListUpdates>& updatesRet)
556556
{
557557
AssertLockHeld(cs_main);
@@ -561,23 +561,12 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_null<co
561561
return true;
562562
}
563563

564-
CDeterministicMNList oldList, newList;
564+
CDeterministicMNList oldList;
565565
CDeterministicMNListDiff diff;
566566

567567
int nHeight = pindex->nHeight;
568568

569569
try {
570-
if (!BuildNewListFromBlock(block, pindex->pprev, state, view, newList, qsnapman, true)) {
571-
// pass the state returned by the function above
572-
return false;
573-
}
574-
575-
if (fJustCheck) {
576-
return true;
577-
}
578-
579-
newList.SetBlockHash(pindex->GetBlockHash());
580-
581570
LOCK(cs);
582571

583572
oldList = GetListForBlockInternal(pindex->pprev);

src/evo/deterministicmns.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -592,8 +592,9 @@ class CDeterministicMNManager
592592
~CDeterministicMNManager() = default;
593593

594594
bool ProcessBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindex, BlockValidationState& state,
595-
const CCoinsViewCache& view, llmq::CQuorumSnapshotManager& qsnapman, bool fJustCheck,
596-
std::optional<MNListUpdates>& updatesRet) EXCLUSIVE_LOCKS_REQUIRED(!cs, cs_main);
595+
const CCoinsViewCache& view, llmq::CQuorumSnapshotManager& qsnapman,
596+
const CDeterministicMNList& newList, std::optional<MNListUpdates>& updatesRet)
597+
EXCLUSIVE_LOCKS_REQUIRED(!cs, cs_main);
597598
bool UndoBlock(gsl::not_null<const CBlockIndex*> pindex, std::optional<MNListUpdates>& updatesRet) EXCLUSIVE_LOCKS_REQUIRED(!cs);
598599

599600
void UpdatedBlockTip(gsl::not_null<const CBlockIndex*> pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs);

src/evo/specialtxman.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
#include <chainparams.h>
88
#include <consensus/validation.h>
99
#include <deploymentstatus.h>
10+
#include <evo/assetlocktx.h>
1011
#include <evo/cbtx.h>
1112
#include <evo/creditpool.h>
1213
#include <evo/deterministicmns.h>
1314
#include <evo/mnhftx.h>
1415
#include <evo/providertx.h>
15-
#include <evo/assetlocktx.h>
16+
#include <evo/simplifiedmns.h>
1617
#include <hash.h>
1718
#include <llmq/blockprocessor.h>
1819
#include <llmq/commitment.h>
@@ -173,16 +174,26 @@ bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(const CBlock& block, const CB
173174
nTimeQuorum += nTime3 - nTime2;
174175
LogPrint(BCLog::BENCHMARK, " - m_qblockman: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeQuorum * 0.000001);
175176

176-
if (!m_dmnman.ProcessBlock(block, pindex, state, view, m_qsnapman, fJustCheck, updatesRet)) {
177-
// pass the state returned by the function above
178-
return false;
177+
178+
CDeterministicMNList mn_list;
179+
if (DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_DIP0003)) {
180+
if (!m_dmnman.BuildNewListFromBlock(block, pindex->pprev, state, view, mn_list, m_qsnapman, true)) {
181+
// pass the state returned by the function above
182+
return false;
183+
}
184+
mn_list.SetBlockHash(pindex->GetBlockHash());
185+
186+
if (!fJustCheck && !m_dmnman.ProcessBlock(block, pindex, state, view, m_qsnapman, mn_list, updatesRet)) {
187+
// pass the state returned by the function above
188+
return false;
189+
}
179190
}
180191

181192
int64_t nTime4 = GetTimeMicros();
182193
nTimeDMN += nTime4 - nTime3;
183194
LogPrint(BCLog::BENCHMARK, " - m_dmnman: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeDMN * 0.000001);
184195

185-
if (fCheckCbTxMerkleRoots && !CheckCbTxMerkleRoots(block, pindex, m_dmnman, m_qsnapman, m_qblockman, state, view)) {
196+
if (fCheckCbTxMerkleRoots && !CheckCbTxMerkleRoots(block, pindex, m_qblockman, CSimplifiedMNList(mn_list), state)) {
186197
// pass the state returned by the function above
187198
return false;
188199
}

src/node/miner.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <evo/cbtx.h>
2929
#include <evo/chainhelper.h>
3030
#include <evo/creditpool.h>
31+
#include <evo/deterministicmns.h>
3132
#include <evo/mnhftx.h>
3233
#include <evo/simplifiedmns.h>
3334
#include <governance/governance.h>
@@ -224,7 +225,11 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
224225
cbTx.nHeight = nHeight;
225226

226227
BlockValidationState state;
227-
if (!CalcCbTxMerkleRootMNList(*pblock, pindexPrev, cbTx.merkleRootMNList, state, m_dmnman, m_qsnapman, m_chainstate.CoinsTip())) {
228+
CDeterministicMNList mn_list;
229+
if (!m_dmnman.BuildNewListFromBlock(*pblock, pindexPrev, state, m_chainstate.CoinsTip(), mn_list, m_qsnapman, true)) {
230+
throw std::runtime_error(strprintf("%s: BuildNewListFromBlock failed: %s", __func__, state.ToString()));
231+
}
232+
if (!CalcCbTxMerkleRootMNList(cbTx.merkleRootMNList, CSimplifiedMNList(mn_list), state)) {
228233
throw std::runtime_error(strprintf("%s: CalcCbTxMerkleRootMNList failed: %s", __func__, state.ToString()));
229234
}
230235
if (fDIP0008Active_context) {

src/test/util/setup_common.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include <evo/deterministicmns.h>
5252
#include <evo/evodb.h>
5353
#include <evo/mnhftx.h>
54+
#include <evo/simplifiedmns.h>
5455
#include <evo/specialtx.h>
5556
#include <flat-database.h>
5657
#include <governance/governance.h>
@@ -486,7 +487,11 @@ CBlock TestChainSetup::CreateBlock(
486487
auto cbTx = GetTxPayload<CCbTx>(*block.vtx[0]);
487488
Assert(cbTx.has_value());
488489
BlockValidationState state;
489-
if (!CalcCbTxMerkleRootMNList(block, chainstate.m_chain.Tip(), cbTx->merkleRootMNList, state, *m_node.dmnman, *m_node.llmq_ctx->qsnapman, chainstate.CoinsTip())) {
490+
CDeterministicMNList mn_list;
491+
if (!m_node.dmnman->BuildNewListFromBlock(block, chainstate.m_chain.Tip(), state, chainstate.CoinsTip(), mn_list, *m_node.llmq_ctx->qsnapman, true)) {
492+
Assert(false);
493+
}
494+
if (!CalcCbTxMerkleRootMNList(cbTx->merkleRootMNList, CSimplifiedMNList(mn_list), state)) {
490495
Assert(false);
491496
}
492497
if (!CalcCbTxMerkleRootQuorums(block, chainstate.m_chain.Tip(), *m_node.llmq_ctx->quorum_block_processor, cbTx->merkleRootQuorums, state)) {

0 commit comments

Comments
 (0)