ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
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