• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
~Cache()16 Cache::~Cache() {
17 }
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 
keyleveldb::__anon26392f960111::LRUHandle37   Slice key() const {
38     // For cheaper lookups, we allow a temporary Handle object
39     // to store a pointer to a key in "value".
40     if (next == this) {
41       return *(reinterpret_cast<Slice*>(value));
42     } else {
43       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:
HandleTable()55   HandleTable() : length_(0), elems_(0), list_(NULL) { Resize(); }
~HandleTable()56   ~HandleTable() { delete[] list_; }
57 
Lookup(const Slice & key,uint32_t hash)58   LRUHandle* Lookup(const Slice& key, uint32_t hash) {
59     return *FindPointer(key, hash);
60   }
61 
Insert(LRUHandle * h)62   LRUHandle* Insert(LRUHandle* h) {
63     LRUHandle** ptr = FindPointer(h->key(), h->hash);
64     LRUHandle* old = *ptr;
65     h->next_hash = (old == NULL ? NULL : old->next_hash);
66     *ptr = h;
67     if (old == NULL) {
68       ++elems_;
69       if (elems_ > length_) {
70         // Since each cache entry is fairly large, we aim for a small
71         // average linked list length (<= 1).
72         Resize();
73       }
74     }
75     return old;
76   }
77 
Remove(const Slice & key,uint32_t hash)78   LRUHandle* Remove(const Slice& key, uint32_t hash) {
79     LRUHandle** ptr = FindPointer(key, hash);
80     LRUHandle* result = *ptr;
81     if (result != NULL) {
82       *ptr = result->next_hash;
83       --elems_;
84     }
85     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.
FindPointer(const Slice & key,uint32_t hash)98   LRUHandle** FindPointer(const Slice& key, uint32_t hash) {
99     LRUHandle** ptr = &list_[hash & (length_ - 1)];
100     while (*ptr != NULL &&
101            ((*ptr)->hash != hash || key != (*ptr)->key())) {
102       ptr = &(*ptr)->next_hash;
103     }
104     return ptr;
105   }
106 
Resize()107   void Resize() {
108     uint32_t new_length = 4;
109     while (new_length < elems_) {
110       new_length *= 2;
111     }
112     LRUHandle** new_list = new LRUHandle*[new_length];
113     memset(new_list, 0, sizeof(new_list[0]) * new_length);
114     uint32_t count = 0;
115     for (uint32_t i = 0; i < length_; i++) {
116       LRUHandle* h = list_[i];
117       while (h != NULL) {
118         LRUHandle* next = h->next_hash;
119         uint32_t hash = h->hash;
120         LRUHandle** ptr = &new_list[hash & (new_length - 1)];
121         h->next_hash = *ptr;
122         *ptr = h;
123         h = next;
124         count++;
125       }
126     }
127     assert(elems_ == count);
128     delete[] list_;
129     list_ = new_list;
130     length_ = new_length;
131   }
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
SetCapacity(size_t capacity)141   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 
LRUCache()170 LRUCache::LRUCache()
171     : usage_(0) {
172   // Make empty circular linked list
173   lru_.next = &lru_;
174   lru_.prev = &lru_;
175 }
176 
~LRUCache()177 LRUCache::~LRUCache() {
178   for (LRUHandle* e = lru_.next; e != &lru_; ) {
179     LRUHandle* next = e->next;
180     assert(e->refs == 1);  // Error if caller has an unreleased handle
181     Unref(e);
182     e = next;
183   }
184 }
185 
Unref(LRUHandle * e)186 void LRUCache::Unref(LRUHandle* e) {
187   assert(e->refs > 0);
188   e->refs--;
189   if (e->refs <= 0) {
190     usage_ -= e->charge;
191     (*e->deleter)(e->key(), e->value);
192     free(e);
193   }
194 }
195 
LRU_Remove(LRUHandle * e)196 void LRUCache::LRU_Remove(LRUHandle* e) {
197   e->next->prev = e->prev;
198   e->prev->next = e->next;
199 }
200 
LRU_Append(LRUHandle * e)201 void LRUCache::LRU_Append(LRUHandle* e) {
202   // Make "e" newest entry by inserting just before lru_
203   e->next = &lru_;
204   e->prev = lru_.prev;
205   e->prev->next = e;
206   e->next->prev = e;
207 }
208 
Lookup(const Slice & key,uint32_t hash)209 Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) {
210   MutexLock l(&mutex_);
211   LRUHandle* e = table_.Lookup(key, hash);
212   if (e != NULL) {
213     e->refs++;
214     LRU_Remove(e);
215     LRU_Append(e);
216   }
217   return reinterpret_cast<Cache::Handle*>(e);
218 }
219 
Release(Cache::Handle * handle)220 void LRUCache::Release(Cache::Handle* handle) {
221   MutexLock l(&mutex_);
222   Unref(reinterpret_cast<LRUHandle*>(handle));
223 }
224 
Insert(const Slice & key,uint32_t hash,void * value,size_t charge,void (* deleter)(const Slice & key,void * value))225 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   MutexLock l(&mutex_);
229 
230   LRUHandle* e = reinterpret_cast<LRUHandle*>(
231       malloc(sizeof(LRUHandle)-1 + key.size()));
232   e->value = value;
233   e->deleter = deleter;
234   e->charge = charge;
235   e->key_length = key.size();
236   e->hash = hash;
237   e->refs = 2;  // One from LRUCache, one for the returned handle
238   memcpy(e->key_data, key.data(), key.size());
239   LRU_Append(e);
240   usage_ += charge;
241 
242   LRUHandle* old = table_.Insert(e);
243   if (old != NULL) {
244     LRU_Remove(old);
245     Unref(old);
246   }
247 
248   while (usage_ > capacity_ && lru_.next != &lru_) {
249     LRUHandle* old = lru_.next;
250     LRU_Remove(old);
251     table_.Remove(old->key(), old->hash);
252     Unref(old);
253   }
254 
255   return reinterpret_cast<Cache::Handle*>(e);
256 }
257 
Erase(const Slice & key,uint32_t hash)258 void LRUCache::Erase(const Slice& key, uint32_t hash) {
259   MutexLock l(&mutex_);
260   LRUHandle* e = table_.Remove(key, hash);
261   if (e != NULL) {
262     LRU_Remove(e);
263     Unref(e);
264   }
265 }
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 
HashSlice(const Slice & s)276   static inline uint32_t HashSlice(const Slice& s) {
277     return Hash(s.data(), s.size(), 0);
278   }
279 
Shard(uint32_t hash)280   static uint32_t Shard(uint32_t hash) {
281     return hash >> (32 - kNumShardBits);
282   }
283 
284  public:
ShardedLRUCache(size_t capacity)285   explicit ShardedLRUCache(size_t capacity)
286       : last_id_(0) {
287     const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards;
288     for (int s = 0; s < kNumShards; s++) {
289       shard_[s].SetCapacity(per_shard);
290     }
291   }
~ShardedLRUCache()292   virtual ~ShardedLRUCache() { }
Insert(const Slice & key,void * value,size_t charge,void (* deleter)(const Slice & key,void * value))293   virtual Handle* Insert(const Slice& key, void* value, size_t charge,
294                          void (*deleter)(const Slice& key, void* value)) {
295     const uint32_t hash = HashSlice(key);
296     return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter);
297   }
Lookup(const Slice & key)298   virtual Handle* Lookup(const Slice& key) {
299     const uint32_t hash = HashSlice(key);
300     return shard_[Shard(hash)].Lookup(key, hash);
301   }
Release(Handle * handle)302   virtual void Release(Handle* handle) {
303     LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);
304     shard_[Shard(h->hash)].Release(handle);
305   }
Erase(const Slice & key)306   virtual void Erase(const Slice& key) {
307     const uint32_t hash = HashSlice(key);
308     shard_[Shard(hash)].Erase(key, hash);
309   }
Value(Handle * handle)310   virtual void* Value(Handle* handle) {
311     return reinterpret_cast<LRUHandle*>(handle)->value;
312   }
NewId()313   virtual uint64_t NewId() {
314     MutexLock l(&id_mutex_);
315     return ++(last_id_);
316   }
317 };
318 
319 }  // end anonymous namespace
320 
NewLRUCache(size_t capacity)321 Cache* NewLRUCache(size_t capacity) {
322   return new ShardedLRUCache(capacity);
323 }
324 
325 }  // namespace leveldb
326