aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am1
-rw-r--r--src/coins.cpp24
-rw-r--r--src/coins.h20
-rw-r--r--src/init.cpp18
-rw-r--r--src/main.cpp74
-rw-r--r--src/main.h8
-rw-r--r--src/memusage.h111
-rw-r--r--src/test/coins_tests.cpp27
-rw-r--r--src/txdb.h2
9 files changed, 243 insertions, 42 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 1621b2dcd0..91d530222f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -100,6 +100,7 @@ BITCOIN_CORE_H = \
leveldbwrapper.h \
limitedmap.h \
main.h \
+ memusage.h \
merkleblock.h \
miner.h \
mruset.h \
diff --git a/src/coins.cpp b/src/coins.cpp
index d79e29951b..a41d5a310d 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -4,6 +4,7 @@
#include "coins.h"
+#include "memusage.h"
#include "random.h"
#include <assert.h>
@@ -57,13 +58,17 @@ bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStat
CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {}
-CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false) { }
+CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), cachedCoinsUsage(0) { }
CCoinsViewCache::~CCoinsViewCache()
{
assert(!hasModifier);
}
+size_t CCoinsViewCache::DynamicMemoryUsage() const {
+ return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage;
+}
+
CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const {
CCoinsMap::iterator it = cacheCoins.find(txid);
if (it != cacheCoins.end())
@@ -78,6 +83,7 @@ CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const
// version as fresh.
ret->second.flags = CCoinsCacheEntry::FRESH;
}
+ cachedCoinsUsage += memusage::DynamicUsage(ret->second.coins);
return ret;
}
@@ -93,6 +99,7 @@ bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const {
CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) {
assert(!hasModifier);
std::pair<CCoinsMap::iterator, bool> ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry()));
+ size_t cachedCoinUsage = 0;
if (ret.second) {
if (!base->GetCoins(txid, ret.first->second.coins)) {
// The parent view does not have this entry; mark it as fresh.
@@ -102,10 +109,12 @@ CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) {
// The parent view only has a pruned entry for this; mark it as fresh.
ret.first->second.flags = CCoinsCacheEntry::FRESH;
}
+ } else {
+ cachedCoinUsage = memusage::DynamicUsage(ret.first->second.coins);
}
// Assume that whenever ModifyCoins is called, the entry will be modified.
ret.first->second.flags |= CCoinsCacheEntry::DIRTY;
- return CCoinsModifier(*this, ret.first);
+ return CCoinsModifier(*this, ret.first, cachedCoinUsage);
}
const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const {
@@ -150,6 +159,7 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
assert(it->second.flags & CCoinsCacheEntry::FRESH);
CCoinsCacheEntry& entry = cacheCoins[it->first];
entry.coins.swap(it->second.coins);
+ cachedCoinsUsage += memusage::DynamicUsage(entry.coins);
entry.flags = CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH;
}
} else {
@@ -157,10 +167,13 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
// The grandparent does not have an entry, and the child is
// modified and being pruned. This means we can just delete
// it from the parent.
+ cachedCoinsUsage -= memusage::DynamicUsage(itUs->second.coins);
cacheCoins.erase(itUs);
} else {
// A normal modification.
+ cachedCoinsUsage -= memusage::DynamicUsage(itUs->second.coins);
itUs->second.coins.swap(it->second.coins);
+ cachedCoinsUsage += memusage::DynamicUsage(itUs->second.coins);
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
}
}
@@ -175,6 +188,7 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
bool CCoinsViewCache::Flush() {
bool fOk = base->BatchWrite(cacheCoins, hashBlock);
cacheCoins.clear();
+ cachedCoinsUsage = 0;
return fOk;
}
@@ -232,7 +246,7 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const
return tx.ComputePriority(dResult);
}
-CCoinsModifier::CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_) : cache(cache_), it(it_) {
+CCoinsModifier::CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_, size_t usage) : cache(cache_), it(it_), cachedCoinUsage(usage) {
assert(!cache.hasModifier);
cache.hasModifier = true;
}
@@ -242,7 +256,11 @@ CCoinsModifier::~CCoinsModifier()
assert(cache.hasModifier);
cache.hasModifier = false;
it->second.coins.Cleanup();
+ cache.cachedCoinsUsage -= cachedCoinUsage; // Subtract the old usage
if ((it->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) {
cache.cacheCoins.erase(it);
+ } else {
+ // If the coin still exists after the modification, add the new usage
+ cache.cachedCoinsUsage += memusage::DynamicUsage(it->second.coins);
}
}
diff --git a/src/coins.h b/src/coins.h
index fe2eaa08e5..a4671645df 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -7,6 +7,7 @@
#define BITCOIN_COINS_H
#include "compressor.h"
+#include "memusage.h"
#include "serialize.h"
#include "uint256.h"
@@ -252,6 +253,15 @@ public:
return false;
return true;
}
+
+ size_t DynamicMemoryUsage() const {
+ size_t ret = memusage::DynamicUsage(vout);
+ BOOST_FOREACH(const CTxOut &out, vout) {
+ const std::vector<unsigned char> *script = &out.scriptPubKey;
+ ret += memusage::DynamicUsage(*script);
+ }
+ return ret;
+ }
};
class CCoinsKeyHasher
@@ -356,7 +366,8 @@ class CCoinsModifier
private:
CCoinsViewCache& cache;
CCoinsMap::iterator it;
- CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_);
+ size_t cachedCoinUsage; // Cached memory usage of the CCoins object before modification
+ CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_, size_t usage);
public:
CCoins* operator->() { return &it->second.coins; }
@@ -372,6 +383,7 @@ protected:
/* Whether this cache has an active modifier. */
bool hasModifier;
+
/**
* Make mutable so that we can "fill the cache" even from Get-methods
* declared as "const".
@@ -379,6 +391,9 @@ protected:
mutable uint256 hashBlock;
mutable CCoinsMap cacheCoins;
+ /* Cached dynamic memory usage for the inner CCoins objects. */
+ mutable size_t cachedCoinsUsage;
+
public:
CCoinsViewCache(CCoinsView *baseIn);
~CCoinsViewCache();
@@ -414,6 +429,9 @@ public:
//! Calculate the size of the cache (in number of transactions)
unsigned int GetCacheSize() const;
+ //! Calculate the size of the cache (in bytes)
+ size_t DynamicMemoryUsage() const;
+
/**
* Amount of bitcoins coming in to a transaction
* Note that lightweight clients may not know anything besides the hash of previous transactions,
diff --git a/src/init.cpp b/src/init.cpp
index bacc938375..2d75850253 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1061,18 +1061,20 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
// cache size calculations
- size_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20);
- if (nTotalCache < (nMinDbCache << 20))
- nTotalCache = (nMinDbCache << 20); // total cache cannot be less than nMinDbCache
- else if (nTotalCache > (nMaxDbCache << 20))
- nTotalCache = (nMaxDbCache << 20); // total cache cannot be greater than nMaxDbCache
- size_t nBlockTreeDBCache = nTotalCache / 8;
+ int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20);
+ nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
+ nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greated than nMaxDbcache
+ int64_t nBlockTreeDBCache = nTotalCache / 8;
if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false))
nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB
nTotalCache -= nBlockTreeDBCache;
- size_t nCoinDBCache = nTotalCache / 2; // use half of the remaining cache for coindb cache
+ int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache
nTotalCache -= nCoinDBCache;
- nCoinCacheSize = nTotalCache / 300; // coins in memory require around 300 bytes
+ nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache
+ LogPrintf("Cache configuration:\n");
+ LogPrintf("* Using %.1fMiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024));
+ LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024));
+ LogPrintf("* Using %.1fMiB for in-memory UTXO set\n", nCoinCacheUsage * (1.0 / 1024 / 1024));
bool fLoaded = false;
while (!fLoaded) {
diff --git a/src/main.cpp b/src/main.cpp
index 9a3c272e0e..794edc30fb 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -57,7 +57,7 @@ bool fPruneMode = false;
bool fIsBareMultisigStd = true;
bool fCheckBlockIndex = false;
bool fCheckpointsEnabled = true;
-unsigned int nCoinCacheSize = 5000;
+size_t nCoinCacheUsage = 5000 * 300;
uint64_t nPruneTarget = 0;
/** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */
@@ -1880,6 +1880,8 @@ enum FlushStateMode {
bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
LOCK2(cs_main, cs_LastBlockFile);
static int64_t nLastWrite = 0;
+ static int64_t nLastFlush = 0;
+ static int64_t nLastSetChain = 0;
std::set<int> setFilesToPrune;
bool fFlushForPrune = false;
try {
@@ -1893,16 +1895,32 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
}
}
}
- if ((mode == FLUSH_STATE_ALWAYS) ||
- ((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->GetCacheSize() > nCoinCacheSize) ||
- (mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000) ||
- fFlushForPrune) {
- // Typical CCoins structures on disk are around 100 bytes in size.
- // Pushing a new one to the database can cause it to be written
- // twice (once in the log, and once in the tables). This is already
- // an overestimation, as most will delete an existing entry or
- // overwrite one. Still, use a conservative safety factor of 2.
- if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize()))
+ int64_t nNow = GetTimeMicros();
+ // Avoid writing/flushing immediately after startup.
+ if (nLastWrite == 0) {
+ nLastWrite = nNow;
+ }
+ if (nLastFlush == 0) {
+ nLastFlush = nNow;
+ }
+ if (nLastSetChain == 0) {
+ nLastSetChain = nNow;
+ }
+ size_t cacheSize = pcoinsTip->DynamicMemoryUsage();
+ // The cache is large and close to the limit, but we have time now (not in the middle of a block processing).
+ bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize * (10.0/9) > nCoinCacheUsage;
+ // The cache is over the limit, we have to write now.
+ bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nCoinCacheUsage;
+ // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash.
+ bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000;
+ // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
+ bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000;
+ // Combine all conditions that result in a full cache flush.
+ bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune;
+ // Write blocks and block index to disk.
+ if (fDoFullFlush || fPeriodicWrite) {
+ // Depend on nMinDiskSpace to ensure we can write block index
+ if (!CheckDiskSpace(0))
return state.Error("out of disk space");
// First make sure all block and undo data is flushed to disk.
FlushBlockFile();
@@ -1924,21 +1942,31 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
return state.Abort("Files to write to block index database");
}
}
- // Flush the chainstate (which may refer to block index entries).
- if (!pcoinsTip->Flush())
- return state.Abort("Failed to write to coin database");
-
// Finally remove any pruned files
if (fFlushForPrune) {
UnlinkPrunedFiles(setFilesToPrune);
fCheckForPruning = false;
}
-
+ nLastWrite = nNow;
+ }
+ // Flush best chain related state. This can only be done if the blocks / block index write was also done.
+ if (fDoFullFlush) {
+ // Typical CCoins structures on disk are around 128 bytes in size.
+ // Pushing a new one to the database can cause it to be written
+ // twice (once in the log, and once in the tables). This is already
+ // an overestimation, as most will delete an existing entry or
+ // overwrite one. Still, use a conservative safety factor of 2.
+ if (!CheckDiskSpace(128 * 2 * 2 * pcoinsTip->GetCacheSize()))
+ return state.Error("out of disk space");
+ // Flush the chainstate (which may refer to block index entries).
+ if (!pcoinsTip->Flush())
+ return state.Abort("Failed to write to coin database");
+ nLastFlush = nNow;
+ }
+ if ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000) {
// Update best block in wallet (so we can detect restored wallets).
- if (mode != FLUSH_STATE_IF_NEEDED) {
- GetMainSignals().SetBestChain(chainActive.GetLocator());
- }
- nLastWrite = GetTimeMicros();
+ GetMainSignals().SetBestChain(chainActive.GetLocator());
+ nLastSetChain = nNow;
}
} catch (const std::runtime_error& e) {
return state.Abort(std::string("System error while flushing: ") + e.what());
@@ -1966,10 +1994,10 @@ void static UpdateTip(CBlockIndex *pindexNew) {
nTimeBestReceived = GetTime();
mempool.AddTransactionsUpdated(1);
- LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n", __func__,
+ LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%.1fMiB(%utx)\n", __func__,
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx,
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
- Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), (unsigned int)pcoinsTip->GetCacheSize());
+ Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize());
cvBlockChange.notify_all();
@@ -3197,7 +3225,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
- if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= nCoinCacheSize) {
+ if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
bool fClean = true;
if (!DisconnectBlock(block, state, pindex, coins, &fClean))
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
diff --git a/src/main.h b/src/main.h
index 90b809e85e..fcbc4075d2 100644
--- a/src/main.h
+++ b/src/main.h
@@ -82,8 +82,10 @@ static const unsigned int MAX_HEADERS_RESULTS = 2000;
* degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning
* harder). We'll probably want to make this a per-peer adaptive value at some point. */
static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024;
-/** Time to wait (in seconds) between writing blockchain state to disk. */
-static const unsigned int DATABASE_WRITE_INTERVAL = 3600;
+/** Time to wait (in seconds) between writing blocks/block index to disk. */
+static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
+/** Time to wait (in seconds) between flushing chainstate to disk. */
+static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
/** Maximum length of reject messages. */
static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
@@ -119,7 +121,7 @@ extern bool fTxIndex;
extern bool fIsBareMultisigStd;
extern bool fCheckBlockIndex;
extern bool fCheckpointsEnabled;
-extern unsigned int nCoinCacheSize;
+extern size_t nCoinCacheUsage;
extern CFeeRate minRelayTxFee;
/** Best header we've seen so far (used for getheaders queries' starting points). */
diff --git a/src/memusage.h b/src/memusage.h
new file mode 100644
index 0000000000..9f7de9e2e1
--- /dev/null
+++ b/src/memusage.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2015 The Bitcoin developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_MEMUSAGE_H
+#define BITCOIN_MEMUSAGE_H
+
+#include <stdlib.h>
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include <boost/unordered_set.hpp>
+#include <boost/unordered_map.hpp>
+
+namespace memusage
+{
+
+/** Compute the total memory used by allocating alloc bytes. */
+static size_t MallocUsage(size_t alloc);
+
+/** Compute the memory used for dynamically allocated but owned data structures.
+ * For generic data types, this is *not* recursive. DynamicUsage(vector<vector<int> >)
+ * will compute the memory used for the vector<int>'s, but not for the ints inside.
+ * This is for efficiency reasons, as these functions are intended to be fast. If
+ * application data structures require more accurate inner accounting, they should
+ * do the recursion themselves, or use more efficient caching + updating on modification.
+ */
+template<typename X> static size_t DynamicUsage(const std::vector<X>& v);
+template<typename X> static size_t DynamicUsage(const std::set<X>& s);
+template<typename X, typename Y> static size_t DynamicUsage(const std::map<X, Y>& m);
+template<typename X, typename Y> static size_t DynamicUsage(const boost::unordered_set<X, Y>& s);
+template<typename X, typename Y, typename Z> static size_t DynamicUsage(const boost::unordered_map<X, Y, Z>& s);
+template<typename X> static size_t DynamicUsage(const X& x);
+
+static inline size_t MallocUsage(size_t alloc)
+{
+ // Measured on libc6 2.19 on Linux.
+ if (sizeof(void*) == 8) {
+ return ((alloc + 31) >> 4) << 4;
+ } else if (sizeof(void*) == 4) {
+ return ((alloc + 15) >> 3) << 3;
+ } else {
+ assert(0);
+ }
+}
+
+// STL data structures
+
+template<typename X>
+struct stl_tree_node
+{
+private:
+ int color;
+ void* parent;
+ void* left;
+ void* right;
+ X x;
+};
+
+template<typename X>
+static inline size_t DynamicUsage(const std::vector<X>& v)
+{
+ return MallocUsage(v.capacity() * sizeof(X));
+}
+
+template<typename X>
+static inline size_t DynamicUsage(const std::set<X>& s)
+{
+ return MallocUsage(sizeof(stl_tree_node<X>)) * s.size();
+}
+
+template<typename X, typename Y>
+static inline size_t DynamicUsage(const std::map<X, Y>& m)
+{
+ return MallocUsage(sizeof(stl_tree_node<std::pair<const X, Y> >)) * m.size();
+}
+
+// Boost data structures
+
+template<typename X>
+struct boost_unordered_node : private X
+{
+private:
+ void* ptr;
+};
+
+template<typename X, typename Y>
+static inline size_t DynamicUsage(const boost::unordered_set<X, Y>& s)
+{
+ return MallocUsage(sizeof(boost_unordered_node<X>)) * s.size() + MallocUsage(sizeof(void*) * s.bucket_count());
+}
+
+template<typename X, typename Y, typename Z>
+static inline size_t DynamicUsage(const boost::unordered_map<X, Y, Z>& m)
+{
+ return MallocUsage(sizeof(boost_unordered_node<std::pair<const X, Y> >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count());
+}
+
+// Dispatch to class method as fallback
+
+template<typename X>
+static inline size_t DynamicUsage(const X& x)
+{
+ return x.DynamicMemoryUsage();
+}
+
+}
+
+#endif
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 2e2cc2214b..34b311b804 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -59,6 +59,24 @@ public:
bool GetStats(CCoinsStats& stats) const { return false; }
};
+
+class CCoinsViewCacheTest : public CCoinsViewCache
+{
+public:
+ CCoinsViewCacheTest(CCoinsView* base) : CCoinsViewCache(base) {}
+
+ void SelfTest() const
+ {
+ // Manually recompute the dynamic usage of the whole data, and compare it.
+ size_t ret = memusage::DynamicUsage(cacheCoins);
+ for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) {
+ ret += memusage::DynamicUsage(it->second.coins);
+ }
+ BOOST_CHECK_EQUAL(memusage::DynamicUsage(*this), ret);
+ }
+
+};
+
}
BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup)
@@ -90,8 +108,8 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
// The cache stack.
CCoinsViewTest base; // A CCoinsViewTest at the bottom.
- std::vector<CCoinsViewCache*> stack; // A stack of CCoinsViewCaches on top.
- stack.push_back(new CCoinsViewCache(&base)); // Start with one cache.
+ std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top.
+ stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache.
// Use a limited set of random transaction ids, so we do test overwriting entries.
std::vector<uint256> txids;
@@ -136,6 +154,9 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
missed_an_entry = true;
}
}
+ BOOST_FOREACH(const CCoinsViewCacheTest *test, stack) {
+ test->SelfTest();
+ }
}
if (insecure_rand() % 100 == 0) {
@@ -152,7 +173,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
} else {
removed_all_caches = true;
}
- stack.push_back(new CCoinsViewCache(tip));
+ stack.push_back(new CCoinsViewCacheTest(tip));
if (stack.size() == 4) {
reached_4_caches = true;
}
diff --git a/src/txdb.h b/src/txdb.h
index 86e1c5d831..bef5dc9fd1 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -22,7 +22,7 @@ class uint256;
//! -dbcache default (MiB)
static const int64_t nDefaultDbCache = 100;
//! max. -dbcache in (MiB)
-static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 4096 : 1024;
+static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024;
//! min. -dbcache in (MiB)
static const int64_t nMinDbCache = 4;