tatami_chunked
Helpers to create custom chunked tatami matrices
Loading...
Searching...
No Matches
LruSlabCache.hpp
Go to the documentation of this file.
1#ifndef TATAMI_CHUNKED_LRU_SLAB_CACHE_HPP
2#define TATAMI_CHUNKED_LRU_SLAB_CACHE_HPP
3
4#include <unordered_map>
5#include <list>
6#include <cstddef>
7
8#include "sanisizer/sanisizer.hpp"
9
15namespace tatami_chunked {
16
28template<typename Id_, class Slab_>
30private:
31 typedef std::pair<Slab_, Id_> Element;
32 typedef std::list<Element> SlabPool;
33
34 typename SlabPool::size_type my_max_slabs;
35 SlabPool my_cache_data;
36 std::unordered_map<Id_, typename std::list<Element>::iterator> my_cache_exists;
37
38 Id_ my_last_id = 0;
39 Slab_* my_last_slab = NULL;
40
41public:
46 template<typename MaxSlabs_>
47 LruSlabCache(MaxSlabs_ max_slabs) : my_max_slabs(sanisizer::cast<decltype(my_max_slabs)>(max_slabs)) {}
48
52 LruSlabCache(const LruSlabCache&) = delete;
53
58
62 // Iterators are guaranteed to be valid after move, see Notes in
63 // https://en.cppreference.com/w/cpp/container/list/list
64 // https://en.cppreference.com/w/cpp/container/list/operator%3D
65 LruSlabCache& operator=(LruSlabCache&&) = default;
66 LruSlabCache(LruSlabCache&&) = default;
67
68 // Might as well define this.
69 ~LruSlabCache() = default;
74public:
93 template<class Cfunction_, class Pfunction_>
94 const Slab_& find(Id_ id, Cfunction_ create, Pfunction_ populate) {
95 if (id == my_last_id && my_last_slab) {
96 return *my_last_slab;
97 }
98 my_last_id = id;
99
100 auto it = my_cache_exists.find(id);
101 if (it != my_cache_exists.end()) {
102 auto chosen = it->second;
103 my_cache_data.splice(my_cache_data.end(), my_cache_data, chosen); // move to end.
104 my_last_slab = &(chosen->first);
105 return chosen->first;
106 }
107
108 typename std::list<Element>::iterator location;
109 if (my_cache_data.size() < my_max_slabs) {
110 my_cache_data.emplace_back(create(), id);
111 location = std::prev(my_cache_data.end());
112 } else {
113 location = my_cache_data.begin();
114 my_cache_exists.erase(location->second);
115 location->second = id;
116 my_cache_data.splice(my_cache_data.end(), my_cache_data, location); // move to end.
117 }
118 my_cache_exists[id] = location;
119
120 auto& slab = location->first;
121 populate(id, slab);
122 my_last_slab = &slab;
123 return slab;
124 }
125
126public:
131 auto get_max_slabs() const {
132 return my_max_slabs;
133 }
134
139 auto get_num_slabs() const {
140 return my_cache_data.size();
141 }
142};
143
144// COMMENT:
145// As tempting as it is to implement an LruVariableSlabCache, this doesn't work out well in practice.
146// This is because the Slab_ objects are re-used, and in the worst case, each object would have to be large enough to fit the largest slab.
147// At this point, we have several options:
148//
149// - Pre-allocate each Slab_ instance to have enough memory to fit the largest slab, in which case the slabs are not variable.
150// - Allow Slab_ instances to grow/shrink their memory allocation according to the size of its assigned slab.
151// This reduces efficiency due to repeated reallocations, and memory usage might end up exceeding the nominal limit anyway due to fragmentation.
152// - Share a single memory pool across slabs, and manually handle defragmentation to free up enough contiguous memory for each new slab.
153// Unlike the oracular case, we don't have the luxury of defragmenting once for multiple slabs.
154// Instead, we might potentially need to defragment on every newly requested slab, which is computationally expensive.
155//
156// See also https://softwareengineering.stackexchange.com/questions/398503/is-lru-still-a-good-algorithm-for-a-cache-with-diferent-size-elements.
157// This lists a few methods for dealing with fragmentation, but none of them are particularly clean.
158// It's likely that just using the existing LruSlabCache with the maximum possible slab size is good enough for most applications.
159
160}
161
162#endif
Least-recently-used cache for slabs.
Definition LruSlabCache.hpp:29
const Slab_ & find(Id_ id, Cfunction_ create, Pfunction_ populate)
Definition LruSlabCache.hpp:94
LruSlabCache(const LruSlabCache &)=delete
auto get_num_slabs() const
Definition LruSlabCache.hpp:139
LruSlabCache & operator=(const LruSlabCache &)=delete
auto get_max_slabs() const
Definition LruSlabCache.hpp:131
LruSlabCache(MaxSlabs_ max_slabs)
Definition LruSlabCache.hpp:47
Methods to handle chunked tatami matrices.
Definition ChunkDimensionStats.hpp:4