• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   ** Copyright 2011, The Android Open Source Project
3   **
4   ** Licensed under the Apache License, Version 2.0 (the "License");
5   ** you may not use this file except in compliance with the License.
6   ** You may obtain a copy of the License at
7   **
8   **     http://www.apache.org/licenses/LICENSE-2.0
9   **
10   ** Unless required by applicable law or agreed to in writing, software
11   ** distributed under the License is distributed on an "AS IS" BASIS,
12   ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   ** See the License for the specific language governing permissions and
14   ** limitations under the License.
15   */
16  
17  #ifndef ANDROID_FRAMEWORKS_ML_NN_DRIVER_CACHE_BLOB_CACHE_BLOB_CACHE_H
18  #define ANDROID_FRAMEWORKS_ML_NN_DRIVER_CACHE_BLOB_CACHE_BLOB_CACHE_H
19  
20  #include <stddef.h>
21  
22  #include <functional>
23  #include <memory>
24  #include <utility>
25  #include <vector>
26  
27  namespace android {
28  
29  // A BlobCache is an in-memory cache for binary key/value pairs.  A BlobCache
30  // does NOT provide any thread-safety guarantees.
31  //
32  // The cache contents can be serialized to an in-memory buffer or mmap'd file
33  // and then reloaded in a subsequent execution of the program.  This
34  // serialization is non-portable and the data should only be used by the device
35  // that generated it.
36  class BlobCache {
37     public:
38      enum class Select {
39          RANDOM,  // evict random entries
40          LRU,     // evict least-recently-used entries
41  
42          DEFAULT = RANDOM,
43      };
44  
45      enum class Capacity {
46          // cut back to no more than half capacity; new/replacement
47          // entry still might not fit
48          HALVE,
49  
50          // cut back to whatever is necessary to fit new/replacement
51          // entry
52          FIT,
53  
54          // cut back to no more than half capacity and ensure that
55          // there's enough space for new/replacement entry
56          FIT_HALVE,
57  
58          DEFAULT = HALVE,
59      };
60  
61      // When we're inserting or replacing an entry in the cache, and
62      // there's not enough space, how do we clean the cache?
63      typedef std::pair<Select, Capacity> Policy;
64  
defaultPolicy()65      static Policy defaultPolicy() { return Policy(Select::DEFAULT, Capacity::DEFAULT); }
66  
67      // Create an empty blob cache. The blob cache will cache key/value pairs
68      // with key and value sizes less than or equal to maxKeySize and
69      // maxValueSize, respectively. The total combined size of ALL cache entries
70      // (key sizes plus value sizes) will not exceed maxTotalSize.
71      BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
72                Policy policy = defaultPolicy());
73  
74      // set inserts a new binary value into the cache and associates it with the
75      // given binary key.  If the key or value are too large for the cache then
76      // the cache remains unchanged.  This includes the case where a different
77      // value was previously associated with the given key - the old value will
78      // remain in the cache.  If the given key and value are small enough to be
79      // put in the cache (based on the maxKeySize, maxValueSize, and maxTotalSize
80      // values specified to the BlobCache constructor), then the key/value pair
81      // will be in the cache after set returns.  Note, however, that a subsequent
82      // call to set may evict old key/value pairs from the cache.
83      //
84      // Preconditions:
85      //   key != NULL
86      //   0 < keySize
87      //   value != NULL
88      //   0 < valueSize
89      void set(const void* key, size_t keySize, const void* value, size_t valueSize);
90  
91      // get retrieves from the cache the binary value associated with a given
92      // binary key.  If the key is present in the cache then the length of the
93      // binary value associated with that key is returned.  If the key
94      // is not present in the cache then 0 is returned.
95      //
96      // There are two variants of get: one takes a buffer (value, valueSize)
97      // and one takes an allocator (value, alloc).
98      //
99      // For the BUFFER variant, if the value argument is non-NULL and
100      // the size of the cached value is less than valueSize bytes then
101      // the cached value is copied into the buffer pointed to by the
102      // value argument.  If the key is not present in the cache then
103      // the buffer pointed to by the value argument is not modified.
104      //
105      //   Preconditions:
106      //     key != NULL
107      //     0 < keySize
108      //     0 <= valueSize
109      //
110      // For the ALLOCATOR variant, if it is possible to allocate a
111      // buffer for the cached value via a call to the allocator by
112      //
113      //   size_t cached_value_size = ...;
114      //   void* buf = alloc(cached_value_size);
115      //
116      // then the cached value is copied into the newly-allocated buffer
117      // and *value is set to the address of the newly-allocated buffer.
118      // If the allocator returns NULL, or the key is not present in the
119      // cache, then *value is set to NULL.
120      //
121      //   Preconditions:
122      //     key != NULL
123      //     0 < keySize
124      //     value != NULL
125      //
126      // Note that when calling get multiple times with the same key, the later
127      // calls may fail, returning 0, even if earlier calls succeeded.  The return
128      // value must be checked for each call.
129      size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
130      size_t get(const void* key, size_t keySize, void** value, std::function<void*(size_t)> alloc);
131      template <typename T>
get(const void * key,size_t keySize,T ** value,std::function<void * (size_t)> alloc)132      size_t get(const void* key, size_t keySize, T** value, std::function<void*(size_t)> alloc) {
133          void* valueVoid;
134          const size_t size = get(key, keySize, &valueVoid, alloc);
135          *value = static_cast<T*>(valueVoid);
136          return size;
137      }
138  
139      // getFlattenedSize returns the number of bytes needed to store the entire
140      // serialized cache.
141      size_t getFlattenedSize() const;
142  
143      // flatten serializes the current contents of the cache into the memory
144      // pointed to by 'buffer'.  The serialized cache contents can later be
145      // loaded into a BlobCache object using the unflatten method.  The contents
146      // of the BlobCache object will not be modified.
147      //
148      // Preconditions:
149      //   size >= this.getFlattenedSize()
150      int flatten(void* buffer, size_t size) const;
151  
152      // unflatten replaces the contents of the cache with the serialized cache
153      // contents in the memory pointed to by 'buffer'.  The previous contents of
154      // the BlobCache will be evicted from the cache.  If an error occurs while
155      // unflattening the serialized cache contents then the BlobCache will be
156      // left in an empty state.
157      //
158      int unflatten(void const* buffer, size_t size);
159  
160     private:
161      // Copying is disallowed.
162      BlobCache(const BlobCache&);
163      void operator=(const BlobCache&);
164  
165      // A random function helper to get around MinGW not having nrand48()
166      long int blob_random();
167  
168      // Use this in place of a cache entry index to indicate that no
169      // entry is being designated.
170      static const size_t NoEntry = ~size_t(0);
171  
172      // Is this Capacity value one of the *FIT* values?
173      static bool isFit(Capacity capacity);
174  
175      // clean evicts a selected set of entries from the cache to make
176      // room for a new entry or for replacing an entry with a larger
177      // one.  mSelect determines how to pick entries to evict, and
178      // mCapacity determines when to stop evicting entries.
179      //
180      // newEntrySize is the size of the entry we want to add to the
181      // cache, or the new size of the entry we want to replace in the
182      // cache.
183      //
184      // If we are replacing an entry in the cache, then onBehalfOf is
185      // the index of that entry in the cache; otherwise, it is NoEntry.
186      //
187      // Returns true if at least one entry is evicted.
188      bool clean(size_t newEntrySize, size_t onBehalfOf);
189  
190      // isCleanable returns true if the cache is full enough for the clean method
191      // to have some effect, and false otherwise.
192      bool isCleanable() const;
193  
194      // findVictim selects an entry to remove from the cache.  The
195      // cache must not be empty.
196      size_t findVictim();
197  
198      // findDownTo determines how far to clean the cache -- until it
199      // results in a total size that does not exceed the return value
200      // of findDownTo.  newEntrySize and onBehalfOf have the same
201      // meanings they do for clean.
202      size_t findDownTo(size_t newEntrySize, size_t onBehalfOf);
203  
204      // A Blob is an immutable sized unstructured data blob.
205      class Blob {
206         public:
207          Blob(const void* data, size_t size, bool copyData);
208          ~Blob();
209  
210          bool operator<(const Blob& rhs) const;
211  
212          const void* getData() const;
213          size_t getSize() const;
214  
215         private:
216          // Copying is not allowed.
217          Blob(const Blob&);
218          void operator=(const Blob&);
219  
220          // mData points to the buffer containing the blob data.
221          const void* mData;
222  
223          // mSize is the size of the blob data in bytes.
224          size_t mSize;
225  
226          // mOwnsData indicates whether or not this Blob object should free the
227          // memory pointed to by mData when the Blob gets destructed.
228          bool mOwnsData;
229      };
230  
231      // A CacheEntry is a single key/value pair in the cache.
232      class CacheEntry {
233         public:
234          CacheEntry();
235          CacheEntry(const std::shared_ptr<Blob>& key, const std::shared_ptr<Blob>& value,
236                     uint32_t recency);
237          CacheEntry(const CacheEntry& ce);
238  
239          bool operator<(const CacheEntry& rhs) const;
240          const CacheEntry& operator=(const CacheEntry&);
241  
242          std::shared_ptr<Blob> getKey() const;
243          std::shared_ptr<Blob> getValue() const;
244  
245          void setValue(const std::shared_ptr<Blob>& value);
246  
247          uint32_t getRecency() const;
248          void setRecency(uint32_t recency);
249  
250         private:
251          // mKey is the key that identifies the cache entry.
252          std::shared_ptr<Blob> mKey;
253  
254          // mValue is the cached data associated with the key.
255          std::shared_ptr<Blob> mValue;
256  
257          // mRecency is the last "time" (as indicated by
258          // BlobCache::mAccessCount) that this entry was accessed.
259          uint32_t mRecency;
260      };
261  
262      // A Header is the header for the entire BlobCache serialization format. No
263      // need to make this portable, so we simply write the struct out.
264      struct Header {
265          // mMagicNumber is the magic number that identifies the data as
266          // serialized BlobCache contents.  It must always contain 'Blb$'.
267          uint32_t mMagicNumber;
268  
269          // mBlobCacheVersion is the serialization format version.
270          uint32_t mBlobCacheVersion;
271  
272          // mDeviceVersion is the device-specific version of the cache.  This can
273          // be used to invalidate the cache.
274          uint32_t mDeviceVersion;
275  
276          // mNumEntries is number of cache entries following the header in the
277          // data.
278          size_t mNumEntries;
279  
280          // mBuildId is the build id of the device when the cache was created.
281          // When an update to the build happens (via an OTA or other update) this
282          // is used to invalidate the cache.
283          int mBuildIdLength;
284          char mBuildId[];
285      };
286  
287      // An EntryHeader is the header for a serialized cache entry.  No need to
288      // make this portable, so we simply write the struct out.  Each EntryHeader
289      // is followed imediately by the key data and then the value data.
290      //
291      // The beginning of each serialized EntryHeader is 4-byte aligned, so the
292      // number of bytes that a serialized cache entry will occupy is:
293      //
294      //   ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3
295      //
296      struct EntryHeader {
297          // mKeySize is the size of the entry key in bytes.
298          size_t mKeySize;
299  
300          // mValueSize is the size of the entry value in bytes.
301          size_t mValueSize;
302  
303          // mData contains both the key and value data for the cache entry.  The
304          // key comes first followed immediately by the value.
305          uint8_t mData[];
306      };
307  
308      // mMaxKeySize is the maximum key size that will be cached. Calls to
309      // BlobCache::set with a keySize parameter larger than mMaxKeySize will
310      // simply not add the key/value pair to the cache.
311      const size_t mMaxKeySize;
312  
313      // mMaxValueSize is the maximum value size that will be cached. Calls to
314      // BlobCache::set with a valueSize parameter larger than mMaxValueSize will
315      // simply not add the key/value pair to the cache.
316      const size_t mMaxValueSize;
317  
318      // mMaxTotalSize is the maximum size that all cache entries can occupy. This
319      // includes space for both keys and values. When a call to BlobCache::set
320      // would otherwise cause this limit to be exceeded, either the key/value
321      // pair passed to BlobCache::set will not be cached or other cache entries
322      // will be evicted from the cache to make room for the new entry.
323      const size_t mMaxTotalSize;
324  
325      // mPolicySelect indicates how we pick entries to evict from the cache.
326      const Select mPolicySelect;
327  
328      // mPolicyCapacity indicates how we decide when to stop evicting
329      // entries from the cache.
330      const Capacity mPolicyCapacity;
331  
332      // mTotalSize is the total combined size of all keys and values currently in
333      // the cache.
334      size_t mTotalSize;
335  
336      // mAccessCount is the number of times an entry has been
337      // added/replaced by set(), or its content (not just its size)
338      // retrieved by get().  It serves as a clock for recognizing how
339      // recently an entry was accessed, for the Select::LRU policy.
340      uint32_t mAccessCount;
341  
342      // mRandState is the pseudo-random number generator state. It is passed to
343      // nrand48 to generate random numbers when needed.
344      unsigned short mRandState[3];
345  
346      // mCacheEntries stores all the cache entries that are resident in memory.
347      // Cache entries are added to it by the 'set' method.
348      std::vector<CacheEntry> mCacheEntries;
349  };
350  
351  }  // namespace android
352  
353  #endif  // ANDROID_FRAMEWORKS_ML_NN_DRIVER_CACHE_BLOB_CACHE_BLOB_CACHE_H
354