Skip to content

Commit 4a1b19b

Browse files
PastaPastaPastaknst
authored andcommitted
Merge dashpay#6680: perf: avoid heavy object copy during ApplyDiff for CDeterministicMNList
73b68fe perf: avoid heavy object copy during ApplyDiff for CDeterministicMNList (Konstantin Akimov) Pull request description: ## Issue being fixed or feature implemented The function `ApplyDiff` for CDeterministicMNList creates copy of current object and return it. It is not efficient in case of applying several diffs one after one. In case if `UndoBlock` and in case of calculating Quorum Members for old blocks (without caches) up to 576 temporary copies of CDeterministicMNList may be created and destroyed. ## What was done? `ApplyDiff` now changes current object, not returning a copy. It speed ups calculation of GetDeterministicMNListInternal for ~10%; this helper is most hot function during: - block undo - RPC `quorum info` for distant quorums (when it's not in cache). - RPC `quorum verify` for distant quorums (when it's not in cache) ## How Has This Been Tested? Run unit / functional tests Collected `perf` stats for block-undo. develop: <img width="772" alt="image" src="https://github.com/user-attachments/assets/a74e5dbb-314e-4067-b0dc-372235d37fb5" /> PR: <img width="772" alt="image" src="https://github.com/user-attachments/assets/a587cb6d-96f1-4932-9143-90b7a08b2751" /> ## Breaking Changes n/a ## Checklist: - [x] 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 - [x] I have assigned this pull request to a milestone ACKs for top commit: UdjinM6: ACK 73b68fe PastaPastaPasta: utACK 73b68fe Tree-SHA512: cd82e35bdef6793d5bb0ea2189a66b01e50f318dbefd0283dab2973f6df8d99a731cef7b49aed5e8f9ca54c2282ab5570a35224e34c6d10447caeb9e097a31e5
1 parent 3513d91 commit 4a1b19b

File tree

3 files changed

+29
-28
lines changed

3 files changed

+29
-28
lines changed

src/evo/deterministicmns.cpp

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -372,31 +372,28 @@ CDeterministicMNListDiff CDeterministicMNList::BuildDiff(const CDeterministicMNL
372372
return diffRet;
373373
}
374374

375-
CDeterministicMNList CDeterministicMNList::ApplyDiff(gsl::not_null<const CBlockIndex*> pindex, const CDeterministicMNListDiff& diff) const
375+
void CDeterministicMNList::ApplyDiff(gsl::not_null<const CBlockIndex*> pindex, const CDeterministicMNListDiff& diff)
376376
{
377-
CDeterministicMNList result = *this;
378-
result.blockHash = pindex->GetBlockHash();
379-
result.nHeight = pindex->nHeight;
377+
blockHash = pindex->GetBlockHash();
378+
nHeight = pindex->nHeight;
380379

381380
for (const auto& id : diff.removedMns) {
382-
auto dmn = result.GetMNByInternalId(id);
381+
auto dmn = GetMNByInternalId(id);
383382
if (!dmn) {
384383
throw std::runtime_error(strprintf("%s: can't find a removed masternode, id=%d", __func__, id));
385384
}
386-
result.RemoveMN(dmn->proTxHash);
385+
RemoveMN(dmn->proTxHash);
387386
}
388387
for (const auto& dmn : diff.addedMNs) {
389-
result.AddMN(dmn);
388+
AddMN(dmn);
390389
}
391390
for (const auto& p : diff.updatedMNs) {
392-
auto dmn = result.GetMNByInternalId(p.first);
391+
auto dmn = GetMNByInternalId(p.first);
393392
if (!dmn) {
394393
throw std::runtime_error(strprintf("%s: can't find an updated masternode, id=%d", __func__, p.first));
395394
}
396-
result.UpdateMN(*dmn, p.second);
395+
UpdateMN(*dmn, p.second);
397396
}
398-
399-
return result;
400397
}
401398

