1848b8605Smrg/**************************************************************************
2848b8605Smrg *
3848b8605Smrg * Copyright 2008 VMware, Inc.
4848b8605Smrg * All Rights Reserved.
5848b8605Smrg *
6848b8605Smrg * Permission is hereby granted, free of charge, to any person obtaining a
7848b8605Smrg * copy of this software and associated documentation files (the
8848b8605Smrg * "Software"), to deal in the Software without restriction, including
9848b8605Smrg * without limitation the rights to use, copy, modify, merge, publish,
10848b8605Smrg * distribute, sub license, and/or sell copies of the Software, and to
11848b8605Smrg * permit persons to whom the Software is furnished to do so, subject to
12848b8605Smrg * the following conditions:
13848b8605Smrg *
14848b8605Smrg * The above copyright notice and this permission notice (including the
15848b8605Smrg * next paragraph) shall be included in all copies or substantial portions
16848b8605Smrg * of the Software.
17848b8605Smrg *
18848b8605Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19848b8605Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20848b8605Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21848b8605Smrg * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22848b8605Smrg * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23848b8605Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24848b8605Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25848b8605Smrg *
26848b8605Smrg **************************************************************************/
27848b8605Smrg
28848b8605Smrg/**
29848b8605Smrg * @file
30848b8605Smrg * Improved cache implementation.
31848b8605Smrg *
32848b8605Smrg * Fixed size array with linear probing on collision and LRU eviction
33848b8605Smrg * on full.
34848b8605Smrg *
35848b8605Smrg * @author Jose Fonseca <jfonseca@vmware.com>
36848b8605Smrg */
37848b8605Smrg
38848b8605Smrg
39848b8605Smrg#include "pipe/p_compiler.h"
40848b8605Smrg#include "util/u_debug.h"
41848b8605Smrg
42848b8605Smrg#include "util/u_math.h"
43848b8605Smrg#include "util/u_memory.h"
44848b8605Smrg#include "util/u_cache.h"
45b8e80941Smrg#include "util/simple_list.h"
46848b8605Smrg
47848b8605Smrg
48848b8605Smrgstruct util_cache_entry
49848b8605Smrg{
50848b8605Smrg   enum { EMPTY = 0, FILLED, DELETED } state;
51848b8605Smrg   uint32_t hash;
52848b8605Smrg
53848b8605Smrg   struct util_cache_entry *next;
54848b8605Smrg   struct util_cache_entry *prev;
55848b8605Smrg
56848b8605Smrg   void *key;
57848b8605Smrg   void *value;
58848b8605Smrg
59848b8605Smrg#ifdef DEBUG
60848b8605Smrg   unsigned count;
61848b8605Smrg#endif
62848b8605Smrg};
63848b8605Smrg
64848b8605Smrg
65848b8605Smrgstruct util_cache
66848b8605Smrg{
67848b8605Smrg   /** Hash function */
68848b8605Smrg   uint32_t (*hash)(const void *key);
69848b8605Smrg
70848b8605Smrg   /** Compare two keys */
71848b8605Smrg   int (*compare)(const void *key1, const void *key2);
72848b8605Smrg
73848b8605Smrg   /** Destroy a (key, value) pair */
74848b8605Smrg   void (*destroy)(void *key, void *value);
75848b8605Smrg
76b8e80941Smrg   /** Max entries in the cache */
77848b8605Smrg   uint32_t size;
78848b8605Smrg
79b8e80941Smrg   /** Array [size] of entries */
80848b8605Smrg   struct util_cache_entry *entries;
81848b8605Smrg
82b8e80941Smrg   /** Number of entries in the cache */
83848b8605Smrg   unsigned count;
84b8e80941Smrg
85b8e80941Smrg   /** Head of list, sorted from Least-recently used to Most-recently used */
86848b8605Smrg   struct util_cache_entry lru;
87848b8605Smrg};
88848b8605Smrg
89b8e80941Smrg
90848b8605Smrgstatic void
91848b8605Smrgensure_sanity(const struct util_cache *cache);
92848b8605Smrg
93848b8605Smrg#define CACHE_DEFAULT_ALPHA 2
94848b8605Smrg
95b8e80941Smrg/**
96b8e80941Smrg * Create a new cache with 'size' entries.  Also provide functions for
97b8e80941Smrg * hashing keys, comparing keys and destroying (key,value) pairs.
98b8e80941Smrg */
99848b8605Smrgstruct util_cache *
100848b8605Smrgutil_cache_create(uint32_t (*hash)(const void *key),
101848b8605Smrg                  int (*compare)(const void *key1, const void *key2),
102848b8605Smrg                  void (*destroy)(void *key, void *value),
103848b8605Smrg                  uint32_t size)
104848b8605Smrg{
105848b8605Smrg   struct util_cache *cache;
106848b8605Smrg
107848b8605Smrg   cache = CALLOC_STRUCT(util_cache);
108b8e80941Smrg   if (!cache)
109848b8605Smrg      return NULL;
110848b8605Smrg
111848b8605Smrg   cache->hash = hash;
112848b8605Smrg   cache->compare = compare;
113848b8605Smrg   cache->destroy = destroy;
114848b8605Smrg
115848b8605Smrg   make_empty_list(&cache->lru);
116848b8605Smrg
117848b8605Smrg   size *= CACHE_DEFAULT_ALPHA;
118848b8605Smrg   cache->size = size;
119848b8605Smrg
120848b8605Smrg   cache->entries = CALLOC(size, sizeof(struct util_cache_entry));
121b8e80941Smrg   if (!cache->entries) {
122848b8605Smrg      FREE(cache);
123848b8605Smrg      return NULL;
124848b8605Smrg   }
125848b8605Smrg
126848b8605Smrg   ensure_sanity(cache);
127848b8605Smrg   return cache;
128848b8605Smrg}
129848b8605Smrg
130848b8605Smrg
131b8e80941Smrg/**
132b8e80941Smrg * Try to find a cache entry, given the key and hash of the key.
133b8e80941Smrg */
134848b8605Smrgstatic struct util_cache_entry *
135848b8605Smrgutil_cache_entry_get(struct util_cache *cache,
136848b8605Smrg                     uint32_t hash,
137848b8605Smrg                     const void *key)
138848b8605Smrg{
139848b8605Smrg   struct util_cache_entry *first_unfilled = NULL;
140848b8605Smrg   uint32_t index = hash % cache->size;
141848b8605Smrg   uint32_t probe;
142848b8605Smrg
143848b8605Smrg   /* Probe until we find either a matching FILLED entry or an EMPTY
144848b8605Smrg    * slot (which has never been occupied).
145848b8605Smrg    *
146848b8605Smrg    * Deleted or non-matching slots are not indicative of completion
147848b8605Smrg    * as a previous linear probe for the same key could have continued
148848b8605Smrg    * past this point.
149848b8605Smrg    */
150848b8605Smrg   for (probe = 0; probe < cache->size; probe++) {
151848b8605Smrg      uint32_t i = (index + probe) % cache->size;
152848b8605Smrg      struct util_cache_entry *current = &cache->entries[i];
153848b8605Smrg
154848b8605Smrg      if (current->state == FILLED) {
155848b8605Smrg         if (current->hash == hash &&
156848b8605Smrg             cache->compare(key, current->key) == 0)
157848b8605Smrg            return current;
158848b8605Smrg      }
159848b8605Smrg      else {
160848b8605Smrg         if (!first_unfilled)
161848b8605Smrg            first_unfilled = current;
162848b8605Smrg
163848b8605Smrg         if (current->state == EMPTY)
164848b8605Smrg            return first_unfilled;
165848b8605Smrg      }
166848b8605Smrg   }
167848b8605Smrg
168848b8605Smrg   return NULL;
169848b8605Smrg}
170848b8605Smrg
171b8e80941Smrgstatic inline void
172848b8605Smrgutil_cache_entry_destroy(struct util_cache *cache,
173848b8605Smrg                         struct util_cache_entry *entry)
174848b8605Smrg{
175848b8605Smrg   void *key = entry->key;
176848b8605Smrg   void *value = entry->value;
177848b8605Smrg
178848b8605Smrg   entry->key = NULL;
179848b8605Smrg   entry->value = NULL;
180848b8605Smrg
181848b8605Smrg   if (entry->state == FILLED) {
182848b8605Smrg      remove_from_list(entry);
183848b8605Smrg      cache->count--;
184848b8605Smrg
185b8e80941Smrg      if (cache->destroy)
186848b8605Smrg         cache->destroy(key, value);
187848b8605Smrg
188848b8605Smrg      entry->state = DELETED;
189848b8605Smrg   }
190848b8605Smrg}
191848b8605Smrg
192848b8605Smrg
193b8e80941Smrg/**
194b8e80941Smrg * Insert an entry in the cache, given the key and value.
195b8e80941Smrg */
196848b8605Smrgvoid
197848b8605Smrgutil_cache_set(struct util_cache *cache,
198848b8605Smrg               void *key,
199848b8605Smrg               void *value)
200848b8605Smrg{
201848b8605Smrg   struct util_cache_entry *entry;
202848b8605Smrg   uint32_t hash;
203848b8605Smrg
204848b8605Smrg   assert(cache);
205848b8605Smrg   if (!cache)
206848b8605Smrg      return;
207848b8605Smrg   hash = cache->hash(key);
208848b8605Smrg   entry = util_cache_entry_get(cache, hash, key);
209848b8605Smrg   if (!entry)
210848b8605Smrg      entry = cache->lru.prev;
211848b8605Smrg
212848b8605Smrg   if (cache->count >= cache->size / CACHE_DEFAULT_ALPHA)
213848b8605Smrg      util_cache_entry_destroy(cache, cache->lru.prev);
214848b8605Smrg
215848b8605Smrg   util_cache_entry_destroy(cache, entry);
216848b8605Smrg
217848b8605Smrg#ifdef DEBUG
218848b8605Smrg   ++entry->count;
219848b8605Smrg#endif
220848b8605Smrg
221848b8605Smrg   entry->key = key;
222848b8605Smrg   entry->hash = hash;
223848b8605Smrg   entry->value = value;
224848b8605Smrg   entry->state = FILLED;
225848b8605Smrg   insert_at_head(&cache->lru, entry);
226848b8605Smrg   cache->count++;
227848b8605Smrg
228848b8605Smrg   ensure_sanity(cache);
229848b8605Smrg}
230848b8605Smrg
231848b8605Smrg
232b8e80941Smrg/**
233b8e80941Smrg * Search the cache for an entry with the given key.  Return the corresponding
234b8e80941Smrg * value or NULL if not found.
235b8e80941Smrg */
236848b8605Smrgvoid *
237848b8605Smrgutil_cache_get(struct util_cache *cache,
238848b8605Smrg               const void *key)
239848b8605Smrg{
240848b8605Smrg   struct util_cache_entry *entry;
241848b8605Smrg   uint32_t hash;
242848b8605Smrg
243848b8605Smrg   assert(cache);
244848b8605Smrg   if (!cache)
245848b8605Smrg      return NULL;
246848b8605Smrg   hash = cache->hash(key);
247848b8605Smrg   entry = util_cache_entry_get(cache, hash, key);
248848b8605Smrg   if (!entry)
249848b8605Smrg      return NULL;
250848b8605Smrg
251848b8605Smrg   if (entry->state == FILLED)
252848b8605Smrg      move_to_head(&cache->lru, entry);
253848b8605Smrg
254848b8605Smrg   return entry->value;
255848b8605Smrg}
256848b8605Smrg
257848b8605Smrg
258b8e80941Smrg/**
259b8e80941Smrg * Remove all entries from the cache.  The 'destroy' function will be called
260b8e80941Smrg * for each entry's (key, value).
261b8e80941Smrg */
262848b8605Smrgvoid
263848b8605Smrgutil_cache_clear(struct util_cache *cache)
264848b8605Smrg{
265848b8605Smrg   uint32_t i;
266848b8605Smrg
267848b8605Smrg   assert(cache);
268848b8605Smrg   if (!cache)
269848b8605Smrg      return;
270848b8605Smrg
271b8e80941Smrg   for (i = 0; i < cache->size; ++i) {
272848b8605Smrg      util_cache_entry_destroy(cache, &cache->entries[i]);
273848b8605Smrg      cache->entries[i].state = EMPTY;
274848b8605Smrg   }
275848b8605Smrg
276848b8605Smrg   assert(cache->count == 0);
277848b8605Smrg   assert(is_empty_list(&cache->lru));
278848b8605Smrg   ensure_sanity(cache);
279848b8605Smrg}
280848b8605Smrg
281848b8605Smrg
282b8e80941Smrg/**
283b8e80941Smrg * Destroy the cache and all entries.
284b8e80941Smrg */
285848b8605Smrgvoid
286848b8605Smrgutil_cache_destroy(struct util_cache *cache)
287848b8605Smrg{
288848b8605Smrg   assert(cache);
289848b8605Smrg   if (!cache)
290848b8605Smrg      return;
291848b8605Smrg
292848b8605Smrg#ifdef DEBUG
293b8e80941Smrg   if (cache->count >= 20*cache->size) {
294848b8605Smrg      /* Normal approximation of the Poisson distribution */
295848b8605Smrg      double mean = (double)cache->count/(double)cache->size;
296848b8605Smrg      double stddev = sqrt(mean);
297848b8605Smrg      unsigned i;
298b8e80941Smrg      for (i = 0; i < cache->size; ++i) {
299848b8605Smrg         double z = fabs(cache->entries[i].count - mean)/stddev;
300848b8605Smrg         /* This assert should not fail 99.9999998027% of the times, unless
301848b8605Smrg          * the hash function is a poor one */
302848b8605Smrg         assert(z <= 6.0);
303848b8605Smrg      }
304848b8605Smrg   }
305848b8605Smrg#endif
306848b8605Smrg
307848b8605Smrg   util_cache_clear(cache);
308848b8605Smrg
309848b8605Smrg   FREE(cache->entries);
310848b8605Smrg   FREE(cache);
311848b8605Smrg}
312848b8605Smrg
313848b8605Smrg
314b8e80941Smrg/**
315b8e80941Smrg * Remove the cache entry which matches the given key.
316b8e80941Smrg */
317848b8605Smrgvoid
318848b8605Smrgutil_cache_remove(struct util_cache *cache,
319848b8605Smrg                  const void *key)
320848b8605Smrg{
321848b8605Smrg   struct util_cache_entry *entry;
322848b8605Smrg   uint32_t hash;
323848b8605Smrg
324848b8605Smrg   assert(cache);
325848b8605Smrg   if (!cache)
326848b8605Smrg      return;
327848b8605Smrg
328848b8605Smrg   hash = cache->hash(key);
329848b8605Smrg
330848b8605Smrg   entry = util_cache_entry_get(cache, hash, key);
331848b8605Smrg   if (!entry)
332848b8605Smrg      return;
333848b8605Smrg
334848b8605Smrg   if (entry->state == FILLED)
335848b8605Smrg      util_cache_entry_destroy(cache, entry);
336848b8605Smrg
337848b8605Smrg   ensure_sanity(cache);
338848b8605Smrg}
339848b8605Smrg
340848b8605Smrg
341848b8605Smrgstatic void
342848b8605Smrgensure_sanity(const struct util_cache *cache)
343848b8605Smrg{
344848b8605Smrg#ifdef DEBUG
345848b8605Smrg   unsigned i, cnt = 0;
346848b8605Smrg
347848b8605Smrg   assert(cache);
348848b8605Smrg   for (i = 0; i < cache->size; i++) {
349848b8605Smrg      struct util_cache_entry *header = &cache->entries[i];
350848b8605Smrg
351848b8605Smrg      assert(header);
352848b8605Smrg      assert(header->state == FILLED ||
353848b8605Smrg             header->state == EMPTY ||
354848b8605Smrg             header->state == DELETED);
355848b8605Smrg      if (header->state == FILLED) {
356848b8605Smrg         cnt++;
357848b8605Smrg         assert(header->hash == cache->hash(header->key));
358848b8605Smrg      }
359848b8605Smrg   }
360848b8605Smrg
361848b8605Smrg   assert(cnt == cache->count);
362848b8605Smrg   assert(cache->size >= cnt);
363848b8605Smrg
364848b8605Smrg   if (cache->count == 0) {
365848b8605Smrg      assert (is_empty_list(&cache->lru));
366848b8605Smrg   }
367848b8605Smrg   else {
368848b8605Smrg      struct util_cache_entry *header = cache->lru.next;
369848b8605Smrg
370848b8605Smrg      assert (header);
371848b8605Smrg      assert (!is_empty_list(&cache->lru));
372848b8605Smrg
373848b8605Smrg      for (i = 0; i < cache->count; i++)
374848b8605Smrg         header = header->next;
375848b8605Smrg
376848b8605Smrg      assert(header == &cache->lru);
377848b8605Smrg   }
378848b8605Smrg#endif
379848b8605Smrg
380848b8605Smrg   (void)cache;
381848b8605Smrg}
382