Skip to content

Commit

Permalink
Mempool Update Cut-Through Optimization
Browse files Browse the repository at this point in the history
Often when we're updating mempool entries we update entries that we
ultimately end up removing the updated entries shortly thereafter. This
patch makes it so that we filter for such entries a bit earlier in
processing, which yields a mild improvement for these cases, and is
negligible overhead otherwise.
  • Loading branch information
JeremyRubin committed Mar 17, 2021
1 parent a9d1b40 commit c821ada
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 6 deletions.
27 changes: 24 additions & 3 deletions src/txmempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ size_t CTxMemPoolEntry::GetTxSize() const
// Update the given tx for any in-mempool descendants.
// Assumes that CTxMemPool::m_children is correct for the given tx and all
// descendants.
void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendants, const std::set<uint256> &setExclude)
void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendants, const std::set<uint256> &setExclude,
CTxMemPoolEntry::Children &setRemove, uint64_t ancestor_size_limit, uint64_t ancestor_limit)
{
CTxMemPoolEntry::Children stageEntries, descendants;
stageEntries = updateIt->GetMemPoolChildrenConst();
Expand Down Expand Up @@ -95,6 +96,10 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan
cachedDescendants[updateIt].insert(mapTx.iterator_to(descendant));
// Update ancestor state for each descendant
mapTx.modify(mapTx.iterator_to(descendant), update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCost()));

if (descendant.GetCountWithAncestors() > ancestor_limit || descendant.GetSizeWithAncestors() > ancestor_size_limit) {
setRemove.insert(descendant);
}
}
}
mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount));
Expand All @@ -105,7 +110,7 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan
// for each entry, look for descendants that are outside vHashesToUpdate, and
// add fee/size information for such descendants to the parent.
// for each such descendant, also update the ancestor state to include the parent.
void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashesToUpdate)
void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashesToUpdate, uint64_t ancestor_size_limit, uint64_t ancestor_limit)
{
AssertLockHeld(cs);
// For each entry in vHashesToUpdate, store the set of in-mempool, but not
Expand All @@ -117,6 +122,8 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
// accounted for in the state of their ancestors)
std::set<uint256> setAlreadyIncluded(vHashesToUpdate.begin(), vHashesToUpdate.end());

CTxMemPoolEntry::Children setRemove;

// Iterate in reverse, so that whenever we are looking at a transaction
// we are sure that all in-mempool descendants have already been processed.
// This maximizes the benefit of the descendant cache and guarantees that
Expand Down Expand Up @@ -146,7 +153,21 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
}
}
} // release epoch guard for UpdateForDescendants
UpdateForDescendants(it, mapMemPoolDescendantsToUpdate, setAlreadyIncluded);
UpdateForDescendants(it, mapMemPoolDescendantsToUpdate, setAlreadyIncluded, setRemove, ancestor_size_limit, ancestor_limit);
}

// copy shared pointers to vec first to guarantee setRemove's txiters are not invalidated during
// later call to removeRecursive.
std::vector<CTransactionRef> to_remove;
for (const auto& ref : setRemove) {
to_remove.emplace_back(ref.GetSharedTx());
}
for (auto& tx : to_remove) {
// this guard is safe because we know that all to_remove *were* in the mempool before we
// began iterating here, so this only serves to de-duplicate calls to removeRecursive.
// Otherwise, removeRecursive should be called whether or not the hash is in the mempool.
if (mapTx.count(tx->GetHash()))
removeRecursive(*tx, MemPoolRemovalReason::SIZELIMIT);
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/txmempool.h
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,8 @@ class CTxMemPool
* for). Note: vHashesToUpdate should be the set of transactions from the
* disconnected block that have been accepted back into the mempool.
*/
void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main) LOCKS_EXCLUDED(m_epoch);
void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate,
uint64_t ancestor_size_limit, uint64_t ancestor_limit) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main) LOCKS_EXCLUDED(m_epoch);

/** Try to calculate all in-mempool ancestors of entry.
* (these are all calculated including the tx itself)
Expand Down Expand Up @@ -806,7 +807,7 @@ class CTxMemPool
*/
void UpdateForDescendants(txiter updateIt,
cacheMap &cachedDescendants,
const std::set<uint256> &setExclude) EXCLUSIVE_LOCKS_REQUIRED(cs);
const std::set<uint256> &setExclude, CTxMemPoolEntry::Children& setRemove, uint64_t ancestor_size_limit, uint64_t ancestor_limit) EXCLUSIVE_LOCKS_REQUIRED(cs);
/** Update ancestors of hash to add/remove it as a descendant transaction. */
void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs);
/** Set ancestor state for an entry */
Expand Down
4 changes: 3 additions & 1 deletion src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,9 @@ static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& me
// previously-confirmed transactions back to the mempool.
// UpdateTransactionsFromBlock finds descendants of any transactions in
// the disconnectpool that were added back and cleans up the mempool state.
mempool.UpdateTransactionsFromBlock(vHashUpdate);
uint64_t ancestor_limit = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
uint64_t ancestor_limit_size = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000;
mempool.UpdateTransactionsFromBlock(vHashUpdate, ancestor_limit_size, ancestor_limit);

// We also need to remove any now-immature transactions
mempool.removeForReorg(active_chainstate, STANDARD_LOCKTIME_VERIFY_FLAGS);
Expand Down

0 comments on commit c821ada

Please sign in to comment.