1bf215546Sopenharmony_ci/************************************************************************** 2bf215546Sopenharmony_ci * 3bf215546Sopenharmony_ci * Copyright 2008 VMware, Inc. 4bf215546Sopenharmony_ci * All Rights Reserved. 5bf215546Sopenharmony_ci * 6bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 7bf215546Sopenharmony_ci * copy of this software and associated documentation files (the 8bf215546Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 9bf215546Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 10bf215546Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 11bf215546Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 12bf215546Sopenharmony_ci * the following conditions: 13bf215546Sopenharmony_ci * 14bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the 15bf215546Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 16bf215546Sopenharmony_ci * of the Software. 17bf215546Sopenharmony_ci * 18bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19bf215546Sopenharmony_ci * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20bf215546Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 21bf215546Sopenharmony_ci * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 22bf215546Sopenharmony_ci * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23bf215546Sopenharmony_ci * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24bf215546Sopenharmony_ci * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25bf215546Sopenharmony_ci * 26bf215546Sopenharmony_ci **************************************************************************/ 27bf215546Sopenharmony_ci 28bf215546Sopenharmony_ci/** 29bf215546Sopenharmony_ci * @file 30bf215546Sopenharmony_ci * Improved cache implementation. 31bf215546Sopenharmony_ci * 32bf215546Sopenharmony_ci * Fixed size array with linear probing on collision and LRU eviction 33bf215546Sopenharmony_ci * on full. 34bf215546Sopenharmony_ci * 35bf215546Sopenharmony_ci * @author Jose Fonseca <jfonseca@vmware.com> 36bf215546Sopenharmony_ci */ 37bf215546Sopenharmony_ci 38bf215546Sopenharmony_ci 39bf215546Sopenharmony_ci#include "pipe/p_compiler.h" 40bf215546Sopenharmony_ci#include "util/u_debug.h" 41bf215546Sopenharmony_ci 42bf215546Sopenharmony_ci#include "util/u_math.h" 43bf215546Sopenharmony_ci#include "util/u_memory.h" 44bf215546Sopenharmony_ci#include "util/u_cache.h" 45bf215546Sopenharmony_ci#include "util/list.h" 46bf215546Sopenharmony_ci 47bf215546Sopenharmony_cistruct util_cache_entry 48bf215546Sopenharmony_ci{ 49bf215546Sopenharmony_ci enum { EMPTY = 0, FILLED, DELETED } state; 50bf215546Sopenharmony_ci uint32_t hash; 51bf215546Sopenharmony_ci 52bf215546Sopenharmony_ci struct list_head list; 53bf215546Sopenharmony_ci 54bf215546Sopenharmony_ci void *key; 55bf215546Sopenharmony_ci void *value; 56bf215546Sopenharmony_ci 57bf215546Sopenharmony_ci#ifdef DEBUG 58bf215546Sopenharmony_ci unsigned count; 59bf215546Sopenharmony_ci#endif 60bf215546Sopenharmony_ci}; 61bf215546Sopenharmony_ci 62bf215546Sopenharmony_ci 63bf215546Sopenharmony_cistruct util_cache 64bf215546Sopenharmony_ci{ 65bf215546Sopenharmony_ci /** Hash function */ 66bf215546Sopenharmony_ci uint32_t (*hash)(const void *key); 67bf215546Sopenharmony_ci 68bf215546Sopenharmony_ci /** Compare two keys */ 69bf215546Sopenharmony_ci int (*compare)(const void *key1, const void *key2); 70bf215546Sopenharmony_ci 71bf215546Sopenharmony_ci /** Destroy a (key, value) pair */ 72bf215546Sopenharmony_ci void (*destroy)(void *key, void *value); 73bf215546Sopenharmony_ci 74bf215546Sopenharmony_ci /** Max entries in the cache */ 75bf215546Sopenharmony_ci uint32_t size; 76bf215546Sopenharmony_ci 77bf215546Sopenharmony_ci /** Array [size] of entries */ 78bf215546Sopenharmony_ci struct util_cache_entry *entries; 79bf215546Sopenharmony_ci 80bf215546Sopenharmony_ci /** Number of entries in the cache */ 81bf215546Sopenharmony_ci unsigned count; 82bf215546Sopenharmony_ci 83bf215546Sopenharmony_ci /** Head of list, sorted from Least-recently used to Most-recently used */ 84bf215546Sopenharmony_ci struct util_cache_entry lru; 85bf215546Sopenharmony_ci}; 86bf215546Sopenharmony_ci 87bf215546Sopenharmony_ci 88bf215546Sopenharmony_cistatic void 89bf215546Sopenharmony_ciensure_sanity(const struct util_cache *cache); 90bf215546Sopenharmony_ci 91bf215546Sopenharmony_ci#define CACHE_DEFAULT_ALPHA 2 92bf215546Sopenharmony_ci 93bf215546Sopenharmony_ci/** 94bf215546Sopenharmony_ci * Create a new cache with 'size' entries. Also provide functions for 95bf215546Sopenharmony_ci * hashing keys, comparing keys and destroying (key,value) pairs. 96bf215546Sopenharmony_ci */ 97bf215546Sopenharmony_cistruct util_cache * 98bf215546Sopenharmony_ciutil_cache_create(uint32_t (*hash)(const void *key), 99bf215546Sopenharmony_ci int (*compare)(const void *key1, const void *key2), 100bf215546Sopenharmony_ci void (*destroy)(void *key, void *value), 101bf215546Sopenharmony_ci uint32_t size) 102bf215546Sopenharmony_ci{ 103bf215546Sopenharmony_ci struct util_cache *cache; 104bf215546Sopenharmony_ci 105bf215546Sopenharmony_ci cache = CALLOC_STRUCT(util_cache); 106bf215546Sopenharmony_ci if (!cache) 107bf215546Sopenharmony_ci return NULL; 108bf215546Sopenharmony_ci 109bf215546Sopenharmony_ci cache->hash = hash; 110bf215546Sopenharmony_ci cache->compare = compare; 111bf215546Sopenharmony_ci cache->destroy = destroy; 112bf215546Sopenharmony_ci 113bf215546Sopenharmony_ci list_inithead(&cache->lru.list); 114bf215546Sopenharmony_ci 115bf215546Sopenharmony_ci size *= CACHE_DEFAULT_ALPHA; 116bf215546Sopenharmony_ci cache->size = size; 117bf215546Sopenharmony_ci 118bf215546Sopenharmony_ci cache->entries = CALLOC(size, sizeof(struct util_cache_entry)); 119bf215546Sopenharmony_ci if (!cache->entries) { 120bf215546Sopenharmony_ci FREE(cache); 121bf215546Sopenharmony_ci return NULL; 122bf215546Sopenharmony_ci } 123bf215546Sopenharmony_ci 124bf215546Sopenharmony_ci ensure_sanity(cache); 125bf215546Sopenharmony_ci return cache; 126bf215546Sopenharmony_ci} 127bf215546Sopenharmony_ci 128bf215546Sopenharmony_ci 129bf215546Sopenharmony_ci/** 130bf215546Sopenharmony_ci * Try to find a cache entry, given the key and hash of the key. 131bf215546Sopenharmony_ci */ 132bf215546Sopenharmony_cistatic struct util_cache_entry * 133bf215546Sopenharmony_ciutil_cache_entry_get(struct util_cache *cache, 134bf215546Sopenharmony_ci uint32_t hash, 135bf215546Sopenharmony_ci const void *key) 136bf215546Sopenharmony_ci{ 137bf215546Sopenharmony_ci struct util_cache_entry *first_unfilled = NULL; 138bf215546Sopenharmony_ci uint32_t index = hash % cache->size; 139bf215546Sopenharmony_ci uint32_t probe; 140bf215546Sopenharmony_ci 141bf215546Sopenharmony_ci /* Probe until we find either a matching FILLED entry or an EMPTY 142bf215546Sopenharmony_ci * slot (which has never been occupied). 143bf215546Sopenharmony_ci * 144bf215546Sopenharmony_ci * Deleted or non-matching slots are not indicative of completion 145bf215546Sopenharmony_ci * as a previous linear probe for the same key could have continued 146bf215546Sopenharmony_ci * past this point. 147bf215546Sopenharmony_ci */ 148bf215546Sopenharmony_ci for (probe = 0; probe < cache->size; probe++) { 149bf215546Sopenharmony_ci uint32_t i = (index + probe) % cache->size; 150bf215546Sopenharmony_ci struct util_cache_entry *current = &cache->entries[i]; 151bf215546Sopenharmony_ci 152bf215546Sopenharmony_ci if (current->state == FILLED) { 153bf215546Sopenharmony_ci if (current->hash == hash && 154bf215546Sopenharmony_ci cache->compare(key, current->key) == 0) 155bf215546Sopenharmony_ci return current; 156bf215546Sopenharmony_ci } 157bf215546Sopenharmony_ci else { 158bf215546Sopenharmony_ci if (!first_unfilled) 159bf215546Sopenharmony_ci first_unfilled = current; 160bf215546Sopenharmony_ci 161bf215546Sopenharmony_ci if (current->state == EMPTY) 162bf215546Sopenharmony_ci return first_unfilled; 163bf215546Sopenharmony_ci } 164bf215546Sopenharmony_ci } 165bf215546Sopenharmony_ci 166bf215546Sopenharmony_ci return NULL; 167bf215546Sopenharmony_ci} 168bf215546Sopenharmony_ci 169bf215546Sopenharmony_cistatic inline void 170bf215546Sopenharmony_ciutil_cache_entry_destroy(struct util_cache *cache, 171bf215546Sopenharmony_ci struct util_cache_entry *entry) 172bf215546Sopenharmony_ci{ 173bf215546Sopenharmony_ci void *key = entry->key; 174bf215546Sopenharmony_ci void *value = entry->value; 175bf215546Sopenharmony_ci 176bf215546Sopenharmony_ci entry->key = NULL; 177bf215546Sopenharmony_ci entry->value = NULL; 178bf215546Sopenharmony_ci 179bf215546Sopenharmony_ci if (entry->state == FILLED) { 180bf215546Sopenharmony_ci list_del(&entry->list); 181bf215546Sopenharmony_ci cache->count--; 182bf215546Sopenharmony_ci 183bf215546Sopenharmony_ci if (cache->destroy) 184bf215546Sopenharmony_ci cache->destroy(key, value); 185bf215546Sopenharmony_ci 186bf215546Sopenharmony_ci entry->state = DELETED; 187bf215546Sopenharmony_ci } 188bf215546Sopenharmony_ci} 189bf215546Sopenharmony_ci 190bf215546Sopenharmony_ci 191bf215546Sopenharmony_ci/** 192bf215546Sopenharmony_ci * Insert an entry in the cache, given the key and value. 193bf215546Sopenharmony_ci */ 194bf215546Sopenharmony_civoid 195bf215546Sopenharmony_ciutil_cache_set(struct util_cache *cache, 196bf215546Sopenharmony_ci void *key, 197bf215546Sopenharmony_ci void *value) 198bf215546Sopenharmony_ci{ 199bf215546Sopenharmony_ci struct util_cache_entry *entry; 200bf215546Sopenharmony_ci uint32_t hash; 201bf215546Sopenharmony_ci 202bf215546Sopenharmony_ci assert(cache); 203bf215546Sopenharmony_ci if (!cache) 204bf215546Sopenharmony_ci return; 205bf215546Sopenharmony_ci hash = cache->hash(key); 206bf215546Sopenharmony_ci entry = util_cache_entry_get(cache, hash, key); 207bf215546Sopenharmony_ci if (!entry) 208bf215546Sopenharmony_ci entry = list_last_entry(&cache->lru.list, struct util_cache_entry, list); 209bf215546Sopenharmony_ci 210bf215546Sopenharmony_ci if (cache->count >= cache->size / CACHE_DEFAULT_ALPHA) 211bf215546Sopenharmony_ci util_cache_entry_destroy(cache, list_last_entry(&cache->lru.list, struct util_cache_entry, list)); 212bf215546Sopenharmony_ci 213bf215546Sopenharmony_ci util_cache_entry_destroy(cache, entry); 214bf215546Sopenharmony_ci 215bf215546Sopenharmony_ci#ifdef DEBUG 216bf215546Sopenharmony_ci ++entry->count; 217bf215546Sopenharmony_ci#endif 218bf215546Sopenharmony_ci 219bf215546Sopenharmony_ci entry->key = key; 220bf215546Sopenharmony_ci entry->hash = hash; 221bf215546Sopenharmony_ci entry->value = value; 222bf215546Sopenharmony_ci entry->state = FILLED; 223bf215546Sopenharmony_ci list_add(&cache->lru.list, &entry->list); 224bf215546Sopenharmony_ci cache->count++; 225bf215546Sopenharmony_ci 226bf215546Sopenharmony_ci ensure_sanity(cache); 227bf215546Sopenharmony_ci} 228bf215546Sopenharmony_ci 229bf215546Sopenharmony_ci 230bf215546Sopenharmony_ci/** 231bf215546Sopenharmony_ci * Search the cache for an entry with the given key. Return the corresponding 232bf215546Sopenharmony_ci * value or NULL if not found. 233bf215546Sopenharmony_ci */ 234bf215546Sopenharmony_civoid * 235bf215546Sopenharmony_ciutil_cache_get(struct util_cache *cache, 236bf215546Sopenharmony_ci const void *key) 237bf215546Sopenharmony_ci{ 238bf215546Sopenharmony_ci struct util_cache_entry *entry; 239bf215546Sopenharmony_ci uint32_t hash; 240bf215546Sopenharmony_ci 241bf215546Sopenharmony_ci assert(cache); 242bf215546Sopenharmony_ci if (!cache) 243bf215546Sopenharmony_ci return NULL; 244bf215546Sopenharmony_ci hash = cache->hash(key); 245bf215546Sopenharmony_ci entry = util_cache_entry_get(cache, hash, key); 246bf215546Sopenharmony_ci if (!entry) 247bf215546Sopenharmony_ci return NULL; 248bf215546Sopenharmony_ci 249bf215546Sopenharmony_ci if (entry->state == FILLED) { 250bf215546Sopenharmony_ci list_move_to(&cache->lru.list, &entry->list); 251bf215546Sopenharmony_ci } 252bf215546Sopenharmony_ci 253bf215546Sopenharmony_ci return entry->value; 254bf215546Sopenharmony_ci} 255bf215546Sopenharmony_ci 256bf215546Sopenharmony_ci 257bf215546Sopenharmony_ci/** 258bf215546Sopenharmony_ci * Remove all entries from the cache. The 'destroy' function will be called 259bf215546Sopenharmony_ci * for each entry's (key, value). 260bf215546Sopenharmony_ci */ 261bf215546Sopenharmony_civoid 262bf215546Sopenharmony_ciutil_cache_clear(struct util_cache *cache) 263bf215546Sopenharmony_ci{ 264bf215546Sopenharmony_ci uint32_t i; 265bf215546Sopenharmony_ci 266bf215546Sopenharmony_ci assert(cache); 267bf215546Sopenharmony_ci if (!cache) 268bf215546Sopenharmony_ci return; 269bf215546Sopenharmony_ci 270bf215546Sopenharmony_ci for (i = 0; i < cache->size; ++i) { 271bf215546Sopenharmony_ci util_cache_entry_destroy(cache, &cache->entries[i]); 272bf215546Sopenharmony_ci cache->entries[i].state = EMPTY; 273bf215546Sopenharmony_ci } 274bf215546Sopenharmony_ci 275bf215546Sopenharmony_ci assert(cache->count == 0); 276bf215546Sopenharmony_ci assert(list_is_empty(&cache->lru.list)); 277bf215546Sopenharmony_ci ensure_sanity(cache); 278bf215546Sopenharmony_ci} 279bf215546Sopenharmony_ci 280bf215546Sopenharmony_ci 281bf215546Sopenharmony_ci/** 282bf215546Sopenharmony_ci * Destroy the cache and all entries. 283bf215546Sopenharmony_ci */ 284bf215546Sopenharmony_civoid 285bf215546Sopenharmony_ciutil_cache_destroy(struct util_cache *cache) 286bf215546Sopenharmony_ci{ 287bf215546Sopenharmony_ci assert(cache); 288bf215546Sopenharmony_ci if (!cache) 289bf215546Sopenharmony_ci return; 290bf215546Sopenharmony_ci 291bf215546Sopenharmony_ci#ifdef DEBUG 292bf215546Sopenharmony_ci if (cache->count >= 20*cache->size) { 293bf215546Sopenharmony_ci /* Normal approximation of the Poisson distribution */ 294bf215546Sopenharmony_ci double mean = (double)cache->count/(double)cache->size; 295bf215546Sopenharmony_ci double stddev = sqrt(mean); 296bf215546Sopenharmony_ci unsigned i; 297bf215546Sopenharmony_ci for (i = 0; i < cache->size; ++i) { 298bf215546Sopenharmony_ci double z = fabs(cache->entries[i].count - mean)/stddev; 299bf215546Sopenharmony_ci /* This assert should not fail 99.9999998027% of the times, unless 300bf215546Sopenharmony_ci * the hash function is a poor one */ 301bf215546Sopenharmony_ci assert(z <= 6.0); 302bf215546Sopenharmony_ci } 303bf215546Sopenharmony_ci } 304bf215546Sopenharmony_ci#endif 305bf215546Sopenharmony_ci 306bf215546Sopenharmony_ci util_cache_clear(cache); 307bf215546Sopenharmony_ci 308bf215546Sopenharmony_ci FREE(cache->entries); 309bf215546Sopenharmony_ci FREE(cache); 310bf215546Sopenharmony_ci} 311bf215546Sopenharmony_ci 312bf215546Sopenharmony_ci 313bf215546Sopenharmony_ci/** 314bf215546Sopenharmony_ci * Remove the cache entry which matches the given key. 315bf215546Sopenharmony_ci */ 316bf215546Sopenharmony_civoid 317bf215546Sopenharmony_ciutil_cache_remove(struct util_cache *cache, 318bf215546Sopenharmony_ci const void *key) 319bf215546Sopenharmony_ci{ 320bf215546Sopenharmony_ci struct util_cache_entry *entry; 321bf215546Sopenharmony_ci uint32_t hash; 322bf215546Sopenharmony_ci 323bf215546Sopenharmony_ci assert(cache); 324bf215546Sopenharmony_ci if (!cache) 325bf215546Sopenharmony_ci return; 326bf215546Sopenharmony_ci 327bf215546Sopenharmony_ci hash = cache->hash(key); 328bf215546Sopenharmony_ci 329bf215546Sopenharmony_ci entry = util_cache_entry_get(cache, hash, key); 330bf215546Sopenharmony_ci if (!entry) 331bf215546Sopenharmony_ci return; 332bf215546Sopenharmony_ci 333bf215546Sopenharmony_ci if (entry->state == FILLED) 334bf215546Sopenharmony_ci util_cache_entry_destroy(cache, entry); 335bf215546Sopenharmony_ci 336bf215546Sopenharmony_ci ensure_sanity(cache); 337bf215546Sopenharmony_ci} 338bf215546Sopenharmony_ci 339bf215546Sopenharmony_ci 340bf215546Sopenharmony_cistatic void 341bf215546Sopenharmony_ciensure_sanity(const struct util_cache *cache) 342bf215546Sopenharmony_ci{ 343bf215546Sopenharmony_ci#ifdef DEBUG 344bf215546Sopenharmony_ci unsigned i, cnt = 0; 345bf215546Sopenharmony_ci 346bf215546Sopenharmony_ci assert(cache); 347bf215546Sopenharmony_ci for (i = 0; i < cache->size; i++) { 348bf215546Sopenharmony_ci struct util_cache_entry *header = &cache->entries[i]; 349bf215546Sopenharmony_ci 350bf215546Sopenharmony_ci assert(header); 351bf215546Sopenharmony_ci assert(header->state == FILLED || 352bf215546Sopenharmony_ci header->state == EMPTY || 353bf215546Sopenharmony_ci header->state == DELETED); 354bf215546Sopenharmony_ci if (header->state == FILLED) { 355bf215546Sopenharmony_ci cnt++; 356bf215546Sopenharmony_ci assert(header->hash == cache->hash(header->key)); 357bf215546Sopenharmony_ci } 358bf215546Sopenharmony_ci } 359bf215546Sopenharmony_ci 360bf215546Sopenharmony_ci assert(cnt == cache->count); 361bf215546Sopenharmony_ci assert(cache->size >= cnt); 362bf215546Sopenharmony_ci 363bf215546Sopenharmony_ci if (cache->count == 0) { 364bf215546Sopenharmony_ci assert (list_is_empty(&cache->lru.list)); 365bf215546Sopenharmony_ci } 366bf215546Sopenharmony_ci else { 367bf215546Sopenharmony_ci struct util_cache_entry *header = 368bf215546Sopenharmony_ci list_entry(&cache->lru, struct util_cache_entry, list); 369bf215546Sopenharmony_ci 370bf215546Sopenharmony_ci assert (header); 371bf215546Sopenharmony_ci assert (!list_is_empty(&cache->lru.list)); 372bf215546Sopenharmony_ci 373bf215546Sopenharmony_ci for (i = 0; i < cache->count; i++) 374bf215546Sopenharmony_ci header = list_entry(&header, struct util_cache_entry, list); 375bf215546Sopenharmony_ci 376bf215546Sopenharmony_ci assert(header == &cache->lru); 377bf215546Sopenharmony_ci } 378bf215546Sopenharmony_ci#endif 379bf215546Sopenharmony_ci 380bf215546Sopenharmony_ci (void)cache; 381bf215546Sopenharmony_ci} 382