0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-08 10:31:50 -05:00

validation: improve performance of CheckBlockIndex

by not saving all indexes in a std::multimap, but only
those that are not part of the best header chain.
The indexes of the best header chain are stored in a vector,
which, in the typical case of a mostly linear chain with
a few forks, results in a much smaller multimap, and increases
performance noticeably for long chains.

This does not change the actual consistency checks that are being
performed for each index, just the way the block index tree is
stored and traversed.

Co-authored-by: Ryan Ofsky <ryan@ofsky.org>
This commit is contained in:
Martin Zumsande 2023-08-23 14:05:42 -04:00
parent 32c80413fd
commit d5a631b959

View file

@ -5050,19 +5050,28 @@ void ChainstateManager::CheckBlockIndex()
return;
}
// Build forward-pointing map of the entire block tree.
// Build forward-pointing data structure for the entire block tree.
// For performance reasons, indexes of the best header chain are stored in a vector (within CChain).
// All remaining blocks are stored in a multimap.
// The best header chain can differ from the active chain: E.g. its entries may belong to blocks that
// are not yet validated.
CChain best_hdr_chain;
assert(m_best_header);
best_hdr_chain.SetTip(*m_best_header);
std::multimap<CBlockIndex*,CBlockIndex*> forward;
for (auto& [_, block_index] : m_blockman.m_block_index) {
forward.emplace(block_index.pprev, &block_index);
// Only save indexes in forward that are not part of the best header chain.
if (!best_hdr_chain.Contains(&block_index)) {
// Only genesis, which must be part of the best header chain, can have a nullptr parent.
assert(block_index.pprev);
forward.emplace(block_index.pprev, &block_index);
}
}
assert(forward.size() + best_hdr_chain.Height() + 1 == m_blockman.m_block_index.size());
assert(forward.size() == m_blockman.m_block_index.size());
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangeGenesis = forward.equal_range(nullptr);
CBlockIndex *pindex = rangeGenesis.first->second;
rangeGenesis.first++;
assert(rangeGenesis.first == rangeGenesis.second); // There is only one index entry with parent nullptr.
CBlockIndex* pindex = best_hdr_chain[0];
assert(pindex);
// Iterate over the entire block tree, using depth-first search.
// Along the way, remember whether there are blocks on the path from genesis
// block being explored which are the first to have certain properties.
@ -5274,14 +5283,21 @@ void ChainstateManager::CheckBlockIndex()
// assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow
// End: actual consistency checks.
// Try descending into the first subnode.
// Try descending into the first subnode. Always process forks first and the best header chain after.
snap_update_firsts();
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> range = forward.equal_range(pindex);
if (range.first != range.second) {
// A subnode was found.
// A subnode not part of the best header chain was found.
pindex = range.first->second;
nHeight++;
continue;
} else if (best_hdr_chain.Contains(pindex)) {
// Descend further into best header chain.
nHeight++;
pindex = best_hdr_chain[nHeight];
if (!pindex) break; // we are finished, since the best header chain is always processed last
continue;
}
// This is a leaf node.
// Move upwards until we reach a node of which we have not yet visited the last child.
@ -5307,9 +5323,15 @@ void ChainstateManager::CheckBlockIndex()
// Proceed to the next one.
rangePar.first++;
if (rangePar.first != rangePar.second) {
// Move to the sibling.
// Move to a sibling not part of the best header chain.
pindex = rangePar.first->second;
break;
} else if (pindexPar == best_hdr_chain[nHeight - 1]) {
// Move to pindex's sibling on the best-chain, if it has one.
pindex = best_hdr_chain[nHeight];
// There will not be a next block if (and only if) parent block is the best header.
assert((pindex == nullptr) == (pindexPar == best_hdr_chain.Tip()));
break;
} else {
// Move up further.
pindex = pindexPar;
@ -5319,8 +5341,8 @@ void ChainstateManager::CheckBlockIndex()
}
}
// Check that we actually traversed the entire map.
assert(nNodes == forward.size());
// Check that we actually traversed the entire block index.
assert(nNodes == forward.size() + best_hdr_chain.Height() + 1);
}
std::string Chainstate::ToString()