Loading [MathJax]/extensions/TeX/AMSmath.js
ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
Cache.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2017-2022 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include <cstddef>
23#include <memory>
24#include <random>
25#include <type_traits>
26#include <unordered_map>
27
28namespace Utils {
29template <typename Key, typename Value> class Cache {
30 using map_type =
31 std::unordered_map<Key, typename std::add_const<Value>::type>;
32
33public:
34 using key_type = Key;
35 using value_type = const Value *;
36 using size_type = typename map_type::size_type;
37
39 /* Default to maximal size of our map. */
40 m_max_size = m_cache.max_size();
41 }
42 explicit Cache(size_type max_size) : m_max_size(max_size), m_rand(max_size) {}
43
44private:
45 /** @brief The actual cache, maps Key -> Value.
46 */
47 map_type m_cache;
48 size_type m_max_size;
49
50 /** Cheap linear-congruential PRNG. */
51 std::minstd_rand m_rand;
52
53 /** @brief Drop an element.
54 *
55 * This function drops an element from the cache,
56 * it is used when a new item is to be cached, but then
57 * maximal size is reached. We use the bucket interface
58 * here to be able to pick a random element in constant
59 * time.
60 */
61 void drop_random_element() {
62 if (m_cache.empty())
63 return;
64
65 auto const bucket_count = m_cache.bucket_count();
66
67 /* Pick a random bucket, this has to terminate because
68 * the map is not empty. So there has to be a bucket with
69 * at least one element. */
70 auto bucket =
71 std::uniform_int_distribution<size_type>{0, bucket_count - 1}(m_rand);
72
73 while (0 == m_cache.bucket_size(bucket)) {
74 /* Wrap around the end */
75 bucket = ((bucket + 1) % bucket_count);
76 }
77
78 /* Pick a random element form that bucket. */
79 auto const elem_index = std::uniform_int_distribution<size_type>{
80 0, m_cache.bucket_size(bucket) - 1}(m_rand);
81
82 /* Get the element in the bucket */
83 auto const drop_key =
84 std::next(m_cache.cbegin(bucket), static_cast<long>(elem_index))->first;
85
86 /* And drop it. */
87 m_cache.erase(drop_key);
88 }
89
90public:
91 /** @brief Clear the cache.
92 *
93 * This invalidates all pointers into the cache.
94 */
95 void invalidate() { m_cache.clear(); }
96
97 /** @brief Query if k is contained in the cache. */
98 bool has(Key const &k) const { return m_cache.find(k) != m_cache.end(); }
99
100 /** @brief Number of elements currently cached. */
101 size_type size() const { return m_cache.size(); }
102
103 /** @brief Maximal size of the cache. */
104 size_type max_size() const { return m_max_size; }
105
106 /** @brief Put a value into the cache.
107 *
108 * If the value already exists, it is overwritten.
109 * When the size of the cache would grow below the
110 * maximal size, a random element is removed before
111 * putting the new one. */
112 template <typename ValueRef> Value const *put(Key const &k, ValueRef &&v) {
113 auto check_for_k = true;
114
115 /* If there already is a value for k, overwriting it
116 * will not increase the size, so we don't have to
117 * make room. */
118 if ((m_cache.size() >= m_max_size) && !has(k)) {
119 drop_random_element();
120 check_for_k = false;
121 }
122
123 if (check_for_k && has(k)) {
124 m_cache.erase(k);
125 }
126
127 typename map_type::const_iterator it;
128 std::tie(it, std::ignore) = m_cache.emplace(k, std::forward<ValueRef>(v));
129
130 return &(it->second);
131 }
132
133 /** @brief Put a range of values into the cache.
134 *
135 * If the values already exists, it is overwritten.
136 * When the size of the cache would grow below the
137 * maximal size, a random elements are removed until
138 * all of the new values fit. If the given range is
139 * larger than max_size(), only the first max_size()
140 * elements are put into the cache.
141 *
142 * @tparam KeyInputIterator iterator of keys, at least InputIterator.
143 * @tparam ValueInputIterator iterator of value, at least InputIterator.
144 *
145 * @returns KeyInputIterator one past the last element that was put
146 * into the cache.
147 */
148 template <typename KeyInputIterator, typename ValueInputIterator>
149 KeyInputIterator put(KeyInputIterator kbegin, KeyInputIterator kend,
150 ValueInputIterator vbegin) {
151 auto const range_len = std::distance(kbegin, kend);
152 auto const size_max = static_cast<decltype(range_len)>(max_size());
153 auto const len = (range_len > size_max) ? size_max : range_len;
154 kend = std::next(kbegin, len);
155
156 /* Make some space. */
157 while (static_cast<decltype(len)>(max_size() - size()) < len) {
158 drop_random_element();
159 }
160
161 while (kbegin != kend) {
162 put(*kbegin++, *vbegin++);
163 }
164
165 return kend;
166 }
167
168 /** @brief Get a value.
169 *
170 * The value is owned by the cache and can not be modified.
171 * Pointers into the cache can be invalidated at any point
172 * and should not be stored beyond the calling function.
173 */
174 Value const *get(Key const &k) const {
175 auto const needle = m_cache.find(k);
176
177 if (m_cache.end() != needle) {
178 return &(needle->second);
179 }
180 return nullptr;
181 }
182};
183} // namespace Utils
bool has(Key const &k) const
Query if k is contained in the cache.
Definition Cache.hpp:98
Key key_type
Definition Cache.hpp:34
void invalidate()
Clear the cache.
Definition Cache.hpp:95
const Value * value_type
Definition Cache.hpp:35
typename map_type::size_type size_type
Definition Cache.hpp:36
size_type size() const
Number of elements currently cached.
Definition Cache.hpp:101
Cache(size_type max_size)
Definition Cache.hpp:42
KeyInputIterator put(KeyInputIterator kbegin, KeyInputIterator kend, ValueInputIterator vbegin)
Put a range of values into the cache.
Definition Cache.hpp:149
Value const * put(Key const &k, ValueRef &&v)
Put a value into the cache.
Definition Cache.hpp:112
Value const * get(Key const &k) const
Get a value.
Definition Cache.hpp:174
size_type max_size() const
Maximal size of the cache.
Definition Cache.hpp:104