LCOV - code coverage report
Current view: top level - src/leveldb/util - cache.cc (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 130 151 86.1 %
Date: 2015-10-12 22:39:14 Functions: 21 28 75.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "util/hash.h"
      12             : #include "util/mutexlock.h"
      13             : 
      14             : namespace leveldb {
      15             : 
      16         488 : Cache::~Cache() {
      17         488 : }
      18             : 
      19             : namespace {
      20             : 
      21             : // LRU cache implementation
      22             : 
      23             : // An entry is a variable length heap-allocated structure.  Entries
      24             : // are kept in a circular doubly linked list ordered by access time.
      25             : struct LRUHandle {
      26             :   void* value;
      27             :   void (*deleter)(const Slice&, void* value);
      28             :   LRUHandle* next_hash;
      29             :   LRUHandle* next;
      30             :   LRUHandle* prev;
      31             :   size_t charge;      // TODO(opt): Only allow uint32_t?
      32             :   size_t key_length;
      33             :   uint32_t refs;
      34             :   uint32_t hash;      // Hash of key(); used for fast sharding and comparisons
      35             :   char key_data[1];   // Beginning of key
      36             : 
      37             :   Slice key() const {
      38             :     // For cheaper lookups, we allow a temporary Handle object
      39             :     // to store a pointer to a key in "value".
      40       17283 :     if (next == this) {
      41           0 :       return *(reinterpret_cast<Slice*>(value));
      42             :     } else {
      43       17283 :       return Slice(key_data, key_length);
      44             :     }
      45             :   }
      46             : };
      47             : 
      48             : // We provide our own simple hash table since it removes a whole bunch
      49             : // of porting hacks and is also faster than some of the built-in hash
      50             : // table implementations in some of the compiler/runtime combinations
      51             : // we have tested.  E.g., readrandom speeds up by ~5% over the g++
      52             : // 4.4.3's builtin hashtable.
      53             : class HandleTable {
      54             :  public:
      55        7808 :   HandleTable() : length_(0), elems_(0), list_(NULL) { Resize(); }
      56        7808 :   ~HandleTable() { delete[] list_; }
      57             : 
      58             :   LRUHandle* Lookup(const Slice& key, uint32_t hash) {
      59       29524 :     return *FindPointer(key, hash);
      60             :   }
      61             : 
      62         160 :   LRUHandle* Insert(LRUHandle* h) {
      63         320 :     LRUHandle** ptr = FindPointer(h->key(), h->hash);
      64         160 :     LRUHandle* old = *ptr;
      65         160 :     h->next_hash = (old == NULL ? NULL : old->next_hash);
      66         160 :     *ptr = h;
      67         160 :     if (old == NULL) {
      68         160 :       ++elems_;
      69         160 :       if (elems_ > length_) {
      70             :         // Since each cache entry is fairly large, we aim for a small
      71             :         // average linked list length (<= 1).
      72           0 :         Resize();
      73             :       }
      74             :     }
      75         160 :     return old;
      76             :   }
      77             : 
      78          15 :   LRUHandle* Remove(const Slice& key, uint32_t hash) {
      79          15 :     LRUHandle** ptr = FindPointer(key, hash);
      80          15 :     LRUHandle* result = *ptr;
      81          15 :     if (result != NULL) {
      82          15 :       *ptr = result->next_hash;
      83          15 :       --elems_;
      84             :     }
      85          15 :     return result;
      86             :   }
      87             : 
      88             :  private:
      89             :   // The table consists of an array of buckets where each bucket is
      90             :   // a linked list of cache entries that hash into the bucket.
      91             :   uint32_t length_;
      92             :   uint32_t elems_;
      93             :   LRUHandle** list_;
      94             : 
      95             :   // Return a pointer to slot that points to a cache entry that
      96             :   // matches key/hash.  If there is no such cache entry, return a
      97             :   // pointer to the trailing slot in the corresponding linked list.
      98       29699 :   LRUHandle** FindPointer(const Slice& key, uint32_t hash) {
      99       29699 :     LRUHandle** ptr = &list_[hash & (length_ - 1)];
     100       76361 :     while (*ptr != NULL &&
     101       67852 :            ((*ptr)->hash != hash || key != (*ptr)->key())) {
     102           0 :       ptr = &(*ptr)->next_hash;
     103             :     }
     104       29699 :     return ptr;
     105             :   }
     106             : 
     107        7808 :   void Resize() {
     108        7808 :     uint32_t new_length = 4;
     109       15616 :     while (new_length < elems_) {
     110           0 :       new_length *= 2;
     111             :     }
     112        7808 :     LRUHandle** new_list = new LRUHandle*[new_length];
     113        7808 :     memset(new_list, 0, sizeof(new_list[0]) * new_length);
     114        7808 :     uint32_t count = 0;
     115        7808 :     for (uint32_t i = 0; i < length_; i++) {
     116           0 :       LRUHandle* h = list_[i];
     117           0 :       while (h != NULL) {
     118           0 :         LRUHandle* next = h->next_hash;
     119           0 :         uint32_t hash = h->hash;
     120           0 :         LRUHandle** ptr = &new_list[hash & (new_length - 1)];
     121           0 :         h->next_hash = *ptr;
     122           0 :         *ptr = h;
     123           0 :         h = next;
     124           0 :         count++;
     125             :       }
     126             :     }
     127        7808 :     assert(elems_ == count);
     128        7808 :     delete[] list_;
     129        7808 :     list_ = new_list;
     130        7808 :     length_ = new_length;
     131        7808 :   }
     132             : };
     133             : 
     134             : // A single shard of sharded cache.
     135             : class LRUCache {
     136             :  public:
     137             :   LRUCache();
     138             :   ~LRUCache();
     139             : 
     140             :   // Separate from constructor so caller can easily make an array of LRUCache
     141        7808 :   void SetCapacity(size_t capacity) { capacity_ = capacity; }
     142             : 
     143             :   // Like Cache methods, but with an extra "hash" parameter.
     144             :   Cache::Handle* Insert(const Slice& key, uint32_t hash,
     145             :                         void* value, size_t charge,
     146             :                         void (*deleter)(const Slice& key, void* value));
     147             :   Cache::Handle* Lookup(const Slice& key, uint32_t hash);
     148             :   void Release(Cache::Handle* handle);
     149             :   void Erase(const Slice& key, uint32_t hash);
     150             : 
     151             :  private:
     152             :   void LRU_Remove(LRUHandle* e);
     153             :   void LRU_Append(LRUHandle* e);
     154             :   void Unref(LRUHandle* e);
     155             : 
     156             :   // Initialized before use.
     157             :   size_t capacity_;
     158             : 
     159             :   // mutex_ protects the following state.
     160             :   port::Mutex mutex_;
     161             :   size_t usage_;
     162             : 
     163             :   // Dummy head of LRU list.
     164             :   // lru.prev is newest entry, lru.next is oldest entry.
     165             :   LRUHandle lru_;
     166             : 
     167             :   HandleTable table_;
     168             : };
     169             : 
     170        7808 : LRUCache::LRUCache()
     171        7808 :     : usage_(0) {
     172             :   // Make empty circular linked list
     173        7808 :   lru_.next = &lru_;
     174        7808 :   lru_.prev = &lru_;
     175        7808 : }
     176             : 
     177       23424 : LRUCache::~LRUCache() {
     178       15761 :   for (LRUHandle* e = lru_.next; e != &lru_; ) {
     179         145 :     LRUHandle* next = e->next;
     180         145 :     assert(e->refs == 1);  // Error if caller has an unreleased handle
     181         145 :     Unref(e);
     182         145 :     e = next;
     183             :   }
     184        7808 : }
     185             : 
     186       17268 : void LRUCache::Unref(LRUHandle* e) {
     187       17268 :   assert(e->refs > 0);
     188       17268 :   e->refs--;
     189       17268 :   if (e->refs <= 0) {
     190         160 :     usage_ -= e->charge;
     191         320 :     (*e->deleter)(e->key(), e->value);
     192         160 :     free(e);
     193             :   }
     194       17268 : }
     195             : 
     196           0 : void LRUCache::LRU_Remove(LRUHandle* e) {
     197       16963 :   e->next->prev = e->prev;
     198       16963 :   e->prev->next = e->next;
     199           0 : }
     200             : 
     201             : void LRUCache::LRU_Append(LRUHandle* e) {
     202             :   // Make "e" newest entry by inserting just before lru_
     203       17108 :   e->next = &lru_;
     204       17108 :   e->prev = lru_.prev;
     205       17108 :   e->prev->next = e;
     206       17108 :   e->next->prev = e;
     207             : }
     208             : 
     209       29524 : Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) {
     210       29524 :   MutexLock l(&mutex_);
     211       59048 :   LRUHandle* e = table_.Lookup(key, hash);
     212       29524 :   if (e != NULL) {
     213       16948 :     e->refs++;
     214       16948 :     LRU_Remove(e);
     215             :     LRU_Append(e);
     216             :   }
     217       59048 :   return reinterpret_cast<Cache::Handle*>(e);
     218             : }
     219             : 
     220       17108 : void LRUCache::Release(Cache::Handle* handle) {
     221       17108 :   MutexLock l(&mutex_);
     222       17108 :   Unref(reinterpret_cast<LRUHandle*>(handle));
     223       17108 : }
     224             : 
     225         160 : Cache::Handle* LRUCache::Insert(
     226             :     const Slice& key, uint32_t hash, void* value, size_t charge,
     227             :     void (*deleter)(const Slice& key, void* value)) {
     228         160 :   MutexLock l(&mutex_);
     229             : 
     230             :   LRUHandle* e = reinterpret_cast<LRUHandle*>(
     231         160 :       malloc(sizeof(LRUHandle)-1 + key.size()));
     232         160 :   e->value = value;
     233         160 :   e->deleter = deleter;
     234         160 :   e->charge = charge;
     235         160 :   e->key_length = key.size();
     236         160 :   e->hash = hash;
     237         160 :   e->refs = 2;  // One from LRUCache, one for the returned handle
     238         160 :   memcpy(e->key_data, key.data(), key.size());
     239             :   LRU_Append(e);
     240         160 :   usage_ += charge;
     241             : 
     242         160 :   LRUHandle* old = table_.Insert(e);
     243         160 :   if (old != NULL) {
     244           0 :     LRU_Remove(old);
     245           0 :     Unref(old);
     246             :   }
     247             : 
     248         160 :   while (usage_ > capacity_ && lru_.next != &lru_) {
     249           0 :     LRUHandle* old = lru_.next;
     250           0 :     LRU_Remove(old);
     251           0 :     table_.Remove(old->key(), old->hash);
     252           0 :     Unref(old);
     253             :   }
     254             : 
     255         320 :   return reinterpret_cast<Cache::Handle*>(e);
     256             : }
     257             : 
     258          15 : void LRUCache::Erase(const Slice& key, uint32_t hash) {
     259          15 :   MutexLock l(&mutex_);
     260          15 :   LRUHandle* e = table_.Remove(key, hash);
     261          15 :   if (e != NULL) {
     262          15 :     LRU_Remove(e);
     263          15 :     Unref(e);
     264             :   }
     265          15 : }
     266             : 
     267             : static const int kNumShardBits = 4;
     268             : static const int kNumShards = 1 << kNumShardBits;
     269             : 
     270             : class ShardedLRUCache : public Cache {
     271             :  private:
     272             :   LRUCache shard_[kNumShards];
     273             :   port::Mutex id_mutex_;
     274             :   uint64_t last_id_;
     275             : 
     276             :   static inline uint32_t HashSlice(const Slice& s) {
     277       29699 :     return Hash(s.data(), s.size(), 0);
     278             :   }
     279             : 
     280             :   static uint32_t Shard(uint32_t hash) {
     281       46807 :     return hash >> (32 - kNumShardBits);
     282             :   }
     283             : 
     284             :  public:
     285         488 :   explicit ShardedLRUCache(size_t capacity)
     286         976 :       : last_id_(0) {
     287         488 :     const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards;
     288        8296 :     for (int s = 0; s < kNumShards; s++) {
     289        7808 :       shard_[s].SetCapacity(per_shard);
     290             :     }
     291         488 :   }
     292        1464 :   virtual ~ShardedLRUCache() { }
     293         160 :   virtual Handle* Insert(const Slice& key, void* value, size_t charge,
     294             :                          void (*deleter)(const Slice& key, void* value)) {
     295         160 :     const uint32_t hash = HashSlice(key);
     296         160 :     return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter);
     297             :   }
     298       29524 :   virtual Handle* Lookup(const Slice& key) {
     299       29524 :     const uint32_t hash = HashSlice(key);
     300       29524 :     return shard_[Shard(hash)].Lookup(key, hash);
     301             :   }
     302       17108 :   virtual void Release(Handle* handle) {
     303       17108 :     LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);
     304       34216 :     shard_[Shard(h->hash)].Release(handle);
     305       17108 :   }
     306          15 :   virtual void Erase(const Slice& key) {
     307          15 :     const uint32_t hash = HashSlice(key);
     308          15 :     shard_[Shard(hash)].Erase(key, hash);
     309          15 :   }
     310       17108 :   virtual void* Value(Handle* handle) {
     311       17108 :     return reinterpret_cast<LRUHandle*>(handle)->value;
     312             :   }
     313         160 :   virtual uint64_t NewId() {
     314         160 :     MutexLock l(&id_mutex_);
     315         320 :     return ++(last_id_);
     316             :   }
     317             : };
     318             : 
     319             : }  // end anonymous namespace
     320             : 
     321         488 : Cache* NewLRUCache(size_t capacity) {
     322         488 :   return new ShardedLRUCache(capacity);
     323             : }
     324             : 
     325             : }  // namespace leveldb

Generated by: LCOV version 1.11