14a49301eSmrg/**************************************************************************
24a49301eSmrg *
34a49301eSmrg * Copyright 2008 VMware, Inc.
44a49301eSmrg * All Rights Reserved.
54a49301eSmrg *
64a49301eSmrg * Permission is hereby granted, free of charge, to any person obtaining a
74a49301eSmrg * copy of this software and associated documentation files (the
84a49301eSmrg * "Software"), to deal in the Software without restriction, including
94a49301eSmrg * without limitation the rights to use, copy, modify, merge, publish,
104a49301eSmrg * distribute, sub license, and/or sell copies of the Software, and to
114a49301eSmrg * permit persons to whom the Software is furnished to do so, subject to
124a49301eSmrg * the following conditions:
134a49301eSmrg *
144a49301eSmrg * The above copyright notice and this permission notice (including the
154a49301eSmrg * next paragraph) shall be included in all copies or substantial portions
164a49301eSmrg * of the Software.
174a49301eSmrg *
184a49301eSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
194a49301eSmrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
204a49301eSmrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
214a49301eSmrg * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
224a49301eSmrg * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
234a49301eSmrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
244a49301eSmrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
254a49301eSmrg *
264a49301eSmrg **************************************************************************/
274a49301eSmrg
284a49301eSmrg/**
294a49301eSmrg * @file
303464ebd5Sriastradh * Improved cache implementation.
314a49301eSmrg *
323464ebd5Sriastradh * Fixed size array with linear probing on collision and LRU eviction
333464ebd5Sriastradh * on full.
344a49301eSmrg *
354a49301eSmrg * @author Jose Fonseca <jfonseca@vmware.com>
364a49301eSmrg */
374a49301eSmrg
384a49301eSmrg
394a49301eSmrg#include "pipe/p_compiler.h"
404a49301eSmrg#include "util/u_debug.h"
414a49301eSmrg
424a49301eSmrg#include "util/u_math.h"
434a49301eSmrg#include "util/u_memory.h"
444a49301eSmrg#include "util/u_cache.h"
4501e04c3fSmrg#include "util/simple_list.h"
464a49301eSmrg
474a49301eSmrg
484a49301eSmrgstruct util_cache_entry
494a49301eSmrg{
503464ebd5Sriastradh   enum { EMPTY = 0, FILLED, DELETED } state;
513464ebd5Sriastradh   uint32_t hash;
523464ebd5Sriastradh
533464ebd5Sriastradh   struct util_cache_entry *next;
543464ebd5Sriastradh   struct util_cache_entry *prev;
553464ebd5Sriastradh
564a49301eSmrg   void *key;
574a49301eSmrg   void *value;
584a49301eSmrg
594a49301eSmrg#ifdef DEBUG
604a49301eSmrg   unsigned count;
614a49301eSmrg#endif
624a49301eSmrg};
634a49301eSmrg
644a49301eSmrg
654a49301eSmrgstruct util_cache
664a49301eSmrg{
674a49301eSmrg   /** Hash function */
684a49301eSmrg   uint32_t (*hash)(const void *key);
694a49301eSmrg
704a49301eSmrg   /** Compare two keys */
714a49301eSmrg   int (*compare)(const void *key1, const void *key2);
724a49301eSmrg
734a49301eSmrg   /** Destroy a (key, value) pair */
744a49301eSmrg   void (*destroy)(void *key, void *value);
754a49301eSmrg
7601e04c3fSmrg   /** Max entries in the cache */
774a49301eSmrg   uint32_t size;
784a49301eSmrg
7901e04c3fSmrg   /** Array [size] of entries */
804a49301eSmrg   struct util_cache_entry *entries;
814a49301eSmrg
8201e04c3fSmrg   /** Number of entries in the cache */
834a49301eSmrg   unsigned count;
8401e04c3fSmrg
8501e04c3fSmrg   /** Head of list, sorted from Least-recently used to Most-recently used */
863464ebd5Sriastradh   struct util_cache_entry lru;
874a49301eSmrg};
884a49301eSmrg
8901e04c3fSmrg
903464ebd5Sriastradhstatic void
913464ebd5Sriastradhensure_sanity(const struct util_cache *cache);
923464ebd5Sriastradh
933464ebd5Sriastradh#define CACHE_DEFAULT_ALPHA 2
944a49301eSmrg
9501e04c3fSmrg/**
9601e04c3fSmrg * Create a new cache with 'size' entries.  Also provide functions for
9701e04c3fSmrg * hashing keys, comparing keys and destroying (key,value) pairs.
9801e04c3fSmrg */
994a49301eSmrgstruct util_cache *
1004a49301eSmrgutil_cache_create(uint32_t (*hash)(const void *key),
1014a49301eSmrg                  int (*compare)(const void *key1, const void *key2),
1024a49301eSmrg                  void (*destroy)(void *key, void *value),
1034a49301eSmrg                  uint32_t size)
1044a49301eSmrg{
1054a49301eSmrg   struct util_cache *cache;
1064a49301eSmrg
1074a49301eSmrg   cache = CALLOC_STRUCT(util_cache);
10801e04c3fSmrg   if (!cache)
1094a49301eSmrg      return NULL;
1104a49301eSmrg
1114a49301eSmrg   cache->hash = hash;
1124a49301eSmrg   cache->compare = compare;
1134a49301eSmrg   cache->destroy = destroy;
1143464ebd5Sriastradh
1153464ebd5Sriastradh   make_empty_list(&cache->lru);
1163464ebd5Sriastradh
1173464ebd5Sriastradh   size *= CACHE_DEFAULT_ALPHA;
1184a49301eSmrg   cache->size = size;
1194a49301eSmrg
1204a49301eSmrg   cache->entries = CALLOC(size, sizeof(struct util_cache_entry));
12101e04c3fSmrg   if (!cache->entries) {
1224a49301eSmrg      FREE(cache);
1234a49301eSmrg      return NULL;
1244a49301eSmrg   }
1254a49301eSmrg
1263464ebd5Sriastradh   ensure_sanity(cache);
1274a49301eSmrg   return cache;
1284a49301eSmrg}
1294a49301eSmrg
1304a49301eSmrg
13101e04c3fSmrg/**
13201e04c3fSmrg * Try to find a cache entry, given the key and hash of the key.
13301e04c3fSmrg */
1343464ebd5Sriastradhstatic struct util_cache_entry *
1354a49301eSmrgutil_cache_entry_get(struct util_cache *cache,
1363464ebd5Sriastradh                     uint32_t hash,
1374a49301eSmrg                     const void *key)
1384a49301eSmrg{
1393464ebd5Sriastradh   struct util_cache_entry *first_unfilled = NULL;
1403464ebd5Sriastradh   uint32_t index = hash % cache->size;
1413464ebd5Sriastradh   uint32_t probe;
1423464ebd5Sriastradh
1433464ebd5Sriastradh   /* Probe until we find either a matching FILLED entry or an EMPTY
1443464ebd5Sriastradh    * slot (which has never been occupied).
1453464ebd5Sriastradh    *
1463464ebd5Sriastradh    * Deleted or non-matching slots are not indicative of completion
1473464ebd5Sriastradh    * as a previous linear probe for the same key could have continued
1483464ebd5Sriastradh    * past this point.
1493464ebd5Sriastradh    */
1503464ebd5Sriastradh   for (probe = 0; probe < cache->size; probe++) {
1513464ebd5Sriastradh      uint32_t i = (index + probe) % cache->size;
1523464ebd5Sriastradh      struct util_cache_entry *current = &cache->entries[i];
1533464ebd5Sriastradh
1543464ebd5Sriastradh      if (current->state == FILLED) {
1553464ebd5Sriastradh         if (current->hash == hash &&
1563464ebd5Sriastradh             cache->compare(key, current->key) == 0)
1573464ebd5Sriastradh            return current;
1583464ebd5Sriastradh      }
1593464ebd5Sriastradh      else {
1603464ebd5Sriastradh         if (!first_unfilled)
1613464ebd5Sriastradh            first_unfilled = current;
1623464ebd5Sriastradh
1633464ebd5Sriastradh         if (current->state == EMPTY)
1643464ebd5Sriastradh            return first_unfilled;
1653464ebd5Sriastradh      }
1663464ebd5Sriastradh   }
1674a49301eSmrg
1683464ebd5Sriastradh   return NULL;
1694a49301eSmrg}
1704a49301eSmrg
17101e04c3fSmrgstatic inline void
1724a49301eSmrgutil_cache_entry_destroy(struct util_cache *cache,
1734a49301eSmrg                         struct util_cache_entry *entry)
1744a49301eSmrg{
1754a49301eSmrg   void *key = entry->key;
1764a49301eSmrg   void *value = entry->value;
1774a49301eSmrg
1784a49301eSmrg   entry->key = NULL;
1794a49301eSmrg   entry->value = NULL;
1804a49301eSmrg
1813464ebd5Sriastradh   if (entry->state == FILLED) {
1823464ebd5Sriastradh      remove_from_list(entry);
1833464ebd5Sriastradh      cache->count--;
1843464ebd5Sriastradh
18501e04c3fSmrg      if (cache->destroy)
1864a49301eSmrg         cache->destroy(key, value);
1873464ebd5Sriastradh
1883464ebd5Sriastradh      entry->state = DELETED;
1893464ebd5Sriastradh   }
1904a49301eSmrg}
1914a49301eSmrg
1924a49301eSmrg
19301e04c3fSmrg/**
19401e04c3fSmrg * Insert an entry in the cache, given the key and value.
19501e04c3fSmrg */
1964a49301eSmrgvoid
1974a49301eSmrgutil_cache_set(struct util_cache *cache,
1984a49301eSmrg               void *key,
1994a49301eSmrg               void *value)
2004a49301eSmrg{
2014a49301eSmrg   struct util_cache_entry *entry;
202af69d88dSmrg   uint32_t hash;
2034a49301eSmrg
2044a49301eSmrg   assert(cache);
2054a49301eSmrg   if (!cache)
2064a49301eSmrg      return;
207af69d88dSmrg   hash = cache->hash(key);
2083464ebd5Sriastradh   entry = util_cache_entry_get(cache, hash, key);
2093464ebd5Sriastradh   if (!entry)
2103464ebd5Sriastradh      entry = cache->lru.prev;
2113464ebd5Sriastradh
2123464ebd5Sriastradh   if (cache->count >= cache->size / CACHE_DEFAULT_ALPHA)
2133464ebd5Sriastradh      util_cache_entry_destroy(cache, cache->lru.prev);
2143464ebd5Sriastradh
2154a49301eSmrg   util_cache_entry_destroy(cache, entry);
2164a49301eSmrg
2174a49301eSmrg#ifdef DEBUG
2184a49301eSmrg   ++entry->count;
2194a49301eSmrg#endif
2204a49301eSmrg
2214a49301eSmrg   entry->key = key;
2223464ebd5Sriastradh   entry->hash = hash;
2234a49301eSmrg   entry->value = value;
2243464ebd5Sriastradh   entry->state = FILLED;
2253464ebd5Sriastradh   insert_at_head(&cache->lru, entry);
2263464ebd5Sriastradh   cache->count++;
2273464ebd5Sriastradh
2283464ebd5Sriastradh   ensure_sanity(cache);
2294a49301eSmrg}
2304a49301eSmrg
2314a49301eSmrg
23201e04c3fSmrg/**
23301e04c3fSmrg * Search the cache for an entry with the given key.  Return the corresponding
23401e04c3fSmrg * value or NULL if not found.
23501e04c3fSmrg */
2364a49301eSmrgvoid *
2374a49301eSmrgutil_cache_get(struct util_cache *cache,
2384a49301eSmrg               const void *key)
2394a49301eSmrg{
2404a49301eSmrg   struct util_cache_entry *entry;
241af69d88dSmrg   uint32_t hash;
2424a49301eSmrg
2434a49301eSmrg   assert(cache);
2444a49301eSmrg   if (!cache)
2454a49301eSmrg      return NULL;
246af69d88dSmrg   hash = cache->hash(key);
2473464ebd5Sriastradh   entry = util_cache_entry_get(cache, hash, key);
2483464ebd5Sriastradh   if (!entry)
2494a49301eSmrg      return NULL;
2503464ebd5Sriastradh
2513464ebd5Sriastradh   if (entry->state == FILLED)
2523464ebd5Sriastradh      move_to_head(&cache->lru, entry);
2534a49301eSmrg
2544a49301eSmrg   return entry->value;
2554a49301eSmrg}
2564a49301eSmrg
2574a49301eSmrg
25801e04c3fSmrg/**
25901e04c3fSmrg * Remove all entries from the cache.  The 'destroy' function will be called
26001e04c3fSmrg * for each entry's (key, value).
26101e04c3fSmrg */
2624a49301eSmrgvoid
2634a49301eSmrgutil_cache_clear(struct util_cache *cache)
2644a49301eSmrg{
2654a49301eSmrg   uint32_t i;
2664a49301eSmrg
2674a49301eSmrg   assert(cache);
2684a49301eSmrg   if (!cache)
2694a49301eSmrg      return;
2704a49301eSmrg
27101e04c3fSmrg   for (i = 0; i < cache->size; ++i) {
2724a49301eSmrg      util_cache_entry_destroy(cache, &cache->entries[i]);
2733464ebd5Sriastradh      cache->entries[i].state = EMPTY;
2743464ebd5Sriastradh   }
2753464ebd5Sriastradh
2763464ebd5Sriastradh   assert(cache->count == 0);
2773464ebd5Sriastradh   assert(is_empty_list(&cache->lru));
2783464ebd5Sriastradh   ensure_sanity(cache);
2794a49301eSmrg}
2804a49301eSmrg
2814a49301eSmrg
28201e04c3fSmrg/**
28301e04c3fSmrg * Destroy the cache and all entries.
28401e04c3fSmrg */
2854a49301eSmrgvoid
2864a49301eSmrgutil_cache_destroy(struct util_cache *cache)
2874a49301eSmrg{
2884a49301eSmrg   assert(cache);
2894a49301eSmrg   if (!cache)
2904a49301eSmrg      return;
2914a49301eSmrg
2924a49301eSmrg#ifdef DEBUG
29301e04c3fSmrg   if (cache->count >= 20*cache->size) {
2944a49301eSmrg      /* Normal approximation of the Poisson distribution */
2954a49301eSmrg      double mean = (double)cache->count/(double)cache->size;
2964a49301eSmrg      double stddev = sqrt(mean);
2974a49301eSmrg      unsigned i;
29801e04c3fSmrg      for (i = 0; i < cache->size; ++i) {
2993464ebd5Sriastradh         double z = fabs(cache->entries[i].count - mean)/stddev;
3004a49301eSmrg         /* This assert should not fail 99.9999998027% of the times, unless
3014a49301eSmrg          * the hash function is a poor one */
3024a49301eSmrg         assert(z <= 6.0);
3034a49301eSmrg      }
3044a49301eSmrg   }
3054a49301eSmrg#endif
3064a49301eSmrg
3074a49301eSmrg   util_cache_clear(cache);
3084a49301eSmrg
3094a49301eSmrg   FREE(cache->entries);
3104a49301eSmrg   FREE(cache);
3114a49301eSmrg}
3123464ebd5Sriastradh
3133464ebd5Sriastradh
31401e04c3fSmrg/**
31501e04c3fSmrg * Remove the cache entry which matches the given key.
31601e04c3fSmrg */
3173464ebd5Sriastradhvoid
3183464ebd5Sriastradhutil_cache_remove(struct util_cache *cache,
3193464ebd5Sriastradh                  const void *key)
3203464ebd5Sriastradh{
3213464ebd5Sriastradh   struct util_cache_entry *entry;
3223464ebd5Sriastradh   uint32_t hash;
3233464ebd5Sriastradh
3243464ebd5Sriastradh   assert(cache);
3253464ebd5Sriastradh   if (!cache)
3263464ebd5Sriastradh      return;
3273464ebd5Sriastradh
3283464ebd5Sriastradh   hash = cache->hash(key);
3293464ebd5Sriastradh
3303464ebd5Sriastradh   entry = util_cache_entry_get(cache, hash, key);
3313464ebd5Sriastradh   if (!entry)
3323464ebd5Sriastradh      return;
3333464ebd5Sriastradh
3343464ebd5Sriastradh   if (entry->state == FILLED)
3353464ebd5Sriastradh      util_cache_entry_destroy(cache, entry);
3363464ebd5Sriastradh
3373464ebd5Sriastradh   ensure_sanity(cache);
3383464ebd5Sriastradh}
3393464ebd5Sriastradh
3403464ebd5Sriastradh
3413464ebd5Sriastradhstatic void
3423464ebd5Sriastradhensure_sanity(const struct util_cache *cache)
3433464ebd5Sriastradh{
3443464ebd5Sriastradh#ifdef DEBUG
3453464ebd5Sriastradh   unsigned i, cnt = 0;
3463464ebd5Sriastradh
3473464ebd5Sriastradh   assert(cache);
3483464ebd5Sriastradh   for (i = 0; i < cache->size; i++) {
3493464ebd5Sriastradh      struct util_cache_entry *header = &cache->entries[i];
3503464ebd5Sriastradh
3513464ebd5Sriastradh      assert(header);
3523464ebd5Sriastradh      assert(header->state == FILLED ||
3533464ebd5Sriastradh             header->state == EMPTY ||
3543464ebd5Sriastradh             header->state == DELETED);
3553464ebd5Sriastradh      if (header->state == FILLED) {
3563464ebd5Sriastradh         cnt++;
3573464ebd5Sriastradh         assert(header->hash == cache->hash(header->key));
3583464ebd5Sriastradh      }
3593464ebd5Sriastradh   }
3603464ebd5Sriastradh
3613464ebd5Sriastradh   assert(cnt == cache->count);
3623464ebd5Sriastradh   assert(cache->size >= cnt);
3633464ebd5Sriastradh
3643464ebd5Sriastradh   if (cache->count == 0) {
3653464ebd5Sriastradh      assert (is_empty_list(&cache->lru));
3663464ebd5Sriastradh   }
3673464ebd5Sriastradh   else {
3683464ebd5Sriastradh      struct util_cache_entry *header = cache->lru.next;
3693464ebd5Sriastradh
3703464ebd5Sriastradh      assert (header);
3713464ebd5Sriastradh      assert (!is_empty_list(&cache->lru));
3723464ebd5Sriastradh
3733464ebd5Sriastradh      for (i = 0; i < cache->count; i++)
3743464ebd5Sriastradh         header = header->next;
3753464ebd5Sriastradh
3763464ebd5Sriastradh      assert(header == &cache->lru);
3773464ebd5Sriastradh   }
3783464ebd5Sriastradh#endif
3793464ebd5Sriastradh
3803464ebd5Sriastradh   (void)cache;
3813464ebd5Sriastradh}
382