Skip to content

Commit 046392d

Browse files
committed
Keep track of memory usage in CCoinsViewCache
1 parent 540629c commit 046392d

File tree

3 files changed

+64
-7
lines changed

3 files changed

+64
-7
lines changed

src/coins.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "coins.h"
66

7+
#include "memusage.h"
78
#include "random.h"
89

910
#include <assert.h>
@@ -57,13 +58,17 @@ bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStat
5758

5859
CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {}
5960

60-
CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false) { }
61+
CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), cachedCoinsUsage(0) { }
6162

6263
CCoinsViewCache::~CCoinsViewCache()
6364
{
6465
assert(!hasModifier);
6566
}
6667

68+
size_t CCoinsViewCache::DynamicMemoryUsage() const {
69+
return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage;
70+
}
71+
6772
CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const {
6873
CCoinsMap::iterator it = cacheCoins.find(txid);
6974
if (it != cacheCoins.end())
@@ -78,6 +83,7 @@ CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const
7883
// version as fresh.
7984
ret->second.flags = CCoinsCacheEntry::FRESH;
8085
}
86+
cachedCoinsUsage += memusage::DynamicUsage(ret->second.coins);
8187
return ret;
8288
}
8389

@@ -93,6 +99,7 @@ bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const {
9399
CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) {
94100
assert(!hasModifier);
95101
std::pair<CCoinsMap::iterator, bool> ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry()));
102+
size_t cachedCoinUsage = 0;
96103
if (ret.second) {
97104
if (!base->GetCoins(txid, ret.first->second.coins)) {
98105
// The parent view does not have this entry; mark it as fresh.
@@ -102,10 +109,12 @@ CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) {
102109
// The parent view only has a pruned entry for this; mark it as fresh.
103110
ret.first->second.flags = CCoinsCacheEntry::FRESH;
104111
}
112+
} else {
113+
cachedCoinUsage = memusage::DynamicUsage(ret.first->second.coins);
105114
}
106115
// Assume that whenever ModifyCoins is called, the entry will be modified.
107116
ret.first->second.flags |= CCoinsCacheEntry::DIRTY;
108-
return CCoinsModifier(*this, ret.first);
117+
return CCoinsModifier(*this, ret.first, cachedCoinUsage);
109118
}
110119

111120
const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const {
@@ -150,17 +159,21 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
150159
assert(it->second.flags & CCoinsCacheEntry::FRESH);
151160
CCoinsCacheEntry& entry = cacheCoins[it->first];
152161
entry.coins.swap(it->second.coins);
162+
cachedCoinsUsage += memusage::DynamicUsage(entry.coins);
153163
entry.flags = CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH;
154164
}
155165
} else {
156166
if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) {
157167
// The grandparent does not have an entry, and the child is
158168
// modified and being pruned. This means we can just delete
159169
// it from the parent.
170+
cachedCoinsUsage -= memusage::DynamicUsage(itUs->second.coins);
160171
cacheCoins.erase(itUs);
161172
} else {
162173
// A normal modification.
174+
cachedCoinsUsage -= memusage::DynamicUsage(itUs->second.coins);
163175
itUs->second.coins.swap(it->second.coins);
176+
cachedCoinsUsage += memusage::DynamicUsage(itUs->second.coins);
164177
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
165178
}
166179
}
@@ -175,6 +188,7 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
175188
bool CCoinsViewCache::Flush() {
176189
bool fOk = base->BatchWrite(cacheCoins, hashBlock);
177190
cacheCoins.clear();
191+
cachedCoinsUsage = 0;
178192
return fOk;
179193
}
180194

@@ -232,7 +246,7 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const
232246
return tx.ComputePriority(dResult);
233247
}
234248

235-
CCoinsModifier::CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_) : cache(cache_), it(it_) {
249+
CCoinsModifier::CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_, size_t usage) : cache(cache_), it(it_), cachedCoinUsage(usage) {
236250
assert(!cache.hasModifier);
237251
cache.hasModifier = true;
238252
}
@@ -242,7 +256,11 @@ CCoinsModifier::~CCoinsModifier()
242256
assert(cache.hasModifier);
243257
cache.hasModifier = false;
244258
it->second.coins.Cleanup();
259+
cache.cachedCoinsUsage -= cachedCoinUsage; // Subtract the old usage
245260
if ((it->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) {
246261
cache.cacheCoins.erase(it);
262+
} else {
263+
// If the coin still exists after the modification, add the new usage
264+
cache.cachedCoinsUsage += memusage::DynamicUsage(it->second.coins);
247265
}
248266
}

