作者: 谢瑞阳 10225101483 徐翔宇 10225101535
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

328 řádky
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. Slice key = h->key();
  105. uint32_t hash = h->hash;
  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. // A single shard of sharded cache.
  120. class LRUCache {
  121. public:
  122. LRUCache();
  123. ~LRUCache();
  124. // Separate from constructor so caller can easily make an array of LRUCache
  125. void SetCapacity(size_t capacity) { capacity_ = capacity; }
  126. // Like Cache methods, but with an extra "hash" parameter.
  127. Cache::Handle* Insert(const Slice& key, uint32_t hash,
  128. void* value, size_t charge,
  129. void (*deleter)(const Slice& key, void* value));
  130. Cache::Handle* Lookup(const Slice& key, uint32_t hash);
  131. void Release(Cache::Handle* handle);
  132. void Erase(const Slice& key, uint32_t hash);
  133. private:
  134. void LRU_Remove(LRUHandle* e);
  135. void LRU_Append(LRUHandle* e);
  136. void Unref(LRUHandle* e);
  137. // Initialized before use.
  138. size_t capacity_;
  139. // mutex_ protects the following state.
  140. port::Mutex mutex_;
  141. size_t usage_;
  142. uint64_t last_id_;
  143. // Dummy head of LRU list.
  144. // lru.prev is newest entry, lru.next is oldest entry.
  145. LRUHandle lru_;
  146. HandleTable table_;
  147. };
  148. LRUCache::LRUCache()
  149. : usage_(0),
  150. last_id_(0) {
  151. // Make empty circular linked list
  152. lru_.next = &lru_;
  153. lru_.prev = &lru_;
  154. }
  155. LRUCache::~LRUCache() {
  156. for (LRUHandle* e = lru_.next; e != &lru_; ) {
  157. LRUHandle* next = e->next;
  158. assert(e->refs == 1); // Error if caller has an unreleased handle
  159. Unref(e);
  160. e = next;
  161. }
  162. }
  163. void LRUCache::Unref(LRUHandle* e) {
  164. assert(e->refs > 0);
  165. e->refs--;
  166. if (e->refs <= 0) {
  167. usage_ -= e->charge;
  168. (*e->deleter)(e->key(), e->value);
  169. free(e);
  170. }
  171. }
  172. void LRUCache::LRU_Remove(LRUHandle* e) {
  173. e->next->prev = e->prev;
  174. e->prev->next = e->next;
  175. }
  176. void LRUCache::LRU_Append(LRUHandle* e) {
  177. // Make "e" newest entry by inserting just before lru_
  178. e->next = &lru_;
  179. e->prev = lru_.prev;
  180. e->prev->next = e;
  181. e->next->prev = e;
  182. }
  183. Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) {
  184. MutexLock l(&mutex_);
  185. LRUHandle* e = table_.Lookup(key, hash);
  186. if (e != NULL) {
  187. e->refs++;
  188. LRU_Remove(e);
  189. LRU_Append(e);
  190. }
  191. return reinterpret_cast<Cache::Handle*>(e);
  192. }
  193. void LRUCache::Release(Cache::Handle* handle) {
  194. MutexLock l(&mutex_);
  195. Unref(reinterpret_cast<LRUHandle*>(handle));
  196. }
  197. Cache::Handle* LRUCache::Insert(
  198. const Slice& key, uint32_t hash, void* value, size_t charge,
  199. void (*deleter)(const Slice& key, void* value)) {
  200. MutexLock l(&mutex_);
  201. LRUHandle* e = reinterpret_cast<LRUHandle*>(
  202. malloc(sizeof(LRUHandle)-1 + key.size()));
  203. e->value = value;
  204. e->deleter = deleter;
  205. e->charge = charge;
  206. e->key_length = key.size();
  207. e->hash = hash;
  208. e->refs = 2; // One from LRUCache, one for the returned handle
  209. memcpy(e->key_data, key.data(), key.size());
  210. LRU_Append(e);
  211. usage_ += charge;
  212. LRUHandle* old = table_.Insert(e);
  213. if (old != NULL) {
  214. LRU_Remove(old);
  215. Unref(old);
  216. }
  217. while (usage_ > capacity_ && lru_.next != &lru_) {
  218. LRUHandle* old = lru_.next;
  219. LRU_Remove(old);
  220. table_.Remove(old->key(), old->hash);
  221. Unref(old);
  222. }
  223. return reinterpret_cast<Cache::Handle*>(e);
  224. }
  225. void LRUCache::Erase(const Slice& key, uint32_t hash) {
  226. MutexLock l(&mutex_);
  227. LRUHandle* e = table_.Remove(key, hash);
  228. if (e != NULL) {
  229. LRU_Remove(e);
  230. Unref(e);
  231. }
  232. }
  233. static const int kNumShardBits = 4;
  234. static const int kNumShards = 1 << kNumShardBits;
  235. class ShardedLRUCache : public Cache {
  236. private:
  237. LRUCache shard_[kNumShards];
  238. port::Mutex id_mutex_;
  239. uint64_t last_id_;
  240. static inline uint32_t HashSlice(const Slice& s) {
  241. return Hash(s.data(), s.size(), 0);
  242. }
  243. static uint32_t Shard(uint32_t hash) {
  244. return hash >> (32 - kNumShardBits);
  245. }
  246. public:
  247. explicit ShardedLRUCache(size_t capacity)
  248. : last_id_(0) {
  249. const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards;
  250. for (int s = 0; s < kNumShards; s++) {
  251. shard_[s].SetCapacity(per_shard);
  252. }
  253. }
  254. virtual ~ShardedLRUCache() { }
  255. virtual Handle* Insert(const Slice& key, void* value, size_t charge,
  256. void (*deleter)(const Slice& key, void* value)) {
  257. const uint32_t hash = HashSlice(key);
  258. return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter);
  259. }
  260. virtual Handle* Lookup(const Slice& key) {
  261. const uint32_t hash = HashSlice(key);
  262. return shard_[Shard(hash)].Lookup(key, hash);
  263. }
  264. virtual void Release(Handle* handle) {
  265. LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);
  266. shard_[Shard(h->hash)].Release(handle);
  267. }
  268. virtual void Erase(const Slice& key) {
  269. const uint32_t hash = HashSlice(key);
  270. shard_[Shard(hash)].Erase(key, hash);
  271. }
  272. virtual void* Value(Handle* handle) {
  273. return reinterpret_cast<LRUHandle*>(handle)->value;
  274. }
  275. virtual uint64_t NewId() {
  276. MutexLock l(&id_mutex_);
  277. return ++(last_id_);
  278. }
  279. };
  280. } // end anonymous namespace
  281. Cache* NewLRUCache(size_t capacity) {
  282. return new ShardedLRUCache(capacity);
  283. }
  284. } // namespace leveldb