validation: reuse same CCoinsViewCache for every ConnectBlock call

Add m_connect_block_view to ChainState's CoinsViews.
Call CreateResetGuard inside ConnectTip to ensure the view
is Reset after each block, avoiding repeated memory allocations.

Co-authored-by: l0rinc <pap.lorinc@gmail.com>
This commit is contained in:
Andrew Toth 2026-01-24 14:02:43 -05:00
parent 8fb6043231
commit 44b4ee194d
No known key found for this signature in database
GPG Key ID: 60007AFC8938B018
2 changed files with 8 additions and 2 deletions

View File

@ -1861,6 +1861,7 @@ void CoinsViews::InitCache()
{
AssertLockHeld(::cs_main);
m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview);
m_connect_block_view = std::make_unique<CCoinsViewCache>(&*m_cacheview);
}
Chainstate::Chainstate(
@ -3098,7 +3099,8 @@ bool Chainstate::ConnectTip(
LogDebug(BCLog::BENCH, " - Load block from disk: %.2fms\n",
Ticks<MillisecondsDouble>(time_2 - time_1));
{
CCoinsViewCache view(&CoinsTip());
CCoinsViewCache& view{*m_coins_views->m_connect_block_view};
const auto reset_guard{view.CreateResetGuard()};
bool rv = ConnectBlock(*block_to_connect, state, pindexNew, view);
if (m_chainman.m_options.signals) {
m_chainman.m_options.signals->BlockChecked(block_to_connect, state);
@ -3116,7 +3118,7 @@ bool Chainstate::ConnectTip(
Ticks<MillisecondsDouble>(time_3 - time_2),
Ticks<SecondsDouble>(m_chainman.time_connect_total),
Ticks<MillisecondsDouble>(m_chainman.time_connect_total) / m_chainman.num_blocks_total);
view.Flush(/*will_reuse_cache=*/false); // local CCoinsViewCache goes out of scope
view.Flush(/*will_reuse_cache=*/false); // No need to reallocate since it only has capacity for 1 block
}
const auto time_4{SteadyClock::now()};
m_chainman.time_flush += time_4 - time_3;

View File

@ -488,6 +488,10 @@ public:
//! can fit per the dbcache setting.
std::unique_ptr<CCoinsViewCache> m_cacheview GUARDED_BY(cs_main);
//! Temporary CCoinsViewCache layered on top of m_cacheview and passed to ConnectBlock().
//! Reset between calls and flushed only on success, so invalid blocks don't pollute the underlying cache.
std::unique_ptr<CCoinsViewCache> m_connect_block_view GUARDED_BY(cs_main);
//! This constructor initializes CCoinsViewDB and CCoinsViewErrorCatcher instances, but it
//! *does not* create a CCoinsViewCache instance by default. This is done separately because the
//! presence of the cache has implications on whether or not we're allowed to flush the cache's