Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/bench/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ add_executable(bench_bitcoin
checkblockindex.cpp
checkqueue.cpp
cluster_linearize.cpp
coinsviewcacheasync.cpp
connectblock.cpp
crypto_hash.cpp
descriptors.cpp
Expand Down
52 changes: 52 additions & 0 deletions src/bench/coinsviewcacheasync.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <bench/bench.h>
#include <bench/data/block413567.raw.h>
#include <coins.h>
#include <coinsviewcacheasync.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <serialize.h>
#include <streams.h>
#include <sync.h>
#include <test/util/setup_common.h>
#include <txdb.h>
#include <validation.h>

#include <cassert>
#include <ranges>
#include <utility>

static void CoinsViewCacheAsyncBenchmark(benchmark::Bench& bench)
{
CBlock block;
DataStream{benchmark::data::block413567} >> TX_WITH_WITNESS(block);
const auto testing_setup{MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN, { .coins_db_in_memory = false })};
Chainstate& chainstate{testing_setup->m_node.chainman->ActiveChainstate()};
auto& coins_tip{WITH_LOCK(testing_setup->m_node.chainman->GetMutex(), return chainstate.CoinsTip();)};

for (const auto& tx : block.vtx | std::views::drop(1)) {
for (const auto& in : tx->vin) {
Coin coin{};
coin.out.nValue = 1;
coins_tip.EmplaceCoinInternalDANGER(COutPoint{in.prevout}, std::move(coin));
}
}
chainstate.ForceFlushStateToDisk();
CoinsViewCacheAsync async_cache{&coins_tip};

bench.run([&] {
async_cache.StartFetching(block);
for (const auto& tx : block.vtx | std::views::drop(1)) {
for (const auto& in : tx->vin) {
const auto have{async_cache.HaveCoin(in.prevout)};
assert(have);
}
}
async_cache.Reset();
});
}

BENCHMARK(CoinsViewCacheAsyncBenchmark, benchmark::PriorityLevel::HIGH);
25 changes: 25 additions & 0 deletions src/coins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,26 @@ void CCoinsViewBacked::BatchWrite(CoinsViewCacheCursor& cursor, const uint256& h
std::unique_ptr<CCoinsViewCursor> CCoinsViewBacked::Cursor() const { return base->Cursor(); }
size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }

std::optional<Coin> CCoinsViewCache::FetchCoinWithoutMutating(const COutPoint& outpoint) const noexcept
{
// Walk up the chain of caches, returning on first entry that exists
const CCoinsView* view{base};
while (const auto* cache{dynamic_cast<const CCoinsViewCache*>(view)}) {
auto it{cache->cacheCoins.find(outpoint)};
if (it != cache->cacheCoins.end()) {
return !it->second.coin.IsSpent() ? std::optional<Coin>{it->second.coin} : std::nullopt;
}
view = cache->base;
}
return view->GetCoin(outpoint);
}

CCoinsViewCache::CCoinsViewCache(CCoinsView* baseIn, bool deterministic) :
CCoinsViewBacked(baseIn), m_deterministic(deterministic),
cacheCoins(0, SaltedOutpointHasher(/*deterministic=*/deterministic), CCoinsMap::key_equal{}, &m_cache_coins_memory_resource)
{
constexpr float max_load_factor{0.5f};
cacheCoins.max_load_factor(max_load_factor);
m_sentinel.second.SelfRef(m_sentinel);
}

Expand Down Expand Up @@ -274,6 +290,13 @@ void CCoinsViewCache::Sync()
}
}

void CCoinsViewCache::Reset() noexcept
{
cacheCoins.clear();
cachedCoinsUsage = 0;
hashBlock.SetNull();
}

void CCoinsViewCache::Uncache(const COutPoint& hash)
{
CCoinsMap::iterator it = cacheCoins.find(hash);
Expand Down Expand Up @@ -309,10 +332,12 @@ void CCoinsViewCache::ReallocateCache()
{
// Cache should be empty when we're calling this.
assert(cacheCoins.size() == 0);
const auto prev{cacheCoins.max_load_factor()};
cacheCoins.~CCoinsMap();
m_cache_coins_memory_resource.~CCoinsMapMemoryResource();
::new (&m_cache_coins_memory_resource) CCoinsMapMemoryResource{};
::new (&cacheCoins) CCoinsMap{0, SaltedOutpointHasher{/*deterministic=*/m_deterministic}, CCoinsMap::key_equal{}, &m_cache_coins_memory_resource};
cacheCoins.max_load_factor(prev);
}

void CCoinsViewCache::SanityCheck() const
Expand Down
14 changes: 10 additions & 4 deletions src/coins.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ class CCoinsViewBacked : public CCoinsView
bool HaveCoin(const COutPoint &outpoint) const override;
uint256 GetBestBlock() const override;
std::vector<uint256> GetHeadBlocks() const override;
void SetBackend(CCoinsView &viewIn);
virtual void SetBackend(CCoinsView &viewIn);
void BatchWrite(CoinsViewCacheCursor& cursor, const uint256& hashBlock) override;
std::unique_ptr<CCoinsViewCursor> Cursor() const override;
size_t EstimateSize() const override;
Expand All @@ -376,6 +376,9 @@ class CCoinsViewCache : public CCoinsViewBacked
/* Cached dynamic memory usage for the inner Coin objects. */
mutable size_t cachedCoinsUsage{0};

//! Get the coin from base but do not access or mutate cacheCoins.
std::optional<Coin> FetchCoinWithoutMutating(const COutPoint& outpoint) const noexcept;

public:
CCoinsViewCache(CCoinsView *baseIn, bool deterministic = false);

Expand Down Expand Up @@ -442,15 +445,18 @@ class CCoinsViewCache : public CCoinsViewBacked
* If will_reuse_cache is false, the cache will retain the same memory footprint
* after flushing and should be destroyed to deallocate.
*/
void Flush(bool will_reuse_cache = true);
virtual void Flush(bool will_reuse_cache = true);

/**
* Push the modifications applied to this cache to its base while retaining
* the contents of this cache (except for spent coins, which we erase).
* Failure to call this method or Flush() before destruction will cause the changes
* to be forgotten.
*/
void Sync();
virtual void Sync();

//! Wipe local state.
virtual void Reset() noexcept;

/**
* Removes the UTXO with the given outpoint from the cache, if it is
Expand Down Expand Up @@ -482,7 +488,7 @@ class CCoinsViewCache : public CCoinsViewBacked
* @note this is marked const, but may actually append to `cacheCoins`, increasing
* memory usage.
*/
CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const;
virtual CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const;
};

//! Utility function to add all of a transaction's outputs to a cache.
Expand Down
Loading
Loading