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.
9 #include "leveldb/cache.h"
10 #include "port/port.h"
11 #include "util/hash.h"
12 #include "util/mutexlock.h"
21 // LRU cache implementation
23 // An entry is a variable length heap-allocated structure. Entries
24 // are kept in a circular doubly linked list ordered by access time.
27 void (*deleter)(const Slice&, void* value);
31 size_t charge; // TODO(opt): Only allow uint32_t?
34 uint32_t hash; // Hash of key(); used for fast sharding and comparisons
35 char key_data[1]; // Beginning of key
38 // For cheaper lookups, we allow a temporary Handle object
39 // to store a pointer to a key in "value".
41 return *(reinterpret_cast<Slice*>(value));
43 return Slice(key_data, key_length);
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.
55 HandleTable() : length_(0), elems_(0), list_(NULL) { Resize(); }
56 ~HandleTable() { delete[] list_; }
58 LRUHandle* Lookup(const Slice& key, uint32_t hash) {
59 return *FindPointer(key, hash);
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);
69 if (elems_ > length_) {
70 // Since each cache entry is fairly large, we aim for a small
71 // average linked list length (<= 1).
78 LRUHandle* Remove(const Slice& key, uint32_t hash) {
79 LRUHandle** ptr = FindPointer(key, hash);
80 LRUHandle* result = *ptr;
82 *ptr = result->next_hash;
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.
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 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;
108 uint32_t new_length = 4;
109 while (new_length < elems_) {
112 LRUHandle** new_list = new LRUHandle*[new_length];
113 memset(new_list, 0, sizeof(new_list[0]) * new_length);
115 for (uint32_t i = 0; i < length_; i++) {
116 LRUHandle* h = list_[i];
118 LRUHandle* next = h->next_hash;
119 Slice key = h->key();
120 uint32_t hash = h->hash;
121 LRUHandle** ptr = &new_list[hash & (new_length - 1)];
128 assert(elems_ == count);
131 length_ = new_length;
135 // A single shard of sharded cache.
141 // Separate from constructor so caller can easily make an array of LRUCache
142 void SetCapacity(size_t capacity) { capacity_ = capacity; }
144 // Like Cache methods, but with an extra "hash" parameter.
145 Cache::Handle* Insert(const Slice& key, uint32_t hash,
146 void* value, size_t charge,
147 void (*deleter)(const Slice& key, void* value));
148 Cache::Handle* Lookup(const Slice& key, uint32_t hash);
149 void Release(Cache::Handle* handle);
150 void Erase(const Slice& key, uint32_t hash);
153 void LRU_Remove(LRUHandle* e);
154 void LRU_Append(LRUHandle* e);
155 void Unref(LRUHandle* e);
157 // Initialized before use.
160 // mutex_ protects the following state.
165 // Dummy head of LRU list.
166 // lru.prev is newest entry, lru.next is oldest entry.
175 // Make empty circular linked list
180 LRUCache::~LRUCache() {
181 for (LRUHandle* e = lru_.next; e != &lru_; ) {
182 LRUHandle* next = e->next;
183 assert(e->refs == 1); // Error if caller has an unreleased handle
189 void LRUCache::Unref(LRUHandle* e) {
194 (*e->deleter)(e->key(), e->value);
199 void LRUCache::LRU_Remove(LRUHandle* e) {
200 e->next->prev = e->prev;
201 e->prev->next = e->next;
204 void LRUCache::LRU_Append(LRUHandle* e) {
205 // Make "e" newest entry by inserting just before lru_
212 Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) {
213 MutexLock l(&mutex_);
214 LRUHandle* e = table_.Lookup(key, hash);
220 return reinterpret_cast<Cache::Handle*>(e);
223 void LRUCache::Release(Cache::Handle* handle) {
224 MutexLock l(&mutex_);
225 Unref(reinterpret_cast<LRUHandle*>(handle));
228 Cache::Handle* LRUCache::Insert(
229 const Slice& key, uint32_t hash, void* value, size_t charge,
230 void (*deleter)(const Slice& key, void* value)) {
231 MutexLock l(&mutex_);
233 LRUHandle* e = reinterpret_cast<LRUHandle*>(
234 malloc(sizeof(LRUHandle)-1 + key.size()));
236 e->deleter = deleter;
238 e->key_length = key.size();
240 e->refs = 2; // One from LRUCache, one for the returned handle
241 memcpy(e->key_data, key.data(), key.size());
245 LRUHandle* old = table_.Insert(e);
251 while (usage_ > capacity_ && lru_.next != &lru_) {
252 LRUHandle* old = lru_.next;
254 table_.Remove(old->key(), old->hash);
258 return reinterpret_cast<Cache::Handle*>(e);
261 void LRUCache::Erase(const Slice& key, uint32_t hash) {
262 MutexLock l(&mutex_);
263 LRUHandle* e = table_.Remove(key, hash);
270 static const int kNumShardBits = 4;
271 static const int kNumShards = 1 << kNumShardBits;
273 class ShardedLRUCache : public Cache {
275 LRUCache shard_[kNumShards];
276 port::Mutex id_mutex_;
279 static inline uint32_t HashSlice(const Slice& s) {
280 return Hash(s.data(), s.size(), 0);
283 static uint32_t Shard(uint32_t hash) {
284 return hash >> (32 - kNumShardBits);
288 explicit ShardedLRUCache(size_t capacity)
290 const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards;
291 for (int s = 0; s < kNumShards; s++) {
292 shard_[s].SetCapacity(per_shard);
295 virtual ~ShardedLRUCache() { }
296 virtual Handle* Insert(const Slice& key, void* value, size_t charge,
297 void (*deleter)(const Slice& key, void* value)) {
298 const uint32_t hash = HashSlice(key);
299 return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter);
301 virtual Handle* Lookup(const Slice& key) {
302 const uint32_t hash = HashSlice(key);
303 return shard_[Shard(hash)].Lookup(key, hash);
305 virtual void Release(Handle* handle) {
306 LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);
307 shard_[Shard(h->hash)].Release(handle);
309 virtual void Erase(const Slice& key) {
310 const uint32_t hash = HashSlice(key);
311 shard_[Shard(hash)].Erase(key, hash);
313 virtual void* Value(Handle* handle) {
314 return reinterpret_cast<LRUHandle*>(handle)->value;
316 virtual uint64_t NewId() {
317 MutexLock l(&id_mutex_);
322 } // end anonymous namespace
324 Cache* NewLRUCache(size_t capacity) {
325 return new ShardedLRUCache(capacity);
328 } // namespace leveldb