src/coins.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#define BITCOIN_COINS_H
88

99
#include "compressor.h"
10+
#include "memusage.h"
1011
#include "serialize.h"
1112
#include "uint256.h"
1213

@@ -252,6 +253,15 @@ class CCoins
252253
return false;
253254
return true;
254255
}
256+
257+
size_t DynamicMemoryUsage() const {
258+
size_t ret = memusage::DynamicUsage(vout);
259+
BOOST_FOREACH(const CTxOut &out, vout) {
260+
const std::vector<unsigned char> *script = &out.scriptPubKey;
261+
ret += memusage::DynamicUsage(*script);
262+
}
263+
return ret;
264+
}
255265
};
256266

257267
class CCoinsKeyHasher
@@ -356,7 +366,8 @@ class CCoinsModifier
356366
private:
357367
CCoinsViewCache& cache;
358368
CCoinsMap::iterator it;
359-
CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_);
369+
size_t cachedCoinUsage; // Cached memory usage of the CCoins object before modification
370+
CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_, size_t usage);
360371

361372
public:
362373
CCoins* operator->() { return &it->second.coins; }
@@ -372,13 +383,17 @@ class CCoinsViewCache : public CCoinsViewBacked
372383
/* Whether this cache has an active modifier. */
373384
bool hasModifier;
374385

386+
375387
/**
376388
* Make mutable so that we can "fill the cache" even from Get-methods
377389
* declared as "const".
378390
*/
379391
mutable uint256 hashBlock;
380392
mutable CCoinsMap cacheCoins;
381393

394+
/* Cached dynamic memory usage for the inner CCoins objects. */
395+
mutable size_t cachedCoinsUsage;
396+
382397
public:
383398
CCoinsViewCache(CCoinsView *baseIn);
384399
~CCoinsViewCache();
@@ -414,6 +429,9 @@ class CCoinsViewCache : public CCoinsViewBacked
414429
//! Calculate the size of the cache (in number of transactions)
415430
unsigned int GetCacheSize() const;
416431

432+
//! Calculate the size of the cache (in bytes)
433+
size_t DynamicMemoryUsage() const;
434+
417435
/**
418436
* Amount of bitcoins coming in to a transaction
419437
* Note that lightweight clients may not know anything besides the hash of previous transactions,

src/test/coins_tests.cpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,24 @@ class CCoinsViewTest : public CCoinsView
5959

6060
bool GetStats(CCoinsStats& stats) const { return false; }
6161
};
62+
63+
class CCoinsViewCacheTest : public CCoinsViewCache
64+
{
65+
public:
66+
CCoinsViewCacheTest(CCoinsView* base) : CCoinsViewCache(base) {}
67+
68+
void SelfTest() const
69+
{
70+
// Manually recompute the dynamic usage of the whole data, and compare it.
71+
size_t ret = memusage::DynamicUsage(cacheCoins);
72+
for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) {
73+
ret += memusage::DynamicUsage(it->second.coins);
74+
}
75+
BOOST_CHECK_EQUAL(memusage::DynamicUsage(*this), ret);
76+
}
77+
78+
};
79+
6280
}
6381

6482
BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup)
@@ -90,8 +108,8 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
90108

91109
// The cache stack.
92110
CCoinsViewTest base; // A CCoinsViewTest at the bottom.
93-
std::vector<CCoinsViewCache*> stack; // A stack of CCoinsViewCaches on top.
94-
stack.push_back(new CCoinsViewCache(&base)); // Start with one cache.
111+
std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top.
112+
stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache.
95113

96114
// Use a limited set of random transaction ids, so we do test overwriting entries.
97115
std::vector<uint256> txids;
@@ -136,6 +154,9 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
136154
missed_an_entry = true;
137155
}
138156
}
157+
BOOST_FOREACH(const CCoinsViewCacheTest *test, stack) {
158+
test->SelfTest();
159+
}
139160
}
140161

141162
if (insecure_rand() % 100 == 0) {
@@ -152,7 +173,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
152173
} else {
153174
removed_all_caches = true;
154175
}
155-
stack.push_back(new CCoinsViewCache(tip));
176+
stack.push_back(new CCoinsViewCacheTest(tip));
156177
if (stack.size() == 4) {
157178
reached_4_caches = true;
158179
}

0 commit comments

Comments
 (0)