Add a skiplist to the CBlockIndex structure.

This allows fast (O(log n)) access to far predecessor blocks.
Use it to speed up CChain::FindFork and CChain::GetLocator.
This commit is contained in:
Pieter Wuille 2014-06-25 00:56:47 +02:00 committed by Pieter Wuille
parent aa81564700
commit c9a0918330
2 changed files with 70 additions and 3 deletions

View File

@ -414,8 +414,11 @@ CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const {
break; break;
// Exponentially larger steps back, plus the genesis block. // Exponentially larger steps back, plus the genesis block.
int nHeight = std::max(pindex->nHeight - nStep, 0); int nHeight = std::max(pindex->nHeight - nStep, 0);
// Jump back quickly to the same height as the chain.
if (pindex->nHeight > nHeight)
pindex = pindex->GetAncestor(nHeight);
// In case pindex is not in this chain, iterate pindex->pprev to find blocks. // In case pindex is not in this chain, iterate pindex->pprev to find blocks.
while (pindex->nHeight > nHeight && !Contains(pindex)) while (!Contains(pindex))
pindex = pindex->pprev; pindex = pindex->pprev;
// If pindex is in this chain, use direct height-based access. // If pindex is in this chain, use direct height-based access.
if (pindex->nHeight > nHeight) if (pindex->nHeight > nHeight)
@ -442,6 +445,8 @@ CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const {
} }
CBlockIndex *CChain::FindFork(CBlockIndex *pindex) const { CBlockIndex *CChain::FindFork(CBlockIndex *pindex) const {
if (pindex->nHeight > Height())
pindex = pindex->GetAncestor(Height());
while (pindex && !Contains(pindex)) while (pindex && !Contains(pindex))
pindex = pindex->pprev; pindex = pindex->pprev;
return pindex; return pindex;
@ -2151,6 +2156,7 @@ CBlockIndex* AddToBlockIndex(CBlockHeader& block)
{ {
pindexNew->pprev = (*miPrev).second; pindexNew->pprev = (*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1; pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
pindexNew->BuildSkip();
} }
pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + pindexNew->GetBlockWork(); pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + pindexNew->GetBlockWork();
pindexNew->RaiseValidity(BLOCK_VALID_TREE); pindexNew->RaiseValidity(BLOCK_VALID_TREE);
@ -2508,6 +2514,55 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns
return (nFound >= nRequired); return (nFound >= nRequired);
} }
/** Turn the lowest '1' bit in the binary representation of a number into a '0'. */
int static inline InvertLowestOne(int n) { return n & (n - 1); }
/** Compute what height to jump back to with the CBlockIndex::pskip pointer. */
int static inline GetSkipHeight(int height) {
if (height < 2)
return 0;
// Determine which height to jump back to. Any number strictly lower than height is acceptable,
// but the following expression seems to perform well in simulations (max 110 steps to go back
// up to 2**18 blocks).
return (height & 1) ? InvertLowestOne(InvertLowestOne(height - 1)) + 1 : InvertLowestOne(height);
}
CBlockIndex* CBlockIndex::GetAncestor(int height)
{
if (height > nHeight || height < 0)
return NULL;
CBlockIndex* pindexWalk = this;
int heightWalk = nHeight;
while (heightWalk > height) {
int heightSkip = GetSkipHeight(heightWalk);
int heightSkipPrev = GetSkipHeight(heightWalk - 1);
if (heightSkip == height ||
(heightSkip > height && !(heightSkipPrev < heightSkip - 2 &&
heightSkipPrev >= height))) {
// Only follow pskip if pprev->pskip isn't better than pskip->pprev.
pindexWalk = pindexWalk->pskip;
heightWalk = heightSkip;
} else {
pindexWalk = pindexWalk->pprev;
heightWalk--;
}
}
return pindexWalk;
}
const CBlockIndex* CBlockIndex::GetAncestor(int height) const
{
return const_cast<CBlockIndex*>(this)->GetAncestor(height);
}
void CBlockIndex::BuildSkip()
{
if (pprev)
pskip = pprev->GetAncestor(GetSkipHeight(nHeight));
}
void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd)
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
@ -2858,6 +2913,8 @@ bool static LoadBlockIndexDB()
setBlockIndexValid.insert(pindex); setBlockIndexValid.insert(pindex);
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
pindexBestInvalid = pindex; pindexBestInvalid = pindex;
if (pindex->pprev)
pindex->BuildSkip();
} }
// Load block file info // Load block file info

View File

@ -677,6 +677,9 @@ public:
// pointer to the index of the predecessor of this block // pointer to the index of the predecessor of this block
CBlockIndex* pprev; CBlockIndex* pprev;
// pointer to the index of some further predecessor of this block
CBlockIndex* pskip;
// height of the entry in the chain. The genesis block has height 0 // height of the entry in the chain. The genesis block has height 0
int nHeight; int nHeight;
@ -716,6 +719,7 @@ public:
{ {
phashBlock = NULL; phashBlock = NULL;
pprev = NULL; pprev = NULL;
pskip = NULL;
nHeight = 0; nHeight = 0;
nFile = 0; nFile = 0;
nDataPos = 0; nDataPos = 0;
@ -737,6 +741,7 @@ public:
{ {
phashBlock = NULL; phashBlock = NULL;
pprev = NULL; pprev = NULL;
pskip = NULL;
nHeight = 0; nHeight = 0;
nFile = 0; nFile = 0;
nDataPos = 0; nDataPos = 0;
@ -869,10 +874,15 @@ public:
} }
return false; return false;
} }
// Build the skiplist pointer for this entry.
void BuildSkip();
// Efficiently find an ancestor of this block.
CBlockIndex* GetAncestor(int height);
const CBlockIndex* GetAncestor(int height) const;
}; };
/** Used to marshal pointers into hashes for db storage. */ /** Used to marshal pointers into hashes for db storage. */
class CDiskBlockIndex : public CBlockIndex class CDiskBlockIndex : public CBlockIndex
{ {