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 = std::unordered_map<Key, typename std::add_const_t<Value>>;
31
32public:
33 using key_type = Key;
34 using value_type = const Value *;
35 using size_type = typename map_type::size_type;
36
38 /* Default to maximal size of our map. */
39 m_max_size = m_cache.max_size();
40 }
41 explicit Cache(size_type max_size) : m_max_size(max_size), m_rand(max_size) {}
42
43private:
44 /** @brief The actual cache, maps Key -> Value.
45 */
46 map_type m_cache;
47 size_type m_max_size;
48
49 /** Cheap linear-congruential PRNG. */
50 std::minstd_rand m_rand;
51
52 /** @brief Drop an element.
53 *
54 * This function drops an element from the cache,
55 * it is used when a new item is to be cached, but then
56 * maximal size is reached. We use the bucket interface
57 * here to be able to pick a random element in constant
58 * time.
59 */
60 void drop_random_element() {
61 if (m_cache.empty())
62 return;
63
64 auto const bucket_count = m_cache.bucket_count();
65
66 /* Pick a random bucket, this has to terminate because
67 * the map is not empty. So there has to be a bucket with
68 * at least one element. */
69 auto bucket =
70 std::uniform_int_distribution<size_type>{0, bucket_count - 1}(m_rand);
71
72 while (0 == m_cache.bucket_size(bucket)) {
73 /* Wrap around the end */
74 bucket = ((bucket + 1) % bucket_count);
75 }
76
77 /* Pick a random element form that bucket. */
78 auto const elem_index = std::uniform_int_distribution<size_type>{
79 0, m_cache.bucket_size(bucket) - 1}(m_rand);
80
81 /* Get the element in the bucket */
82 auto const drop_key =
83 std::next(m_cache.cbegin(bucket), static_cast<long>(elem_index))->first;
84
85 /* And drop it. */
86 m_cache.erase(drop_key);
87 }
88
89public:
90 /** @brief Clear the cache.
91 *
92 * This invalidates all pointers into the cache.
93 */
94 void invalidate() { m_cache.clear(); }
95
96 /** @brief Query if k is contained in the cache. */
97 bool has(Key const &k) const { return m_cache.find(k) != m_cache.end(); }
98
99 /** @brief Number of elements currently cached. */
100 size_type size() const { return m_cache.size(); }
101
102 /** @brief Maximal size of the cache. */
103 size_type max_size() const { return m_max_size; }
104
105 /** @brief Put a value into the cache.
106 *
107 * If the value already exists, it is overwritten.
108 * When the size of the cache would grow below the
109 * maximal size, a random element is removed before
110 * putting the new one. */
111 template <typename ValueRef> Value const *put(Key const &k, ValueRef &&v) {
112 auto check_for_k = true;
113
114 /* If there already is a value for k, overwriting it
115 * will not increase the size, so we don't have to
116 * make room. */
117 if ((m_cache.size() >= m_max_size) && !has(k)) {
118 drop_random_element();
119 check_for_k = false;
120 }
121
122 if (check_for_k && has(k)) {
123 m_cache.erase(k);
124 }
125
126 typename map_type::const_iterator it;
127 std::tie(it, std::ignore) = m_cache.emplace(k, std::forward<ValueRef>(v));
128
129 return &(it->second);
130 }
131
132 /** @brief Put a range of values into the cache.
133 *
134 * If the values already exists, it is overwritten.
135 * When the size of the cache would grow below the
136 * maximal size, a random elements are removed until
137 * all of the new values fit. If the given range is
138 * larger than max_size(), only the first max_size()
139 * elements are put into the cache.
140 *
141 * @tparam KeyInputIterator iterator of keys, at least InputIterator.
142 * @tparam ValueInputIterator iterator of value, at least InputIterator.
143 *
144 * @returns KeyInputIterator one past the last element that was put
145 * into the cache.
146 */
147 template <typename KeyInputIterator, typename ValueInputIterator>
150 auto const range_len = std::distance(kbegin, kend);
151 auto const size_max = static_cast<decltype(range_len)>(max_size());
152 auto const len = (range_len > size_max) ? size_max : range_len;
153 kend = std::next(kbegin, len);
154
155 /* Make some space. */
156 while (static_cast<decltype(len)>(max_size() - size()) < len) {
157 drop_random_element();
158 }
159
160 while (kbegin != kend) {
161 put(*kbegin++, *vbegin++);
162 }
163
164 return kend;
165 }
166
167 /** @brief Get a value.
168 *
169 * The value is owned by the cache and can not be modified.
170 * Pointers into the cache can be invalidated at any point
171 * and should not be stored beyond the calling function.
172 */
173 Value const *get(Key const &k) const {
174 auto const needle = m_cache.find(k);
175
176 if (m_cache.end() != needle) {
177 return &(needle->second);
178 }
179 return nullptr;
180 }
181};
182} // namespace Utils
bool has(Key const &k) const
Query if k is contained in the cache.
Definition Cache.hpp:97
Key key_type
Definition Cache.hpp:33
void invalidate()
Clear the cache.
Definition Cache.hpp:94
const Value * value_type
Definition Cache.hpp:34
typename map_type::size_type size_type
Definition Cache.hpp:35
size_type size() const
Number of elements currently cached.
Definition Cache.hpp:100
Cache(size_type max_size)
Definition Cache.hpp:41
KeyInputIterator put(KeyInputIterator kbegin, KeyInputIterator kend, ValueInputIterator vbegin)
Put a range of values into the cache.
Definition Cache.hpp:148
Value const * put(Key const &k, ValueRef &&v)
Put a value into the cache.
Definition Cache.hpp:111
Value const * get(Key const &k) const
Get a value.
Definition Cache.hpp:173
size_type max_size() const
Maximal size of the cache.
Definition Cache.hpp:103
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.