Eigen  5.0.1
 
Loading...
Searching...
No Matches
LruCache.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2026 Rasmus Munk Larsen <rmlarsen@gmail.com>
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9// SPDX-License-Identifier: MPL-2.0
10
11#ifndef EIGEN_LRU_CACHE_H
12#define EIGEN_LRU_CACHE_H
13
14#include <cstddef>
15#include <functional>
16#include <list>
17#include <unordered_map>
18#include <utility>
19
20#include "../InternalHeaderCheck.h"
21
22namespace Eigen {
23namespace internal {
24
25// Bounded least-recently-used cache.
26//
27// Keyed on Key (hashed by Hash, compared by KeyEqual) and storing movable
28// Value objects. find() and insert() are O(1) average: an unordered_map
29// lookup plus a constant-time std::list splice. On hit, the touched entry
30// is promoted to the front of the list. On insert into a full cache, the
31// back of the list (least-recently-used) is destroyed before the new entry
32// is added — Value's destructor runs, so an RAII Value handles eviction
33// cleanup without any additional callback machinery.
34//
35// Thread safety: none. Callers must serialize.
36//
37// Not intended for hot-loop O(n) workloads where n is large; for the small
38// caches Eigen uses (handful of entries, low shape cardinality), the
39// node-allocation overhead of std::list / std::unordered_map is amortized
40// across many hits per insert.
41template <typename Key, typename Value, typename Hash = std::hash<Key>, typename KeyEqual = std::equal_to<Key>>
42class LruCache {
43 public:
44 using key_type = Key;
45 using value_type = Value;
46
47 explicit LruCache(std::size_t capacity) : capacity_(capacity) {
48 eigen_assert(capacity_ > 0 && "LruCache capacity must be positive");
49 // Pre-size the bucket array so the map never rehashes while filling. A
50 // rehash is otherwise guaranteed once load factor crosses ~1.0.
51 index_.reserve(capacity_);
52 }
53
54 LruCache(const LruCache&) = delete;
55 LruCache& operator=(const LruCache&) = delete;
56
57 LruCache(LruCache&& o) noexcept : capacity_(o.capacity_), items_(std::move(o.items_)), index_(std::move(o.index_)) {}
58
59 LruCache& operator=(LruCache&& o) noexcept {
60 if (this != &o) {
61 capacity_ = o.capacity_;
62 items_ = std::move(o.items_);
63 index_ = std::move(o.index_);
64 }
65 return *this;
66 }
67
68 ~LruCache() = default;
69
70 // Returns a pointer to the cached value and marks it most-recently-used,
71 // or nullptr if the key is not present.
72 Value* find(const Key& key) {
73 auto map_it = index_.find(key);
74 if (map_it == index_.end()) return nullptr;
75 items_.splice(items_.begin(), items_, map_it->second);
76 return &map_it->second->second;
77 }
78
79 // Inserts (key, value), evicting the least-recently-used entry if the cache
80 // is at capacity. If the key already exists, the existing entry is destroyed
81 // and a new entry is inserted as most-recently-used.
82 // Returns a pointer to the inserted value, or nullptr if capacity is 0
83 // (degenerate case — assert-firing in debug; the early return keeps release
84 // builds from dereferencing items_.back() on an empty list).
85 Value* insert(const Key& key, Value value) {
86 if (capacity_ == 0) return nullptr;
87 auto map_it = index_.find(key);
88 if (map_it != index_.end()) {
89 auto old_it = map_it->second;
90 items_.emplace_front(key, std::move(value));
91 map_it->second = items_.begin();
92 items_.erase(old_it);
93 return &map_it->second->second;
94 }
95 if (items_.size() >= capacity_) {
96 index_.erase(items_.back().first);
97 items_.pop_back();
98 }
99 items_.emplace_front(key, std::move(value));
100 index_.emplace(key, items_.begin());
101 return &items_.front().second;
102 }
103
104 void clear() {
105 items_.clear();
106 index_.clear();
107 }
108
109 std::size_t size() const { return items_.size(); }
110 std::size_t capacity() const { return capacity_; }
111 bool empty() const { return items_.empty(); }
112
113 private:
114 using list_type = std::list<std::pair<Key, Value>>;
115
116 std::size_t capacity_;
117 list_type items_;
118 std::unordered_map<Key, typename list_type::iterator, Hash, KeyEqual> index_;
119};
120
121} // namespace internal
122} // namespace Eigen
123
124#endif // EIGEN_LRU_CACHE_H