cache.cc
1 // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 5 #include <assert.h> 6 #include <stdio.h> 7 #include <stdlib.h> 8 9 #include "leveldb/cache.h" 10 #include "port/port.h" 11 #include "port/thread_annotations.h" 12 #include "util/hash.h" 13 #include "util/mutexlock.h" 14 15 namespace leveldb { 16 17 Cache::~Cache() {} 18 19 namespace { 20 21 // LRU cache implementation 22 // 23 // Cache entries have an "in_cache" boolean indicating whether the cache has a 24 // reference on the entry. The only ways that this can become false without the 25 // entry being passed to its "deleter" are via Erase(), via Insert() when 26 // an element with a duplicate key is inserted, or on destruction of the cache. 27 // 28 // The cache keeps two linked lists of items in the cache. All items in the 29 // cache are in one list or the other, and never both. Items still referenced 30 // by clients but erased from the cache are in neither list. The lists are: 31 // - in-use: contains the items currently referenced by clients, in no 32 // particular order. (This list is used for invariant checking. If we 33 // removed the check, elements that would otherwise be on this list could be 34 // left as disconnected singleton lists.) 35 // - LRU: contains the items not currently referenced by clients, in LRU order 36 // Elements are moved between these lists by the Ref() and Unref() methods, 37 // when they detect an element in the cache acquiring or losing its only 38 // external reference. 39 40 // An entry is a variable length heap-allocated structure. Entries 41 // are kept in a circular doubly linked list ordered by access time. 42 struct LRUHandle { 43 void* value; 44 void (*deleter)(const Slice&, void* value); 45 LRUHandle* next_hash; 46 LRUHandle* next; 47 LRUHandle* prev; 48 size_t charge; // TODO(opt): Only allow uint32_t? 49 size_t key_length; 50 bool in_cache; // Whether entry is in the cache. 51 uint32_t refs; // References, including cache reference, if present. 52 uint32_t hash; // Hash of key(); used for fast sharding and comparisons 53 char key_data[1]; // Beginning of key 54 55 Slice key() const { 56 // next_ is only equal to this if the LRU handle is the list head of an 57 // empty list. List heads never have meaningful keys. 58 assert(next != this); 59 60 return Slice(key_data, key_length); 61 } 62 }; 63 64 // We provide our own simple hash table since it removes a whole bunch 65 // of porting hacks and is also faster than some of the built-in hash 66 // table implementations in some of the compiler/runtime combinations 67 // we have tested. E.g., readrandom speeds up by ~5% over the g++ 68 // 4.4.3's builtin hashtable. 69 class HandleTable { 70 public: 71 HandleTable() : length_(0), elems_(0), list_(nullptr) { Resize(); } 72 ~HandleTable() { delete[] list_; } 73 74 LRUHandle* Lookup(const Slice& key, uint32_t hash) { 75 return *FindPointer(key, hash); 76 } 77 78 LRUHandle* Insert(LRUHandle* h) { 79 LRUHandle** ptr = FindPointer(h->key(), h->hash); 80 LRUHandle* old = *ptr; 81 h->next_hash = (old == nullptr ? nullptr : old->next_hash); 82 *ptr = h; 83 if (old == nullptr) { 84 ++elems_; 85 if (elems_ > length_) { 86 // Since each cache entry is fairly large, we aim for a small 87 // average linked list length (<= 1). 88 Resize(); 89 } 90 } 91 return old; 92 } 93 94 LRUHandle* Remove(const Slice& key, uint32_t hash) { 95 LRUHandle** ptr = FindPointer(key, hash); 96 LRUHandle* result = *ptr; 97 if (result != nullptr) { 98 *ptr = result->next_hash; 99 --elems_; 100 } 101 return result; 102 } 103 104 private: 105 // The table consists of an array of buckets where each bucket is 106 // a linked list of cache entries that hash into the bucket. 107 uint32_t length_; 108 uint32_t elems_; 109 LRUHandle** list_; 110 111 // Return a pointer to slot that points to a cache entry that 112 // matches key/hash. If there is no such cache entry, return a 113 // pointer to the trailing slot in the corresponding linked list. 114 LRUHandle** FindPointer(const Slice& key, uint32_t hash) { 115 LRUHandle** ptr = &list_[hash & (length_ - 1)]; 116 while (*ptr != nullptr && ((*ptr)->hash != hash || key != (*ptr)->key())) { 117 ptr = &(*ptr)->next_hash; 118 } 119 return ptr; 120 } 121 122 void Resize() { 123 uint32_t new_length = 4; 124 while (new_length < elems_) { 125 new_length *= 2; 126 } 127 LRUHandle** new_list = new LRUHandle*[new_length]; 128 memset(new_list, 0, sizeof(new_list[0]) * new_length); 129 uint32_t count = 0; 130 for (uint32_t i = 0; i < length_; i++) { 131 LRUHandle* h = list_[i]; 132 while (h != nullptr) { 133 LRUHandle* next = h->next_hash; 134 uint32_t hash = h->hash; 135 LRUHandle** ptr = &new_list[hash & (new_length - 1)]; 136 h->next_hash = *ptr; 137 *ptr = h; 138 h = next; 139 count++; 140 } 141 } 142 assert(elems_ == count); 143 delete[] list_; 144 list_ = new_list; 145 length_ = new_length; 146 } 147 }; 148 149 // A single shard of sharded cache. 150 class LRUCache { 151 public: 152 LRUCache(); 153 ~LRUCache(); 154 155 // Separate from constructor so caller can easily make an array of LRUCache 156 void SetCapacity(size_t capacity) { capacity_ = capacity; } 157 158 // Like Cache methods, but with an extra "hash" parameter. 159 Cache::Handle* Insert(const Slice& key, uint32_t hash, void* value, 160 size_t charge, 161 void (*deleter)(const Slice& key, void* value)); 162 Cache::Handle* Lookup(const Slice& key, uint32_t hash); 163 void Release(Cache::Handle* handle); 164 void Erase(const Slice& key, uint32_t hash); 165 void Prune(); 166 size_t TotalCharge() const { 167 MutexLock l(&mutex_); 168 return usage_; 169 } 170 171 private: 172 void LRU_Remove(LRUHandle* e); 173 void LRU_Append(LRUHandle* list, LRUHandle* e); 174 void Ref(LRUHandle* e) EXCLUSIVE_LOCKS_REQUIRED(mutex_); 175 void Unref(LRUHandle* e) EXCLUSIVE_LOCKS_REQUIRED(mutex_); 176 bool FinishErase(LRUHandle* e) EXCLUSIVE_LOCKS_REQUIRED(mutex_); 177 178 // Initialized before use. 179 size_t capacity_; 180 181 // mutex_ protects the following state. 182 mutable port::Mutex mutex_; 183 size_t usage_ GUARDED_BY(mutex_); 184 185 // Dummy head of LRU list. 186 // lru.prev is newest entry, lru.next is oldest entry. 187 // Entries have refs==1 and in_cache==true. 188 LRUHandle lru_ GUARDED_BY(mutex_); 189 190 // Dummy head of in-use list. 191 // Entries are in use by clients, and have refs >= 2 and in_cache==true. 192 LRUHandle in_use_ GUARDED_BY(mutex_); 193 194 HandleTable table_ GUARDED_BY(mutex_); 195 }; 196 197 LRUCache::LRUCache() : capacity_(0), usage_(0) { 198 // Make empty circular linked lists. 199 lru_.next = &lru_; 200 lru_.prev = &lru_; 201 in_use_.next = &in_use_; 202 in_use_.prev = &in_use_; 203 } 204 205 LRUCache::~LRUCache() { 206 assert(in_use_.next == &in_use_); // Error if caller has an unreleased handle 207 for (LRUHandle* e = lru_.next; e != &lru_;) { 208 LRUHandle* next = e->next; 209 assert(e->in_cache); 210 e->in_cache = false; 211 assert(e->refs == 1); // Invariant of lru_ list. 212 Unref(e); 213 e = next; 214 } 215 } 216 217 void LRUCache::Ref(LRUHandle* e) { 218 if (e->refs == 1 && e->in_cache) { // If on lru_ list, move to in_use_ list. 219 LRU_Remove(e); 220 LRU_Append(&in_use_, e); 221 } 222 e->refs++; 223 } 224 225 void LRUCache::Unref(LRUHandle* e) { 226 assert(e->refs > 0); 227 e->refs--; 228 if (e->refs == 0) { // Deallocate. 229 assert(!e->in_cache); 230 (*e->deleter)(e->key(), e->value); 231 free(e); 232 } else if (e->in_cache && e->refs == 1) { 233 // No longer in use; move to lru_ list. 234 LRU_Remove(e); 235 LRU_Append(&lru_, e); 236 } 237 } 238 239 void LRUCache::LRU_Remove(LRUHandle* e) { 240 e->next->prev = e->prev; 241 e->prev->next = e->next; 242 } 243 244 void LRUCache::LRU_Append(LRUHandle* list, LRUHandle* e) { 245 // Make "e" newest entry by inserting just before *list 246 e->next = list; 247 e->prev = list->prev; 248 e->prev->next = e; 249 e->next->prev = e; 250 } 251 252 Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) { 253 MutexLock l(&mutex_); 254 LRUHandle* e = table_.Lookup(key, hash); 255 if (e != nullptr) { 256 Ref(e); 257 } 258 return reinterpret_cast<Cache::Handle*>(e); 259 } 260 261 void LRUCache::Release(Cache::Handle* handle) { 262 MutexLock l(&mutex_); 263 Unref(reinterpret_cast<LRUHandle*>(handle)); 264 } 265 266 Cache::Handle* LRUCache::Insert(const Slice& key, uint32_t hash, void* value, 267 size_t charge, 268 void (*deleter)(const Slice& key, 269 void* value)) { 270 MutexLock l(&mutex_); 271 272 LRUHandle* e = 273 reinterpret_cast<LRUHandle*>(malloc(sizeof(LRUHandle) - 1 + key.size())); 274 e->value = value; 275 e->deleter = deleter; 276 e->charge = charge; 277 e->key_length = key.size(); 278 e->hash = hash; 279 e->in_cache = false; 280 e->refs = 1; // for the returned handle. 281 memcpy(e->key_data, key.data(), key.size()); 282 283 if (capacity_ > 0) { 284 e->refs++; // for the cache's reference. 285 e->in_cache = true; 286 LRU_Append(&in_use_, e); 287 usage_ += charge; 288 FinishErase(table_.Insert(e)); 289 } else { // don't cache. (capacity_==0 is supported and turns off caching.) 290 // next is read by key() in an assert, so it must be initialized 291 e->next = nullptr; 292 } 293 while (usage_ > capacity_ && lru_.next != &lru_) { 294 LRUHandle* old = lru_.next; 295 assert(old->refs == 1); 296 bool erased = FinishErase(table_.Remove(old->key(), old->hash)); 297 if (!erased) { // to avoid unused variable when compiled NDEBUG 298 assert(erased); 299 } 300 } 301 302 return reinterpret_cast<Cache::Handle*>(e); 303 } 304 305 // If e != nullptr, finish removing *e from the cache; it has already been 306 // removed from the hash table. Return whether e != nullptr. 307 bool LRUCache::FinishErase(LRUHandle* e) { 308 if (e != nullptr) { 309 assert(e->in_cache); 310 LRU_Remove(e); 311 e->in_cache = false; 312 usage_ -= e->charge; 313 Unref(e); 314 } 315 return e != nullptr; 316 } 317 318 void LRUCache::Erase(const Slice& key, uint32_t hash) { 319 MutexLock l(&mutex_); 320 FinishErase(table_.Remove(key, hash)); 321 } 322 323 void LRUCache::Prune() { 324 MutexLock l(&mutex_); 325 while (lru_.next != &lru_) { 326 LRUHandle* e = lru_.next; 327 assert(e->refs == 1); 328 bool erased = FinishErase(table_.Remove(e->key(), e->hash)); 329 if (!erased) { // to avoid unused variable when compiled NDEBUG 330 assert(erased); 331 } 332 } 333 } 334 335 static const int kNumShardBits = 4; 336 static const int kNumShards = 1 << kNumShardBits; 337 338 class ShardedLRUCache : public Cache { 339 private: 340 LRUCache shard_[kNumShards]; 341 port::Mutex id_mutex_; 342 uint64_t last_id_; 343 344 static inline uint32_t HashSlice(const Slice& s) { 345 return Hash(s.data(), s.size(), 0); 346 } 347 348 static uint32_t Shard(uint32_t hash) { return hash >> (32 - kNumShardBits); } 349 350 public: 351 explicit ShardedLRUCache(size_t capacity) : last_id_(0) { 352 const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards; 353 for (int s = 0; s < kNumShards; s++) { 354 shard_[s].SetCapacity(per_shard); 355 } 356 } 357 ~ShardedLRUCache() override {} 358 Handle* Insert(const Slice& key, void* value, size_t charge, 359 void (*deleter)(const Slice& key, void* value)) override { 360 const uint32_t hash = HashSlice(key); 361 return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter); 362 } 363 Handle* Lookup(const Slice& key) override { 364 const uint32_t hash = HashSlice(key); 365 return shard_[Shard(hash)].Lookup(key, hash); 366 } 367 void Release(Handle* handle) override { 368 LRUHandle* h = reinterpret_cast<LRUHandle*>(handle); 369 shard_[Shard(h->hash)].Release(handle); 370 } 371 void Erase(const Slice& key) override { 372 const uint32_t hash = HashSlice(key); 373 shard_[Shard(hash)].Erase(key, hash); 374 } 375 void* Value(Handle* handle) override { 376 return reinterpret_cast<LRUHandle*>(handle)->value; 377 } 378 uint64_t NewId() override { 379 MutexLock l(&id_mutex_); 380 return ++(last_id_); 381 } 382 void Prune() override { 383 for (int s = 0; s < kNumShards; s++) { 384 shard_[s].Prune(); 385 } 386 } 387 size_t TotalCharge() const override { 388 size_t total = 0; 389 for (int s = 0; s < kNumShards; s++) { 390 total += shard_[s].TotalCharge(); 391 } 392 return total; 393 } 394 }; 395 396 } // end anonymous namespace 397 398 Cache* NewLRUCache(size_t capacity) { return new ShardedLRUCache(capacity); } 399 400 } // namespace leveldb