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:
parent
32c80413fd
commit
d5a631b959
1 changed files with 36 additions and 14 deletions
|
@ -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()
|
||||
|
|
Loading…
Add table
Reference in a new issue