小组成员:谢瑞阳、徐翔宇
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.

344 lines
8.8 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. void Prune();
  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. // Dummy head of LRU list.
  143. // lru.prev is newest entry, lru.next is oldest entry.
  144. LRUHandle lru_;
  145. HandleTable table_;
  146. };
  147. LRUCache::LRUCache()
  148. : usage_(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, uint32_t hash) {
  182. MutexLock l(&mutex_);
  183. LRUHandle* e = table_.Lookup(key, hash);
  184. if (e != NULL) {
  185. e->refs++;
  186. LRU_Remove(e);
  187. LRU_Append(e);
  188. }
  189. return reinterpret_cast<Cache::Handle*>(e);
  190. }
  191. void LRUCache::Release(Cache::Handle* handle) {
  192. MutexLock l(&mutex_);
  193. Unref(reinterpret_cast<LRUHandle*>(handle));
  194. }
  195. Cache::Handle* LRUCache::Insert(
  196. const Slice& key, uint32_t hash, void* value, size_t charge,
  197. void (*deleter)(const Slice& key, void* value)) {
  198. MutexLock l(&mutex_);
  199. LRUHandle* e = reinterpret_cast<LRUHandle*>(
  200. malloc(sizeof(LRUHandle)-1 + key.size()));
  201. e->value = value;
  202. e->deleter = deleter;
  203. e->charge = charge;
  204. e->key_length = key.size();
  205. e->hash = hash;
  206. e->refs = 2; // One from LRUCache, one for the returned handle
  207. memcpy(e->key_data, key.data(), key.size());
  208. LRU_Append(e);
  209. usage_ += charge;
  210. LRUHandle* old = table_.Insert(e);
  211. if (old != NULL) {
  212. LRU_Remove(old);
  213. Unref(old);
  214. }
  215. while (usage_ > capacity_ && lru_.next != &lru_) {
  216. LRUHandle* old = lru_.next;
  217. LRU_Remove(old);
  218. table_.Remove(old->key(), old->hash);
  219. Unref(old);
  220. }
  221. return reinterpret_cast<Cache::Handle*>(e);
  222. }
  223. void LRUCache::Erase(const Slice& key, uint32_t hash) {
  224. MutexLock l(&mutex_);
  225. LRUHandle* e = table_.Remove(key, hash);
  226. if (e != NULL) {
  227. LRU_Remove(e);
  228. Unref(e);
  229. }
  230. }
  231. void LRUCache::Prune() {
  232. MutexLock l(&mutex_);
  233. for (LRUHandle* e = lru_.next; e != &lru_; ) {
  234. LRUHandle* next = e->next;
  235. if (e->refs == 1) {
  236. table_.Remove(e->key(), e->hash);
  237. LRU_Remove(e);
  238. Unref(e);
  239. }
  240. e = next;
  241. }
  242. }
  243. static const int kNumShardBits = 4;
  244. static const int kNumShards = 1 << kNumShardBits;
  245. class ShardedLRUCache : public Cache {
  246. private:
  247. LRUCache shard_[kNumShards];
  248. port::Mutex id_mutex_;
  249. uint64_t last_id_;
  250. static inline uint32_t HashSlice(const Slice& s) {
  251. return Hash(s.data(), s.size(), 0);
  252. }
  253. static uint32_t Shard(uint32_t hash) {
  254. return hash >> (32 - kNumShardBits);
  255. }
  256. public:
  257. explicit ShardedLRUCache(size_t capacity)
  258. : last_id_(0) {
  259. const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards;
  260. for (int s = 0; s < kNumShards; s++) {
  261. shard_[s].SetCapacity(per_shard);
  262. }
  263. }
  264. virtual ~ShardedLRUCache() { }
  265. virtual Handle* Insert(const Slice& key, void* value, size_t charge,
  266. void (*deleter)(const Slice& key, void* value)) {
  267. const uint32_t hash = HashSlice(key);
  268. return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter);
  269. }
  270. virtual Handle* Lookup(const Slice& key) {
  271. const uint32_t hash = HashSlice(key);
  272. return shard_[Shard(hash)].Lookup(key, hash);
  273. }
  274. virtual void Release(Handle* handle) {
  275. LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);
  276. shard_[Shard(h->hash)].Release(handle);
  277. }
  278. virtual void Erase(const Slice& key) {
  279. const uint32_t hash = HashSlice(key);
  280. shard_[Shard(hash)].Erase(key, hash);
  281. }
  282. virtual void* Value(Handle* handle) {
  283. return reinterpret_cast<LRUHandle*>(handle)->value;
  284. }
  285. virtual uint64_t NewId() {
  286. MutexLock l(&id_mutex_);
  287. return ++(last_id_);
  288. }
  289. virtual void Prune() {
  290. for (int s = 0; s < kNumShards; s++) {
  291. shard_[s].Prune();
  292. }
  293. }
  294. };
  295. } // end anonymous namespace
  296. Cache* NewLRUCache(size_t capacity) {
  297. return new ShardedLRUCache(capacity);
  298. }
  299. } // namespace leveldb