402399
void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTotalCount)
@@ -624,7 +621,8 @@ bool CDeterministicMNManager::UndoBlock(gsl::not_null<const CBlockIndex*> pindex
624621
mnListDiffsCache.erase(blockHash);
625622
}
626623
if (diff.HasChanges()) {
627-
CDeterministicMNList curList{prevList.ApplyDiff(pindex, diff)};
624+
CDeterministicMNList curList{prevList};
625+
curList.ApplyDiff(pindex, diff);
628626

629627
auto inversedDiff{curList.BuildDiff(prevList)};
630628
updatesRet = {curList, prevList, inversedDiff};
@@ -1007,12 +1005,7 @@ CDeterministicMNList CDeterministicMNManager::GetListForBlockInternal(gsl::not_n
10071005

10081006
for (const auto& diffIndex : listDiffIndexes) {
10091007
const auto& diff = mnListDiffsCache.at(diffIndex->GetBlockHash());
1010-
if (diff.HasChanges()) {
1011-
snapshot = snapshot.ApplyDiff(diffIndex, diff);
1012-
} else {
1013-
snapshot.SetBlockHash(diffIndex->GetBlockHash());
1014-
snapshot.SetHeight(diffIndex->nHeight);
1015-
}
1008+
snapshot.ApplyDiff(diffIndex, diff);
10161009
}
10171010

10181011
if (tipIndex) {

src/evo/deterministicmns.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,12 @@ class CDeterministicMNList
380380
void PoSeDecrease(const CDeterministicMN& dmn);
381381

382382
[[nodiscard]] CDeterministicMNListDiff BuildDiff(const CDeterministicMNList& to) const;
383-
[[nodiscard]] CDeterministicMNList ApplyDiff(gsl::not_null<const CBlockIndex*> pindex, const CDeterministicMNListDiff& diff) const;
383+
/**
384+
* Apply Diff modifies current object.
385+
* It is more efficient than creating a copy due to heavy copy constructor.
386+
* Calculating for old block may require up to {DISK_SNAPSHOT_PERIOD} object copy & destroy.
387+
*/
388+
void ApplyDiff(gsl::not_null<const CBlockIndex*> pindex, const CDeterministicMNListDiff& diff);
384389

385390
void AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTotalCount = true);
386391
void UpdateMN(const CDeterministicMN& oldDmn, const std::shared_ptr<const CDeterministicMNState>& pdmnState);

src/test/evo_deterministicmns_tests.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,10 @@ void FuncV19Activation(TestChainSetup& setup)
366366

367367
// check mn list/diff
368368
CDeterministicMNListDiff dummy_diff = base_list.BuildDiff(tip_list);
369-
CDeterministicMNList dummmy_list = base_list.ApplyDiff(chainman.ActiveChain().Tip(), dummy_diff);
369+
CDeterministicMNList dummy_list{base_list};
370+
dummy_list.ApplyDiff(chainman.ActiveChain().Tip(), dummy_diff);
370371
// Lists should match
371-
BOOST_REQUIRE(dummmy_list == tip_list);
372+
BOOST_REQUIRE(dummy_list == tip_list);
372373

373374
// mine 10 more blocks
374375
for (int i = 0; i < 10; ++i)
@@ -389,20 +390,22 @@ void FuncV19Activation(TestChainSetup& setup)
389390
const CBlockIndex* v19_index = chainman.ActiveChain().Tip()->GetAncestor(Params().GetConsensus().V19Height);
390391
auto v19_list = dmnman.GetListForBlock(v19_index);
391392
dummy_diff = v19_list.BuildDiff(tip_list);
392-
dummmy_list = v19_list.ApplyDiff(chainman.ActiveChain().Tip(), dummy_diff);
393-
BOOST_REQUIRE(dummmy_list == tip_list);
393+
dummy_list = v19_list;
394+
dummy_list.ApplyDiff(chainman.ActiveChain().Tip(), dummy_diff);
395+
BOOST_REQUIRE(dummy_list == tip_list);
394396

395397
// NOTE: this fails on v19/v19.1 with errors like:
396398
// "RemoveMN: Can't delete a masternode ... with a pubKeyOperator=..."
397399
dummy_diff = base_list.BuildDiff(tip_list);
398-
dummmy_list = base_list.ApplyDiff(chainman.ActiveChain().Tip(), dummy_diff);
399-
BOOST_REQUIRE(dummmy_list == tip_list);
400+
dummy_list = base_list;
401+
dummy_list.ApplyDiff(chainman.ActiveChain().Tip(), dummy_diff);
402+
BOOST_REQUIRE(dummy_list == tip_list);
400403

401-
dummmy_list = base_list;
404+
dummy_list = base_list;
402405
for (const auto& diff : diffs) {
403-
dummmy_list = dummmy_list.ApplyDiff(chainman.ActiveChain().Tip(), diff);
406+
dummy_list.ApplyDiff(chainman.ActiveChain().Tip(), diff);
404407
}
405-
BOOST_REQUIRE(dummmy_list == tip_list);
408+
BOOST_REQUIRE(dummy_list == tip_list);
406409
};
407410

408411
void FuncDIP3Protx(TestChainSetup& setup)

0 commit comments

Comments
 (0)