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