xref: /aosp_15_r20/external/angle/src/libANGLE/SizedMRUCache.h (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker //
2*8975f5c5SAndroid Build Coastguard Worker // Copyright 2017 The ANGLE Project Authors. All rights reserved.
3*8975f5c5SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
4*8975f5c5SAndroid Build Coastguard Worker // found in the LICENSE file.
5*8975f5c5SAndroid Build Coastguard Worker //
6*8975f5c5SAndroid Build Coastguard Worker // SizedMRUCache.h: A hashing map that stores blobs of sized, untyped data.
7*8975f5c5SAndroid Build Coastguard Worker 
8*8975f5c5SAndroid Build Coastguard Worker #ifndef LIBANGLE_SIZED_MRU_CACHE_H_
9*8975f5c5SAndroid Build Coastguard Worker #define LIBANGLE_SIZED_MRU_CACHE_H_
10*8975f5c5SAndroid Build Coastguard Worker 
11*8975f5c5SAndroid Build Coastguard Worker #include <anglebase/containers/mru_cache.h>
12*8975f5c5SAndroid Build Coastguard Worker 
13*8975f5c5SAndroid Build Coastguard Worker namespace angle
14*8975f5c5SAndroid Build Coastguard Worker {
15*8975f5c5SAndroid Build Coastguard Worker 
16*8975f5c5SAndroid Build Coastguard Worker template <typename Key, typename Value>
17*8975f5c5SAndroid Build Coastguard Worker class SizedMRUCache final : angle::NonCopyable
18*8975f5c5SAndroid Build Coastguard Worker {
19*8975f5c5SAndroid Build Coastguard Worker   public:
SizedMRUCache(size_t maximumTotalSize)20*8975f5c5SAndroid Build Coastguard Worker     SizedMRUCache(size_t maximumTotalSize)
21*8975f5c5SAndroid Build Coastguard Worker         : mMaximumTotalSize(maximumTotalSize),
22*8975f5c5SAndroid Build Coastguard Worker           mCurrentSize(0),
23*8975f5c5SAndroid Build Coastguard Worker           mStore(SizedMRUCacheStore::NO_AUTO_EVICT)
24*8975f5c5SAndroid Build Coastguard Worker     {}
25*8975f5c5SAndroid Build Coastguard Worker 
26*8975f5c5SAndroid Build Coastguard Worker     // Returns nullptr on failure.
put(const Key & key,Value && value,size_t size)27*8975f5c5SAndroid Build Coastguard Worker     const Value *put(const Key &key, Value &&value, size_t size)
28*8975f5c5SAndroid Build Coastguard Worker     {
29*8975f5c5SAndroid Build Coastguard Worker         if (size > mMaximumTotalSize)
30*8975f5c5SAndroid Build Coastguard Worker         {
31*8975f5c5SAndroid Build Coastguard Worker             return nullptr;
32*8975f5c5SAndroid Build Coastguard Worker         }
33*8975f5c5SAndroid Build Coastguard Worker 
34*8975f5c5SAndroid Build Coastguard Worker         // Check for existing key.
35*8975f5c5SAndroid Build Coastguard Worker         eraseByKey(key);
36*8975f5c5SAndroid Build Coastguard Worker 
37*8975f5c5SAndroid Build Coastguard Worker         auto retVal = mStore.Put(key, ValueAndSize(std::move(value), size));
38*8975f5c5SAndroid Build Coastguard Worker         mCurrentSize += size;
39*8975f5c5SAndroid Build Coastguard Worker 
40*8975f5c5SAndroid Build Coastguard Worker         shrinkToSize(mMaximumTotalSize);
41*8975f5c5SAndroid Build Coastguard Worker 
42*8975f5c5SAndroid Build Coastguard Worker         return &retVal->second.value;
43*8975f5c5SAndroid Build Coastguard Worker     }
44*8975f5c5SAndroid Build Coastguard Worker 
get(const Key & key,const Value ** valueOut)45*8975f5c5SAndroid Build Coastguard Worker     bool get(const Key &key, const Value **valueOut)
46*8975f5c5SAndroid Build Coastguard Worker     {
47*8975f5c5SAndroid Build Coastguard Worker         const auto &iter = mStore.Get(key);
48*8975f5c5SAndroid Build Coastguard Worker         if (iter == mStore.end())
49*8975f5c5SAndroid Build Coastguard Worker         {
50*8975f5c5SAndroid Build Coastguard Worker             return false;
51*8975f5c5SAndroid Build Coastguard Worker         }
52*8975f5c5SAndroid Build Coastguard Worker         *valueOut = &iter->second.value;
53*8975f5c5SAndroid Build Coastguard Worker         return true;
54*8975f5c5SAndroid Build Coastguard Worker     }
55*8975f5c5SAndroid Build Coastguard Worker 
getAt(size_t index,const Key ** keyOut,const Value ** valueOut)56*8975f5c5SAndroid Build Coastguard Worker     bool getAt(size_t index, const Key **keyOut, const Value **valueOut)
57*8975f5c5SAndroid Build Coastguard Worker     {
58*8975f5c5SAndroid Build Coastguard Worker         if (index < mStore.size())
59*8975f5c5SAndroid Build Coastguard Worker         {
60*8975f5c5SAndroid Build Coastguard Worker             auto it = mStore.begin();
61*8975f5c5SAndroid Build Coastguard Worker             std::advance(it, index);
62*8975f5c5SAndroid Build Coastguard Worker             *keyOut   = &it->first;
63*8975f5c5SAndroid Build Coastguard Worker             *valueOut = &it->second.value;
64*8975f5c5SAndroid Build Coastguard Worker             return true;
65*8975f5c5SAndroid Build Coastguard Worker         }
66*8975f5c5SAndroid Build Coastguard Worker         *valueOut = nullptr;
67*8975f5c5SAndroid Build Coastguard Worker         return false;
68*8975f5c5SAndroid Build Coastguard Worker     }
69*8975f5c5SAndroid Build Coastguard Worker 
empty()70*8975f5c5SAndroid Build Coastguard Worker     bool empty() const { return mStore.empty(); }
71*8975f5c5SAndroid Build Coastguard Worker 
clear()72*8975f5c5SAndroid Build Coastguard Worker     void clear()
73*8975f5c5SAndroid Build Coastguard Worker     {
74*8975f5c5SAndroid Build Coastguard Worker         mStore.Clear();
75*8975f5c5SAndroid Build Coastguard Worker         mCurrentSize = 0;
76*8975f5c5SAndroid Build Coastguard Worker     }
77*8975f5c5SAndroid Build Coastguard Worker 
eraseByKey(const Key & key)78*8975f5c5SAndroid Build Coastguard Worker     void eraseByKey(const Key &key)
79*8975f5c5SAndroid Build Coastguard Worker     {
80*8975f5c5SAndroid Build Coastguard Worker         // Check for existing key.
81*8975f5c5SAndroid Build Coastguard Worker         auto existing = mStore.Peek(key);
82*8975f5c5SAndroid Build Coastguard Worker         if (existing != mStore.end())
83*8975f5c5SAndroid Build Coastguard Worker         {
84*8975f5c5SAndroid Build Coastguard Worker             mCurrentSize -= existing->second.size;
85*8975f5c5SAndroid Build Coastguard Worker             mStore.Erase(existing);
86*8975f5c5SAndroid Build Coastguard Worker         }
87*8975f5c5SAndroid Build Coastguard Worker     }
88*8975f5c5SAndroid Build Coastguard Worker 
entryCount()89*8975f5c5SAndroid Build Coastguard Worker     size_t entryCount() const { return mStore.size(); }
90*8975f5c5SAndroid Build Coastguard Worker 
size()91*8975f5c5SAndroid Build Coastguard Worker     size_t size() const { return mCurrentSize; }
92*8975f5c5SAndroid Build Coastguard Worker 
93*8975f5c5SAndroid Build Coastguard Worker     // Also discards the cache contents.
resize(size_t maximumTotalSize)94*8975f5c5SAndroid Build Coastguard Worker     void resize(size_t maximumTotalSize)
95*8975f5c5SAndroid Build Coastguard Worker     {
96*8975f5c5SAndroid Build Coastguard Worker         clear();
97*8975f5c5SAndroid Build Coastguard Worker         mMaximumTotalSize = maximumTotalSize;
98*8975f5c5SAndroid Build Coastguard Worker     }
99*8975f5c5SAndroid Build Coastguard Worker 
100*8975f5c5SAndroid Build Coastguard Worker     // Reduce current memory usage.
shrinkToSize(size_t limit)101*8975f5c5SAndroid Build Coastguard Worker     size_t shrinkToSize(size_t limit)
102*8975f5c5SAndroid Build Coastguard Worker     {
103*8975f5c5SAndroid Build Coastguard Worker         size_t initialSize = mCurrentSize;
104*8975f5c5SAndroid Build Coastguard Worker 
105*8975f5c5SAndroid Build Coastguard Worker         while (mCurrentSize > limit)
106*8975f5c5SAndroid Build Coastguard Worker         {
107*8975f5c5SAndroid Build Coastguard Worker             ASSERT(!mStore.empty());
108*8975f5c5SAndroid Build Coastguard Worker             auto iter = mStore.rbegin();
109*8975f5c5SAndroid Build Coastguard Worker             mCurrentSize -= iter->second.size;
110*8975f5c5SAndroid Build Coastguard Worker             mStore.Erase(iter);
111*8975f5c5SAndroid Build Coastguard Worker         }
112*8975f5c5SAndroid Build Coastguard Worker 
113*8975f5c5SAndroid Build Coastguard Worker         return (initialSize - mCurrentSize);
114*8975f5c5SAndroid Build Coastguard Worker     }
115*8975f5c5SAndroid Build Coastguard Worker 
maxSize()116*8975f5c5SAndroid Build Coastguard Worker     size_t maxSize() const { return mMaximumTotalSize; }
117*8975f5c5SAndroid Build Coastguard Worker 
118*8975f5c5SAndroid Build Coastguard Worker   private:
119*8975f5c5SAndroid Build Coastguard Worker     struct ValueAndSize
120*8975f5c5SAndroid Build Coastguard Worker     {
ValueAndSizeValueAndSize121*8975f5c5SAndroid Build Coastguard Worker         ValueAndSize() : value(), size(0) {}
ValueAndSizeValueAndSize122*8975f5c5SAndroid Build Coastguard Worker         ValueAndSize(Value &&value, size_t size) : value(std::move(value)), size(size) {}
ValueAndSizeValueAndSize123*8975f5c5SAndroid Build Coastguard Worker         ValueAndSize(ValueAndSize &&other) : ValueAndSize() { *this = std::move(other); }
124*8975f5c5SAndroid Build Coastguard Worker         ValueAndSize &operator=(ValueAndSize &&other)
125*8975f5c5SAndroid Build Coastguard Worker         {
126*8975f5c5SAndroid Build Coastguard Worker             std::swap(value, other.value);
127*8975f5c5SAndroid Build Coastguard Worker             std::swap(size, other.size);
128*8975f5c5SAndroid Build Coastguard Worker             return *this;
129*8975f5c5SAndroid Build Coastguard Worker         }
130*8975f5c5SAndroid Build Coastguard Worker 
131*8975f5c5SAndroid Build Coastguard Worker         Value value;
132*8975f5c5SAndroid Build Coastguard Worker         size_t size;
133*8975f5c5SAndroid Build Coastguard Worker     };
134*8975f5c5SAndroid Build Coastguard Worker 
135*8975f5c5SAndroid Build Coastguard Worker     using SizedMRUCacheStore = base::HashingMRUCache<Key, ValueAndSize>;
136*8975f5c5SAndroid Build Coastguard Worker 
137*8975f5c5SAndroid Build Coastguard Worker     size_t mMaximumTotalSize;
138*8975f5c5SAndroid Build Coastguard Worker     size_t mCurrentSize;
139*8975f5c5SAndroid Build Coastguard Worker     SizedMRUCacheStore mStore;
140*8975f5c5SAndroid Build Coastguard Worker };
141*8975f5c5SAndroid Build Coastguard Worker 
142*8975f5c5SAndroid Build Coastguard Worker // Helper function used in a few places.
143*8975f5c5SAndroid Build Coastguard Worker template <typename T>
TrimCache(size_t maxStates,size_t gcLimit,const char * name,T * cache)144*8975f5c5SAndroid Build Coastguard Worker void TrimCache(size_t maxStates, size_t gcLimit, const char *name, T *cache)
145*8975f5c5SAndroid Build Coastguard Worker {
146*8975f5c5SAndroid Build Coastguard Worker     const size_t kGarbageCollectionLimit = maxStates / 2 + gcLimit;
147*8975f5c5SAndroid Build Coastguard Worker 
148*8975f5c5SAndroid Build Coastguard Worker     if (cache->size() >= kGarbageCollectionLimit)
149*8975f5c5SAndroid Build Coastguard Worker     {
150*8975f5c5SAndroid Build Coastguard Worker         WARN() << "Overflowed the " << name << " cache limit of " << (maxStates / 2)
151*8975f5c5SAndroid Build Coastguard Worker                << " elements, removing the least recently used to make room.";
152*8975f5c5SAndroid Build Coastguard Worker         cache->ShrinkToSize(maxStates / 2);
153*8975f5c5SAndroid Build Coastguard Worker     }
154*8975f5c5SAndroid Build Coastguard Worker }
155*8975f5c5SAndroid Build Coastguard Worker }  // namespace angle
156*8975f5c5SAndroid Build Coastguard Worker #endif  // LIBANGLE_SIZED_MRU_CACHE_H_
157