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 1/2] 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); From 249889bee6b88eb9814eb969e6fb108f86a4bf98 Mon Sep 17 00:00:00 2001 From: furszy Date: Wed, 11 Jun 2025 10:19:19 -0400 Subject: [PATCH 2/2] orphanage: avoid vtx iteration when no orphans --- src/node/txorphanage.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/node/txorphanage.cpp b/src/node/txorphanage.cpp index 6093be46ce4..ce087d17ffb 100644 --- a/src/node/txorphanage.cpp +++ b/src/node/txorphanage.cpp @@ -567,6 +567,8 @@ bool TxOrphanageImpl::HaveTxToReconsider(NodeId peer) } void TxOrphanageImpl::EraseForBlock(const CBlock& block) { + if (m_orphans.empty()) return; + std::set wtxids_to_erase; for (const CTransactionRef& ptx : block.vtx) { const CTransaction& block_tx = *ptx;