From c9a0918330f31dcb1d5e86b56af63c3f521d3cf2 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 25 Jun 2014 00:56:47 +0200 Subject: [PATCH] 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. --- src/main.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/main.h | 12 ++++++++++- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index dc6843128..41125cb57 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -414,8 +414,11 @@ CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const { break; // Exponentially larger steps back, plus the genesis block. 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. - while (pindex->nHeight > nHeight && !Contains(pindex)) + while (!Contains(pindex)) pindex = pindex->pprev; // If pindex is in this chain, use direct height-based access. if (pindex->nHeight > nHeight) @@ -442,6 +445,8 @@ CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const { } CBlockIndex *CChain::FindFork(CBlockIndex *pindex) const { + if (pindex->nHeight > Height()) + pindex = pindex->GetAncestor(Height()); while (pindex && !Contains(pindex)) pindex = pindex->pprev; return pindex; @@ -2151,6 +2156,7 @@ CBlockIndex* AddToBlockIndex(CBlockHeader& block) { pindexNew->pprev = (*miPrev).second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; + pindexNew->BuildSkip(); } pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + pindexNew->GetBlockWork(); pindexNew->RaiseValidity(BLOCK_VALID_TREE); @@ -2508,6 +2514,55 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns 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(this)->GetAncestor(height); +} + +void CBlockIndex::BuildSkip() +{ + if (pprev) + pskip = pprev->GetAncestor(GetSkipHeight(nHeight)); +} + void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) { AssertLockHeld(cs_main); @@ -2858,6 +2913,8 @@ bool static LoadBlockIndexDB() setBlockIndexValid.insert(pindex); if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) pindexBestInvalid = pindex; + if (pindex->pprev) + pindex->BuildSkip(); } // Load block file info diff --git a/src/main.h b/src/main.h index 9487078b7..1af86b065 100644 --- a/src/main.h +++ b/src/main.h @@ -677,6 +677,9 @@ public: // pointer to the index of the predecessor of this block 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 int nHeight; @@ -716,6 +719,7 @@ public: { phashBlock = NULL; pprev = NULL; + pskip = NULL; nHeight = 0; nFile = 0; nDataPos = 0; @@ -737,6 +741,7 @@ public: { phashBlock = NULL; pprev = NULL; + pskip = NULL; nHeight = 0; nFile = 0; nDataPos = 0; @@ -869,9 +874,14 @@ public: } 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. */ class CDiskBlockIndex : public CBlockIndex