From 41ad2be4340d7b18a4669aa9becef8936597f000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Sun, 29 Jun 2025 20:20:17 +0200 Subject: [PATCH] mempool: Avoid expensive loop in `removeForBlock` during IBD During Initial Block Download, the mempool is usually empty, but `CTxMemPool::removeForBlock` is still called for every connected block where we: * iterate over every transaction in the block even though none will be found in the empty `mapTx`, always leaving `txs_removed_for_block` empty... * which is pre-allocated regardless with 40 bytes * vtx.size(), even though it will always remain empty. This change introduces a minor performance optimization by only executing the loop if any of the core mempool maps have any contents. The call to `MempoolTransactionsRemovedForBlock` and the updates to the rolling fee logic remain unchanged. The `removeForBlock` was also updated stylistically to match the surrounding methods and a clarification was added to clarify that it affects fee estimation as well. --- src/txmempool.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index d65005fc645..7a66751db5a 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -661,26 +661,25 @@ void CTxMemPool::removeConflicts(const CTransaction &tx) } } -/** - * Called when a block is connected. Removes from mempool. - */ void CTxMemPool::removeForBlock(const std::vector& vtx, unsigned int nBlockHeight) { + // Remove confirmed txs and conflicts when a new block is connected, updating the fee logic AssertLockHeld(cs); Assume(!m_have_changeset); std::vector txs_removed_for_block; - txs_removed_for_block.reserve(vtx.size()); - for (const auto& tx : vtx) - { - txiter it = mapTx.find(tx->GetHash()); - if (it != mapTx.end()) { - setEntries stage; - stage.insert(it); - txs_removed_for_block.emplace_back(*it); - RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK); + if (mapTx.size() || mapNextTx.size() || mapDeltas.size()) { + txs_removed_for_block.reserve(vtx.size()); + for (const auto& tx : vtx) { + txiter it = mapTx.find(tx->GetHash()); + if (it != mapTx.end()) { + setEntries stage; + stage.insert(it); + txs_removed_for_block.emplace_back(*it); + RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK); + } + removeConflicts(*tx); + ClearPrioritisation(tx->GetHash()); } - removeConflicts(*tx); - ClearPrioritisation(tx->GetHash()); } if (m_opts.signals) { m_opts.signals->MempoolTransactionsRemovedForBlock(txs_removed_for_block, nBlockHeight);