小组成员: 曹可心-10223903406 朴祉燕-10224602413
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

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