You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

288 line
7.0 KiB

  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. #include <assert.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include "leveldb/cache.h"
  8. #include "port/port.h"
  9. #include "util/hash.h"
  10. #include "util/mutexlock.h"
  11. namespace leveldb {
  12. Cache::~Cache() {
  13. }
  14. namespace {
  15. // LRU cache implementation
  16. // An entry is a variable length heap-allocated structure. Entries
  17. // are kept in a circular doubly linked list ordered by access time.
  18. struct LRUHandle {
  19. void* value;
  20. void (*deleter)(const Slice&, void* value);
  21. LRUHandle* next_hash;
  22. LRUHandle* next;
  23. LRUHandle* prev;
  24. size_t charge; // TODO(opt): Only allow uint32_t?
  25. size_t key_length;
  26. size_t refs; // TODO(opt): Pack with "key_length"?
  27. char key_data[1]; // Beginning of key
  28. Slice key() const {
  29. // For cheaper lookups, we allow a temporary Handle object
  30. // to store a pointer to a key in "value".
  31. if (next == this) {
  32. return *(reinterpret_cast<Slice*>(value));
  33. } else {
  34. return Slice(key_data, key_length);
  35. }
  36. }
  37. };
  38. // We provide our own simple hash table since it removes a whole bunch
  39. // of porting hacks and is also faster than some of the built-in hash
  40. // table implementations in some of the compiler/runtime combinations
  41. // we have tested. E.g., readrandom speeds up by ~5% over the g++
  42. // 4.4.3's builtin hashtable.
  43. class HandleTable {
  44. public:
  45. HandleTable() : length_(0), elems_(0), list_(NULL) { Resize(); }
  46. ~HandleTable() { delete[] list_; }
  47. LRUHandle* Lookup(LRUHandle* h) {
  48. return *FindPointer(h);
  49. }
  50. LRUHandle* Insert(LRUHandle* h) {
  51. LRUHandle** ptr = FindPointer(h);
  52. LRUHandle* old = *ptr;
  53. h->next_hash = (old == NULL ? NULL : old->next_hash);
  54. *ptr = h;
  55. if (old == NULL) {
  56. ++elems_;
  57. if (elems_ > length_) {
  58. // Since each cache entry is fairly large, we aim for a small
  59. // average linked list length (<= 1).
  60. Resize();
  61. }
  62. }
  63. return old;
  64. }
  65. LRUHandle* Remove(LRUHandle* h) {
  66. LRUHandle** ptr = FindPointer(h);
  67. LRUHandle* result = *ptr;
  68. if (result != NULL) {
  69. *ptr = result->next_hash;
  70. --elems_;
  71. }
  72. return result;
  73. }
  74. private:
  75. // The table consists of an array of buckets where each bucket is
  76. // a linked list of cache entries that hash into the bucket.
  77. uint32_t length_;
  78. uint32_t elems_;
  79. LRUHandle** list_;
  80. // Return a pointer to slot that points to a cache entry that
  81. // matches *h. If there is no such cache entry, return a pointer to
  82. // the trailing slot in the corresponding linked list.
  83. LRUHandle** FindPointer(LRUHandle* h) {
  84. Slice key = h->key();
  85. uint32_t hash = Hash(key.data(), key.size(), 0);
  86. LRUHandle** ptr = &list_[hash & (length_ - 1)];
  87. while (*ptr != NULL && key != (*ptr)->key()) {
  88. ptr = &(*ptr)->next_hash;
  89. }
  90. return ptr;
  91. }
  92. void Resize() {
  93. uint32_t new_length = 4;
  94. while (new_length < elems_) {
  95. new_length *= 2;
  96. }
  97. LRUHandle** new_list = new LRUHandle*[new_length];
  98. memset(new_list, 0, sizeof(new_list[0]) * new_length);
  99. uint32_t count = 0;
  100. for (int i = 0; i < length_; i++) {
  101. LRUHandle* h = list_[i];
  102. while (h != NULL) {
  103. LRUHandle* next = h->next_hash;
  104. Slice key = h->key();
  105. uint32_t hash = Hash(key.data(), key.size(), 0);
  106. LRUHandle** ptr = &new_list[hash & (new_length - 1)];
  107. h->next_hash = *ptr;
  108. *ptr = h;
  109. h = next;
  110. count++;
  111. }
  112. }
  113. assert(elems_ == count);
  114. delete[] list_;
  115. list_ = new_list;
  116. length_ = new_length;
  117. }
  118. };
  119. class LRUCache : public Cache {
  120. public:
  121. explicit LRUCache(size_t capacity);
  122. virtual ~LRUCache();
  123. virtual Handle* Insert(const Slice& key, void* value, size_t charge,
  124. void (*deleter)(const Slice& key, void* value));
  125. virtual Handle* Lookup(const Slice& key);
  126. virtual void Release(Handle* handle);
  127. virtual void* Value(Handle* handle);
  128. virtual void Erase(const Slice& key);
  129. virtual uint64_t NewId();
  130. private:
  131. void LRU_Remove(LRUHandle* e);
  132. void LRU_Append(LRUHandle* e);
  133. void Unref(LRUHandle* e);
  134. // Constructor parameters
  135. const size_t capacity_;
  136. // mutex_ protects the following state.
  137. port::Mutex mutex_;
  138. size_t usage_;
  139. uint64_t last_id_;
  140. // Dummy head of LRU list.
  141. // lru.prev is newest entry, lru.next is oldest entry.
  142. LRUHandle lru_;
  143. HandleTable table_;
  144. };
  145. LRUCache::LRUCache(size_t capacity)
  146. : capacity_(capacity),
  147. usage_(0),
  148. last_id_(0) {
  149. // Make empty circular linked list
  150. lru_.next = &lru_;
  151. lru_.prev = &lru_;
  152. }
  153. LRUCache::~LRUCache() {
  154. for (LRUHandle* e = lru_.next; e != &lru_; ) {
  155. LRUHandle* next = e->next;
  156. assert(e->refs == 1); // Error if caller has an unreleased handle
  157. Unref(e);
  158. e = next;
  159. }
  160. }
  161. void LRUCache::Unref(LRUHandle* e) {
  162. assert(e->refs > 0);
  163. e->refs--;
  164. if (e->refs <= 0) {
  165. usage_ -= e->charge;
  166. (*e->deleter)(e->key(), e->value);
  167. free(e);
  168. }
  169. }
  170. void LRUCache::LRU_Remove(LRUHandle* e) {
  171. e->next->prev = e->prev;
  172. e->prev->next = e->next;
  173. }
  174. void LRUCache::LRU_Append(LRUHandle* e) {
  175. // Make "e" newest entry by inserting just before lru_
  176. e->next = &lru_;
  177. e->prev = lru_.prev;
  178. e->prev->next = e;
  179. e->next->prev = e;
  180. }
  181. Cache::Handle* LRUCache::Lookup(const Slice& key) {
  182. MutexLock l(&mutex_);
  183. LRUHandle dummy;
  184. dummy.next = &dummy;
  185. dummy.value = const_cast<Slice*>(&key);
  186. LRUHandle* e = table_.Lookup(&dummy);
  187. if (e != NULL) {
  188. e->refs++;
  189. LRU_Remove(e);
  190. LRU_Append(e);
  191. }
  192. return reinterpret_cast<Handle*>(e);
  193. }
  194. void* LRUCache::Value(Handle* handle) {
  195. return reinterpret_cast<LRUHandle*>(handle)->value;
  196. }
  197. void LRUCache::Release(Handle* handle) {
  198. MutexLock l(&mutex_);
  199. Unref(reinterpret_cast<LRUHandle*>(handle));
  200. }
  201. Cache::Handle* LRUCache::Insert(const Slice& key, void* value, size_t charge,
  202. void (*deleter)(const Slice& key, void* value)) {
  203. MutexLock l(&mutex_);
  204. LRUHandle* e = reinterpret_cast<LRUHandle*>(
  205. malloc(sizeof(LRUHandle)-1 + key.size()));
  206. e->value = value;
  207. e->deleter = deleter;
  208. e->charge = charge;
  209. e->key_length = key.size();
  210. e->refs = 2; // One from LRUCache, one for the returned handle
  211. memcpy(e->key_data, key.data(), key.size());
  212. LRU_Append(e);
  213. usage_ += charge;
  214. LRUHandle* old = table_.Insert(e);
  215. if (old != NULL) {
  216. LRU_Remove(old);
  217. Unref(old);
  218. }
  219. while (usage_ > capacity_ && lru_.next != &lru_) {
  220. LRUHandle* old = lru_.next;
  221. LRU_Remove(old);
  222. table_.Remove(old);
  223. Unref(old);
  224. }
  225. return reinterpret_cast<Handle*>(e);
  226. }
  227. void LRUCache::Erase(const Slice& key) {
  228. MutexLock l(&mutex_);
  229. LRUHandle dummy;
  230. dummy.next = &dummy;
  231. dummy.value = const_cast<Slice*>(&key);
  232. LRUHandle* e = table_.Remove(&dummy);
  233. if (e != NULL) {
  234. LRU_Remove(e);
  235. Unref(e);
  236. }
  237. }
  238. uint64_t LRUCache::NewId() {
  239. MutexLock l(&mutex_);
  240. return ++(last_id_);
  241. }
  242. } // end anonymous namespace
  243. Cache* NewLRUCache(size_t capacity) {
  244. return new LRUCache(capacity);
  245. }
  246. }