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