Skip to content

Commit c2975f2

Browse files
committed
Merge bitcoin/bitcoin#33602: [IBD] coins: reduce lookups in dbcache layer propagation
0ac969c validation: don't reallocate cache for short-lived CCoinsViewCache (Lőrinc) c8f5e44 coins: reduce lookups in dbcache layer propagation (Lőrinc) Pull request description: This change is part of [[IBD] - Tracking PR for speeding up Initial Block Download](bitcoin/bitcoin#32043) ### Summary Previously, when the parent coins cache had no entry and the child did, `BatchWrite` performed a find followed by `try_emplace`, which resulted in multiple `SipHash` computations and bucket traversals on the common insert path. On a different path, these caches were recreated needlessly for every block connection. ### Fix for double fetch This change uses a single leading `try_emplace` and branches on the returned `inserted` flag. In the `FRESH && SPENT` case (not used in production, only exercised by tests), we erase the just-inserted placeholder (which is constant time with no rehash anyway). Semantics are unchanged for all valid parent/child state combinations. This change is a minimal version of [bitcoin/bitcoin@`723c49b` (#32128)](bitcoin/bitcoin@723c49b) and draws simplification ideas [bitcoin/bitcoin@`ae76ec7` (#30673)](bitcoin/bitcoin@ae76ec7) and bitcoin/bitcoin#30326. ### Fix for temporary cache recreation Related to parent cache propagation, the second commit makes it possible to avoid destructuring-recreating-destructuring of these short-live parent caches created for each new block. A few temporary `CCoinsViewCache`'s are destructed right after the `Flush()`, therefore it is not necessary to call `ReallocateCache` to recreate them right before they're killed anyway. This change was based on a subset of bitcoin/bitcoin#28945, the original authors and relevant commenters were added as coauthors to this version. ----- Reindex-chainstate indicates ~1% speedup. <details> <summary>Details</summary> ```python COMMITS="647cdb4f7e8041affed887e2325ee03a91078bb1 0b0c3293ffd75afb27dadc0b28426b40132a8c6b"; \ STOP=909090; DBCACHE=4500; \ CC=gcc; CXX=g++; \ BASE_DIR="/mnt/my_storage"; DATA_DIR="$BASE_DIR/BitcoinData"; LOG_DIR="$BASE_DIR/logs"; \ (echo ""; for c in $COMMITS; do git fetch -q origin $c && git log -1 --pretty='%h %s' $c || exit 1; done; echo "") && \ hyperfine \ --sort command \ --runs 2 \ --export-json "$BASE_DIR/rdx-$(sed -E 's/(\w{8})\w+ ?/\1-/g;s/-$//'<<<"$COMMITS")-$STOP-$DBCACHE-$CC.json" \ --parameter-list COMMIT ${COMMITS// /,} \ --prepare "killall bitcoind 2>/dev/null; rm -f $DATA_DIR/debug.log; git checkout {COMMIT}; git clean -fxd; git reset --hard && \ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DENABLE_IPC=OFF && ninja -C build bitcoind && \ ./build/bin/bitcoind -datadir=$DATA_DIR -stopatheight=$STOP -dbcache=1000 -printtoconsole=0; sleep 20" \ --cleanup "cp $DATA_DIR/debug.log $LOG_DIR/debug-{COMMIT}-$(date +%s).log" \ "COMPILER=$CC ./build/bin/bitcoind -datadir=$DATA_DIR -stopatheight=$STOP -dbcache=$DBCACHE -reindex-chainstate -blocksonly -connect=0 -printtoconsole=0" 647cdb4 Merge bitcoin/bitcoin#33311: net: Quiet down logging when router doesn't support natpmp/pcp 0b0c3293ff validation: don't reallocate cache for short-lived CCoinsViewCache Benchmark 1: COMPILER=gcc ./build/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -stopatheight=909090 -dbcache=4500 -reindex-chainstate -blocksonly -connect=0 -printtoconsole=0 (COMMIT = 647cdb4) Time (mean ± σ): 16233.508 s ± 9.501 s [User: 19064.578 s, System: 951.672 s] Range (min … max): 16226.790 s … 16240.226 s 2 runs Benchmark 2: COMPILER=gcc ./build/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -stopatheight=909090 -dbcache=4500 -reindex-chainstate -blocksonly -connect=0 -printtoconsole=0 (COMMIT = 0b0c3293ffd75afb27dadc0b28426b40132a8c6b) Time (mean ± σ): 16039.626 s ± 17.284 s [User: 18870.130 s, System: 950.722 s] Range (min … max): 16027.405 s … 16051.848 s 2 runs Relative speed comparison 1.01 ± 0.00 COMPILER=gcc ./build/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -stopatheight=909090 -dbcache=4500 -reindex-chainstate -blocksonly -connect=0 -printtoconsole=0 (COMMIT = 647cdb4) 1.00 COMPILER=gcc ./build/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -stopatheight=909090 -dbcache=4500 -reindex-chainstate -blocksonly -connect=0 -printtoconsole=0 (COMMIT = 0b0c3293ffd75afb27dadc0b28426b40132a8c6b) ``` </details> ACKs for top commit: optout21: utACK 0ac969c achow101: ACK 0ac969c andrewtoth: utACK 0ac969c sedited: ACK 0ac969c Tree-SHA512: 9fcc3f1a8314368576a4fba96ca72665527eaa3a97964ab5b39491757f3527147d134f79a5c3456f76c1330c7ef862989d23f764236c5e2563be89a81c1cee47
2 parents c1f0a89 + 0ac969c commit c2975f2

File tree

5 files changed

+20
-26
lines changed

5 files changed

+20
-26
lines changed

src/coins.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -185,18 +185,16 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
185185

