From 9e4aa1f98c1e47b3ccd7599d57d26eb31d8a5fe1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 11 Mar 2019 06:33:46 +0100 Subject: [PATCH] Implement unordered_lru_cache --- src/Makefile.am | 1 + src/unordered_lru_cache.h | 110 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 src/unordered_lru_cache.h diff --git a/src/Makefile.am b/src/Makefile.am index e87247f8b4..25818bc202 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -227,6 +227,7 @@ BITCOIN_CORE_H = \ txmempool.h \ ui_interface.h \ undo.h \ + unordered_lru_cache.h \ util.h \ utilmoneystr.h \ utiltime.h \ diff --git a/src/unordered_lru_cache.h b/src/unordered_lru_cache.h new file mode 100644 index 0000000000..df685e260f --- /dev/null +++ b/src/unordered_lru_cache.h @@ -0,0 +1,110 @@ +// Copyright (c) 2019 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UNORDERED_LRU_CACHE_H +#define BITCOIN_UNORDERED_LRU_CACHE_H + +#include + +template +class unordered_lru_cache +{ +private: + typedef std::unordered_map, Hasher> MapType; + + MapType cacheMap; + size_t maxSize; + size_t truncateThreshold; + int64_t accessCounter{0}; + +public: + unordered_lru_cache(size_t _maxSize = MaxSize, size_t _truncateThreshold = TruncateThreshold) : + maxSize(_maxSize), + truncateThreshold(_truncateThreshold == 0 ? _maxSize * 2 : _truncateThreshold) + { + // either specify maxSize through template arguments or the contructor and fail otherwise + assert(_maxSize != 0); + } + + + template + void _emplace(const Key& key, Value2&& v) + { + truncate_if_needed(); + auto it = cacheMap.find(key); + if (it == cacheMap.end()) { + cacheMap.emplace(key, std::make_pair(std::forward(v), accessCounter++)); + } else { + it->second.first = std::forward(v); + it->second.second = accessCounter++; + } + } + + void emplace(const Key& key, Value&& v) + { + _emplace(key, v); + } + + void insert(const Key& key, const Value& v) + { + _emplace(key, v); + } + + bool get(const Key& key, Value& value) + { + auto it = cacheMap.find(key); + if (it != cacheMap.end()) { + it->second.second = accessCounter++; + value = it->second.first; + return true; + } + return false; + } + + bool exists(const Key& key) + { + auto it = cacheMap.find(key); + if (it != cacheMap.end()) { + it->second.second = accessCounter++; + return true; + } + return false; + } + + void erase(const Key& key) + { + cacheMap.erase(key); + } + + void clear() + { + cacheMap.clear(); + } + +private: + void truncate_if_needed() + { + typedef typename MapType::iterator Iterator; + + if (cacheMap.size() <= truncateThreshold) { + return; + } + + std::vector vec; + vec.reserve(cacheMap.size()); + for (auto it = cacheMap.begin(); it != cacheMap.end(); ++it) { + vec.emplace_back(it); + } + // sort by last access time (descending order) + std::sort(vec.begin(), vec.end(), [](const Iterator& it1, const Iterator& it2) { + return it1->second.second > it2->second.second; + }); + + for (size_t i = maxSize; i < vec.size(); i++) { + cacheMap.erase(vec[i]); + } + } +}; + +#endif // BITCOIN_UNORDERED_LRU_CACHE_H