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