186186
bool CCoinsViewCache::BatchWrite(CoinsViewCacheCursor& cursor, const uint256 &hashBlockIn) {
187187
for (auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) {
188-
// Ignore non-dirty entries (optimization).
189-
if (!it->second.IsDirty()) {
188+
if (!it->second.IsDirty()) { // TODO a cursor can only contain dirty entries
190189
continue;
191190
}
192-
CCoinsMap::iterator itUs = cacheCoins.find(it->first);
193-
if (itUs == cacheCoins.end()) {
194-
// The parent cache does not have an entry, while the child cache does.
195-
// We can ignore it if it's both spent and FRESH in the child
196-
if (!(it->second.IsFresh() && it->second.coin.IsSpent())) {
197-
// Create the coin in the parent cache, move the data up
198-
// and mark it as dirty.
199-
itUs = cacheCoins.try_emplace(it->first).first;
191+
auto [itUs, inserted]{cacheCoins.try_emplace(it->first)};
192+
if (inserted) {
193+
if (it->second.IsFresh() && it->second.coin.IsSpent()) {
194+
cacheCoins.erase(itUs); // TODO fresh coins should have been removed at spend
195+
} else {
196+
// The parent cache does not have an entry, while the child cache does.
197+
// Move the data up and mark it as dirty.
200198
CCoinsCacheEntry& entry{itUs->second};
201199
assert(entry.coin.DynamicMemoryUsage() == 0);
202200
if (cursor.WillErase(*it)) {
@@ -251,12 +249,14 @@ bool CCoinsViewCache::BatchWrite(CoinsViewCacheCursor& cursor, const uint256 &ha
251249
return true;
252250
}
253251

254-
bool CCoinsViewCache::Flush() {
252+
bool CCoinsViewCache::Flush(bool will_reuse_cache) {
255253
auto cursor{CoinsViewCacheCursor(m_sentinel, cacheCoins, /*will_erase=*/true)};
256254
bool fOk = base->BatchWrite(cursor, hashBlock);
257255
if (fOk) {
258256
cacheCoins.clear();
259-
ReallocateCache();
257+
if (will_reuse_cache) {
258+
ReallocateCache();
259+
}
260260
cachedCoinsUsage = 0;
261261
}
262262
return fOk;

src/coins.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,9 +439,11 @@ class CCoinsViewCache : public CCoinsViewBacked
439439
* Push the modifications applied to this cache to its base and wipe local state.
440440
* Failure to call this method or Sync() before destruction will cause the changes
441441
* to be forgotten.
442+
* If will_reuse_cache is false, the cache will retain the same memory footprint
443+
* after flushing and should be destroyed to deallocate.
442444
* If false is returned, the state of this cache (and its backing view) will be undefined.
443445
*/
444-
bool Flush();
446+
bool Flush(bool will_reuse_cache = true);
445447

446448
/**
447449
* Push the modifications applied to this cache to its base while retaining

src/test/fuzz/coins_view.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsView& backend
7474
}
7575
},
7676
[&] {
77-
(void)coins_view_cache.Flush();
77+
(void)coins_view_cache.Flush(/*will_reuse_cache=*/fuzzed_data_provider.ConsumeBool());
7878
},
7979
[&] {
8080
(void)coins_view_cache.Sync();

src/test/fuzz/coinscache_sim.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ FUZZ_TARGET(coinscache_sim)
392392
// Apply to simulation data.
393393
flush();
394394
// Apply to real caches.
395-
caches.back()->Flush();
395+
caches.back()->Flush(/*will_reuse_cache=*/provider.ConsumeBool());
396396
},
397397

398398
[&]() { // Sync.
@@ -402,14 +402,6 @@ FUZZ_TARGET(coinscache_sim)
402402
caches.back()->Sync();
403403
},
404404

405-
[&]() { // Flush + ReallocateCache.
406-
// Apply to simulation data.
407-
flush();
408-
// Apply to real caches.
409-
caches.back()->Flush();
410-
caches.back()->ReallocateCache();
411-
},
412-
413405
[&]() { // GetCacheSize
414406
(void)caches.back()->GetCacheSize();
415407
},

src/validation.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2966,7 +2966,7 @@ bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTra
29662966
LogError("DisconnectTip(): DisconnectBlock %s failed\n", pindexDelete->GetBlockHash().ToString());
29672967
return false;
29682968
}
2969-
bool flushed = view.Flush();
2969+
bool flushed = view.Flush(/*will_reuse_cache=*/false); // local CCoinsViewCache goes out of scope
29702970
assert(flushed);
29712971
}
29722972
LogDebug(BCLog::BENCH, "- Disconnect block: %.2fms\n",
@@ -3101,7 +3101,7 @@ bool Chainstate::ConnectTip(
31013101
Ticks<MillisecondsDouble>(time_3 - time_2),
31023102
Ticks<SecondsDouble>(m_chainman.time_connect_total),
31033103
Ticks<MillisecondsDouble>(m_chainman.time_connect_total) / m_chainman.num_blocks_total);
3104-
bool flushed = view.Flush();
3104+
bool flushed = view.Flush(/*will_reuse_cache=*/false); // local CCoinsViewCache goes out of scope
31053105
assert(flushed);
31063106
}
31073107
const auto time_4{SteadyClock::now()};
@@ -4895,7 +4895,7 @@ bool Chainstate::ReplayBlocks()
48954895
}
48964896

48974897
cache.SetBestBlock(pindexNew->GetBlockHash());
4898-
cache.Flush();
4898+
cache.Flush(/*will_reuse_cache=*/false); // local CCoinsViewCache goes out of scope
48994899
m_chainman.GetNotifications().progress(bilingual_str{}, 100, false);
49004900
return true;
49014901
}

0 commit comments

Comments
 (0)