Skip to content

Commit

Permalink
Merge #11353: Small refactor of CCoinsViewCache::BatchWrite()
Browse files Browse the repository at this point in the history
5b9748f Small refactor of CCoinsViewCache::BatchWrite() (Dan Raviv)

Pull request description:

  `std::unordered_map::erase( const_iterator pos )` returns an iterator to the element following the removed one. Use that to optimize (probably minor-performance-wise, and definitely code-structure-wise) the implementation of `CCoinsViewCache::BatchWrite()`.

Tree-SHA512: 00abc838ad91771cfcddd45688841c9414869b75289d09b483a7f0ba835614fe189e9c8aca8a80e3de78ee397ec14083ae52e2e92b7863b3b6eb0d0cb892c9dd
  • Loading branch information
MarcoFalke committed Nov 10, 2017
2 parents ee92243 + 5b9748f commit 05a7619
Showing 1 changed file with 48 additions and 46 deletions.
94 changes: 48 additions & 46 deletions src/coins.cpp
Expand Up @@ -146,56 +146,58 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
}

bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn) {
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
CCoinsMap::iterator itUs = cacheCoins.find(it->first);
if (itUs == cacheCoins.end()) {
// The parent cache does not have an entry, while the child does
// We can ignore it if it's both FRESH and pruned in the child
if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coin.IsSpent())) {
// Otherwise we will need to create it in the parent
// and move the data up and mark it as dirty
CCoinsCacheEntry& entry = cacheCoins[it->first];
entry.coin = std::move(it->second.coin);
cachedCoinsUsage += entry.coin.DynamicMemoryUsage();
entry.flags = CCoinsCacheEntry::DIRTY;
// We can mark it FRESH in the parent if it was FRESH in the child
// Otherwise it might have just been flushed from the parent's cache
// and already exist in the grandparent
if (it->second.flags & CCoinsCacheEntry::FRESH)
entry.flags |= CCoinsCacheEntry::FRESH;
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = mapCoins.erase(it)) {
// Ignore non-dirty entries (optimization).
if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) {
continue;
}
CCoinsMap::iterator itUs = cacheCoins.find(it->first);
if (itUs == cacheCoins.end()) {
// The parent cache does not have an entry, while the child does
// We can ignore it if it's both FRESH and pruned in the child
if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coin.IsSpent())) {
// Otherwise we will need to create it in the parent
// and move the data up and mark it as dirty
CCoinsCacheEntry& entry = cacheCoins[it->first];
entry.coin = std::move(it->second.coin);
cachedCoinsUsage += entry.coin.DynamicMemoryUsage();
entry.flags = CCoinsCacheEntry::DIRTY;
// We can mark it FRESH in the parent if it was FRESH in the child
// Otherwise it might have just been flushed from the parent's cache
// and already exist in the grandparent
if (it->second.flags & CCoinsCacheEntry::FRESH) {
entry.flags |= CCoinsCacheEntry::FRESH;
}
} else {
// Assert that the child cache entry was not marked FRESH if the
// parent cache entry has unspent outputs. If this ever happens,
// it means the FRESH flag was misapplied and there is a logic
// error in the calling code.
if ((it->second.flags & CCoinsCacheEntry::FRESH) && !itUs->second.coin.IsSpent())
throw std::logic_error("FRESH flag misapplied to cache entry for base transaction with spendable outputs");
}
} else {
// Assert that the child cache entry was not marked FRESH if the
// parent cache entry has unspent outputs. If this ever happens,
// it means the FRESH flag was misapplied and there is a logic
// error in the calling code.
if ((it->second.flags & CCoinsCacheEntry::FRESH) && !itUs->second.coin.IsSpent()) {
throw std::logic_error("FRESH flag misapplied to cache entry for base transaction with spendable outputs");
}

// Found the entry in the parent cache
if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coin.IsSpent()) {
// 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 -= itUs->second.coin.DynamicMemoryUsage();
cacheCoins.erase(itUs);
} else {
// A normal modification.
cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
itUs->second.coin = std::move(it->second.coin);
cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
// NOTE: It is possible the child has a FRESH flag here in
// the event the entry we found in the parent is pruned. But
// we must not copy that FRESH flag to the parent as that
// pruned state likely still needs to be communicated to the
// grandparent.
}
// Found the entry in the parent cache
if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coin.IsSpent()) {
// 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 -= itUs->second.coin.DynamicMemoryUsage();
cacheCoins.erase(itUs);
} else {
// A normal modification.
cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
itUs->second.coin = std::move(it->second.coin);
cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
// NOTE: It is possible the child has a FRESH flag here in
// the event the entry we found in the parent is pruned. But
// we must not copy that FRESH flag to the parent as that
// pruned state likely still needs to be communicated to the
// grandparent.
}
}
CCoinsMap::iterator itOld = it++;
mapCoins.erase(itOld);
}
hashBlock = hashBlockIn;
return true;
Expand Down

0 comments on commit 05a7619

Please sign in to comment.