作者: 谢瑞阳 10225101483 徐翔宇 10225101535
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

355 linhas
9.1 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. size_t TotalCharge() const {
  134. MutexLock l(&mutex_);
  135. return usage_;
  136. }
  137. private:
  138. void LRU_Remove(LRUHandle* e);
  139. void LRU_Append(LRUHandle* e);
  140. void Unref(LRUHandle* e);
  141. // Initialized before use.
  142. size_t capacity_;
  143. // mutex_ protects the following state.
  144. mutable port::Mutex mutex_;
  145. size_t usage_;
  146. // Dummy head of LRU list.
  147. // lru.prev is newest entry, lru.next is oldest entry.
  148. LRUHandle lru_;
  149. HandleTable table_;
  150. };
  151. LRUCache::LRUCache()
  152. : usage_(0) {
  153. // Make empty circular linked list
  154. lru_.next = &lru_;
  155. lru_.prev = &lru_;
  156. }
  157. LRUCache::~LRUCache() {
  158. for (LRUHandle* e = lru_.next; e != &lru_; ) {
  159. LRUHandle* next = e->next;
  160. assert(e->refs == 1); // Error if caller has an unreleased handle
  161. Unref(e);
  162. e = next;
  163. }
  164. }
  165. void LRUCache::Unref(LRUHandle* e) {
  166. assert(e->refs > 0);
  167. e->refs--;
  168. if (e->refs <= 0) {
  169. usage_ -= e->charge;
  170. (*e->deleter)(e->key(), e->value);
  171. free(e);
  172. }
  173. }
  174. void LRUCache::LRU_Remove(LRUHandle* e) {
  175. e->next->prev = e->prev;
  176. e->prev->next = e->next;
  177. }
  178. void LRUCache::LRU_Append(LRUHandle* e) {
  179. // Make "e" newest entry by inserting just before lru_
  180. e->next = &lru_;
  181. e->prev = lru_.prev;
  182. e->prev->next = e;
  183. e->next->prev = e;
  184. }
  185. Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) {
  186. MutexLock l(&mutex_);
  187. LRUHandle* e = table_.Lookup(key, hash);
  188. if (e != NULL) {
  189. e->refs++;
  190. LRU_Remove(e);
  191. LRU_Append(e);
  192. }
  193. return reinterpret_cast<Cache::Handle*>(e);
  194. }
  195. void LRUCache::Release(Cache::Handle* handle) {
  196. MutexLock l(&mutex_);
  197. Unref(reinterpret_cast<LRUHandle*>(handle));
  198. }
  199. Cache::Handle* LRUCache::Insert(
  200. const Slice& key, uint32_t hash, void* value, size_t charge,
  201. void (*deleter)(const Slice& key, void* value)) {
  202. MutexLock l(&mutex_);
  203. LRUHandle* e = reinterpret_cast<LRUHandle*>(
  204. malloc(sizeof(LRUHandle)-1 + key.size()));
  205. e->value = value;
  206. e->deleter = deleter;
  207. e->charge = charge;
  208. e->key_length = key.size();
  209. e->hash = hash;
  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->key(), old->hash);
  223. Unref(old);
  224. }
  225. return reinterpret_cast<Cache::Handle*>(e);
  226. }
  227. void LRUCache::Erase(const Slice& key, uint32_t hash) {
  228. MutexLock l(&mutex_);
  229. LRUHandle* e = table_.Remove(key, hash);
  230. if (e != NULL) {
  231. LRU_Remove(e);
  232. Unref(e);
  233. }
  234. }
  235. void LRUCache::Prune() {
  236. MutexLock l(&mutex_);
  237. for (LRUHandle* e = lru_.next; e != &lru_; ) {
  238. LRUHandle* next = e->next;
  239. if (e->refs == 1) {
  240. table_.Remove(e->key(), e->hash);
  241. LRU_Remove(e);
  242. Unref(e);
  243. }
  244. e = next;
  245. }
  246. }
  247. static const int kNumShardBits = 4;
  248. static const int kNumShards = 1 << kNumShardBits;
  249. class ShardedLRUCache : public Cache {
  250. private:
  251. LRUCache shard_[kNumShards];
  252. port::Mutex id_mutex_;
  253. uint64_t last_id_;
  254. static inline uint32_t HashSlice(const Slice& s) {
  255. return Hash(s.data(), s.size(), 0);
  256. }
  257. static uint32_t Shard(uint32_t hash) {
  258. return hash >> (32 - kNumShardBits);
  259. }
  260. public:
  261. explicit ShardedLRUCache(size_t capacity)
  262. : last_id_(0) {
  263. const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards;
  264. for (int s = 0; s < kNumShards; s++) {
  265. shard_[s].SetCapacity(per_shard);
  266. }
  267. }
  268. virtual ~ShardedLRUCache() { }
  269. virtual Handle* Insert(const Slice& key, void* value, size_t charge,
  270. void (*deleter)(const Slice& key, void* value)) {
  271. const uint32_t hash = HashSlice(key);
  272. return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter);
  273. }
  274. virtual Handle* Lookup(const Slice& key) {
  275. const uint32_t hash = HashSlice(key);
  276. return shard_[Shard(hash)].Lookup(key, hash);
  277. }
  278. virtual void Release(Handle* handle) {
  279. LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);
  280. shard_[Shard(h->hash)].Release(handle);
  281. }
  282. virtual void Erase(const Slice& key) {
  283. const uint32_t hash = HashSlice(key);
  284. shard_[Shard(hash)].Erase(key, hash);
  285. }
  286. virtual void* Value(Handle* handle) {
  287. return reinterpret_cast<LRUHandle*>(handle)->value;
  288. }
  289. virtual uint64_t NewId() {
  290. MutexLock l(&id_mutex_);
  291. return ++(last_id_);
  292. }
  293. virtual void Prune() {
  294. for (int s = 0; s < kNumShards; s++) {
  295. shard_[s].Prune();
  296. }
  297. }
  298. virtual size_t TotalCharge() const {
  299. size_t total = 0;
  300. for (int s = 0; s < kNumShards; s++) {
  301. total += shard_[s].TotalCharge();
  302. }
  303. return total;
  304. }
  305. };
  306. } // end anonymous namespace
  307. Cache* NewLRUCache(size_t capacity) {
  308. return new ShardedLRUCache(capacity);
  309. }
  310. } // namespace leveldb