lsst.utils  16.0-6-g3610b4f+4
Cache.h
Go to the documentation of this file.
1 #ifndef LSST_UTILS_CACHE_H
2 #define LSST_UTILS_CACHE_H
3 
4 #include <vector>
5 #include <utility> // std::pair
6 
7 #include "boost/multi_index_container.hpp"
8 #include "boost/multi_index/sequenced_index.hpp"
9 #include "boost/multi_index/hashed_index.hpp"
10 #include "boost/multi_index/composite_key.hpp"
11 #include "boost/multi_index/member.hpp"
12 #include "boost/format.hpp"
13 
14 #include "lsst/pex/exceptions.h"
15 #include "lsst/utils/CacheFwd.h"
16 
17 //#define LSST_CACHE_DEBUG 1 // Define this variable to instrument for debugging
18 
19 
20 #ifdef LSST_CACHE_DEBUG
21 #include <iostream>
22 #include <fstream>
23 #include <typeinfo>
24 #include "lsst/utils/Demangle.h"
25 #endif
26 
27 namespace lsst {
28 namespace utils {
29 
52 template <typename Key, typename Value, typename KeyHash, typename KeyPred>
53 class Cache {
54  public:
63  Cache(std::size_t maxElements=0) : _maxElements(maxElements) {
64  _container.template get<Hash>().reserve(maxElements);
65 #ifdef LSST_CACHE_DEBUG
66  _debuggingEnabled = false;
67  _hits = 0;
68  _total = 0;
69  _requests.reserve(maxElements);
70 #endif
71  }
72 
73  // Defaulted stuff
74  Cache(Cache const &) = default;
75  Cache(Cache &&) = default;
76  Cache & operator=(Cache const &) = default;
77  Cache & operator=(Cache &&) = default;
78 
80 #ifdef LSST_CACHE_DEBUG
81  ~Cache();
84 #else
85  ~Cache() = default;
86 #endif
87 
108  template <typename Generator>
109  Value operator()(Key const& key, Generator func);
110 
111  /* Lookup a value
112  *
113  * If the key is in the cache, it will be promoted to the
114  * most recently used value.
115  *
116  * @throws lsst::pex::exceptions::NotFoundError If key is not in the
117  * cache.
118  *
119  * @exceptsafe Basic exception safety: exceptions will leave the
120  * system in a valid but unpredictable state.
121  */
122  Value operator[](Key const& key);
123 
132  void add(Key const& key, Value const& value);
133 
139  std::size_t size() const { return _container.size(); }
140 
146  std::vector<Key> keys() const;
147 
156  bool contains(Key const& key) { return _lookup(key).second; }
157 
162  std::size_t capacity() const { return _maxElements; }
163 
169  void reserve(std::size_t maxElements) { _maxElements = maxElements; _trim(); }
170 
176  void flush();
177 
178 #ifdef LSST_CACHE_DEBUG
179  void enableDebugging() { _debuggingEnabled = true; }
180 #endif
181 
182  private:
183 
184  // Trim the cache to size
185  void _trim() {
186  if (capacity() == 0) return; // Allowed to grow without limit
187  while (size() > capacity()) {
188  _container.template get<Sequence>().pop_back();
189  }
190  }
191 
192  // Element in the multi_index container
194 
195  // Tags for multi_index container
196  struct Sequence {};
197  struct Hash {};
198 
199  // The multi_index container
200  typedef boost::multi_index_container<
201  Element,
202  boost::multi_index::indexed_by<
203  boost::multi_index::sequenced<boost::multi_index::tag<Sequence>>,
204  boost::multi_index::hashed_unique<
205  boost::multi_index::tag<Hash>,
206  boost::multi_index::member<Element, Key, &Element::first>,
207  KeyHash>>> Container;
208 
209  // Lookup key in the container
210  //
211  // Returns the iterator and whether there's anything there.
212  //
213  // If the key exists, updates the cache to make that key the most recent.
215  auto const& hashContainer = _container.template get<Hash>();
216  auto it = hashContainer.find(key);
217  bool found = (it != hashContainer.end());
218  if (found) {
219  _container.relocate(_container.template get<Sequence>().begin(),
220  _container.template project<Sequence>(it));
221  }
222 #ifdef LSST_CACHE_DEBUG
223  if (_debuggingEnabled) {
224  _requests.push_back(key);
225  ++_total;
226  if (found) ++_hits;
227  }
228 #endif
229  return std::make_pair(it, found);
230  }
231 
232  // Add a key-value pair that are not already present
233  void _addNew(Key const& key, Value const& value) {
234  _container.template get<Sequence>().emplace_front(key, value);
235  _trim();
236  }
237 
238  std::size_t _maxElements; // Maximum number of elements; 0 means infinite
239  Container _container; // Container of key,value pairs
240 #ifdef LSST_CACHE_DEBUG
241  bool _debuggingEnabled;
242  mutable std::size_t _hits, _total; // Statistics of cache hits
243  mutable std::vector<Key> _requests; // Cache requests
244  static std::size_t getId() {
245  static std::size_t _id = 0; // Identifier
246  return _id++;
247  }
248 #endif
249 };
250 
251 // Definitions
252 
253 template <typename Key, typename Value, typename KeyHash, typename KeyPred>
254 template <typename Generator>
256  Key const& key,
257  Generator func
258 ) {
259  auto result = _lookup(key);
260  if (result.second) {
261  return result.first->second;
262  }
263  Value value = func(key);
264  _addNew(key, value);
265  return value;
266 }
267 
268 template <typename Key, typename Value, typename KeyHash, typename KeyPred>
270  auto result = _lookup(key);
271  if (result.second) {
272  return result.first->second;
273  }
275  (boost::format("Unable to find key: %s") % key).str());
276 }
277 
278 template <typename Key, typename Value, typename KeyHash, typename KeyPred>
279 void Cache<Key, Value, KeyHash, KeyPred>::add(Key const& key, Value const& value) {
280  auto result = _lookup(key);
281  if (!result.second) {
282  _addNew(key, value);
283  }
284 }
285 
286 template <typename Key, typename Value, typename KeyHash, typename KeyPred>
288  std::vector<Key> result;
289  result.reserve(size());
290  for (auto & keyValue : _container.template get<Sequence>()) {
291  result.push_back(keyValue.first);
292  }
293  return result;
294 }
295 
296 template <typename Key, typename Value, typename KeyHash, typename KeyPred>
298  while (size() > 0) {
299  _container.template get<Sequence>().pop_back();
300  }
301 }
302 
303 #ifdef LSST_CACHE_DEBUG
304 template <typename Key, typename Value, typename KeyHash, typename KeyPred>
306  if (!_debuggingEnabled) {
307  return;
308  }
309  std::string filename = (boost::format("lsst-cache-%s-%d.dat") %
310  demangleType(typeid(*this).name()) % getId()).str();
311  std::ofstream file(filename);
312  for (auto const& key : _requests) {
313  file << key << "\n";
314  }
315  file.close();
316  std::cerr << "Wrote cache requests to " << filename << ": " << _hits << "/" << _total << " hits";
317  std::cerr << std::endl;
318 }
319 #endif
320 
321 }} // namespace lsst::utils
322 
323 
324 #endif // ifndef LSST_UTILS_CACHE_H
std::string demangleType(std::string const _typeName)
Definition: Demangle.cc:113
Value operator[](Key const &key)
Definition: Cache.h:269
T endl(T... args)
void reserve(std::size_t maxElements)
Change the capacity of the cache.
Definition: Cache.h:169
Cache & operator=(Cache const &)=default
void flush()
Empty the cache.
Definition: Cache.h:297
STL class.
void add(Key const &key, Value const &value)
Add a value to the cache.
Definition: Cache.h:279
T push_back(T... args)
STL class.
~Cache()=default
Dtor.
Forward declarations for lsst::utils::Cache.
T close(T... args)
T make_pair(T... args)
std::size_t capacity() const
Return the capacity of the cache.
Definition: Cache.h:162
std::size_t size() const
Return the number of values in the cache.
Definition: Cache.h:139
bool contains(Key const &key)
Does the cache contain the key?
Definition: Cache.h:156
#define LSST_EXCEPT(type,...)
STL class.
Cache of most recently used values.
Definition: Cache.h:53
Value operator()(Key const &key, Generator func)
Lookup or generate a value.
Definition: Cache.h:255
Cache(std::size_t maxElements=0)
Ctor.
Definition: Cache.h:63
file
T reserve(T... args)
std::vector< Key > keys() const
Return all keys in the cache, most recent first.
Definition: Cache.h:287