cache.cc raw

   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
 401