lru_cache_test.cc 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #include "cache/lru_cache.h"
  6. #include <string>
  7. #include <vector>
  8. #include "port/port.h"
  9. #include "test_util/testharness.h"
  10. namespace ROCKSDB_NAMESPACE {
  11. class LRUCacheTest : public testing::Test {
  12. public:
  13. LRUCacheTest() {}
  14. ~LRUCacheTest() override { DeleteCache(); }
  15. void DeleteCache() {
  16. if (cache_ != nullptr) {
  17. cache_->~LRUCacheShard();
  18. port::cacheline_aligned_free(cache_);
  19. cache_ = nullptr;
  20. }
  21. }
  22. void NewCache(size_t capacity, double high_pri_pool_ratio = 0.0,
  23. bool use_adaptive_mutex = kDefaultToAdaptiveMutex) {
  24. DeleteCache();
  25. cache_ = reinterpret_cast<LRUCacheShard*>(
  26. port::cacheline_aligned_alloc(sizeof(LRUCacheShard)));
  27. new (cache_) LRUCacheShard(capacity, false /*strict_capcity_limit*/,
  28. high_pri_pool_ratio, use_adaptive_mutex,
  29. kDontChargeCacheMetadata);
  30. }
  31. void Insert(const std::string& key,
  32. Cache::Priority priority = Cache::Priority::LOW) {
  33. cache_->Insert(key, 0 /*hash*/, nullptr /*value*/, 1 /*charge*/,
  34. nullptr /*deleter*/, nullptr /*handle*/, priority);
  35. }
  36. void Insert(char key, Cache::Priority priority = Cache::Priority::LOW) {
  37. Insert(std::string(1, key), priority);
  38. }
  39. bool Lookup(const std::string& key) {
  40. auto handle = cache_->Lookup(key, 0 /*hash*/);
  41. if (handle) {
  42. cache_->Release(handle);
  43. return true;
  44. }
  45. return false;
  46. }
  47. bool Lookup(char key) { return Lookup(std::string(1, key)); }
  48. void Erase(const std::string& key) { cache_->Erase(key, 0 /*hash*/); }
  49. void ValidateLRUList(std::vector<std::string> keys,
  50. size_t num_high_pri_pool_keys = 0) {
  51. LRUHandle* lru;
  52. LRUHandle* lru_low_pri;
  53. cache_->TEST_GetLRUList(&lru, &lru_low_pri);
  54. LRUHandle* iter = lru;
  55. bool in_high_pri_pool = false;
  56. size_t high_pri_pool_keys = 0;
  57. if (iter == lru_low_pri) {
  58. in_high_pri_pool = true;
  59. }
  60. for (const auto& key : keys) {
  61. iter = iter->next;
  62. ASSERT_NE(lru, iter);
  63. ASSERT_EQ(key, iter->key().ToString());
  64. ASSERT_EQ(in_high_pri_pool, iter->InHighPriPool());
  65. if (in_high_pri_pool) {
  66. high_pri_pool_keys++;
  67. }
  68. if (iter == lru_low_pri) {
  69. ASSERT_FALSE(in_high_pri_pool);
  70. in_high_pri_pool = true;
  71. }
  72. }
  73. ASSERT_EQ(lru, iter->next);
  74. ASSERT_TRUE(in_high_pri_pool);
  75. ASSERT_EQ(num_high_pri_pool_keys, high_pri_pool_keys);
  76. }
  77. private:
  78. LRUCacheShard* cache_ = nullptr;
  79. };
  80. TEST_F(LRUCacheTest, BasicLRU) {
  81. NewCache(5);
  82. for (char ch = 'a'; ch <= 'e'; ch++) {
  83. Insert(ch);
  84. }
  85. ValidateLRUList({"a", "b", "c", "d", "e"});
  86. for (char ch = 'x'; ch <= 'z'; ch++) {
  87. Insert(ch);
  88. }
  89. ValidateLRUList({"d", "e", "x", "y", "z"});
  90. ASSERT_FALSE(Lookup("b"));
  91. ValidateLRUList({"d", "e", "x", "y", "z"});
  92. ASSERT_TRUE(Lookup("e"));
  93. ValidateLRUList({"d", "x", "y", "z", "e"});
  94. ASSERT_TRUE(Lookup("z"));
  95. ValidateLRUList({"d", "x", "y", "e", "z"});
  96. Erase("x");
  97. ValidateLRUList({"d", "y", "e", "z"});
  98. ASSERT_TRUE(Lookup("d"));
  99. ValidateLRUList({"y", "e", "z", "d"});
  100. Insert("u");
  101. ValidateLRUList({"y", "e", "z", "d", "u"});
  102. Insert("v");
  103. ValidateLRUList({"e", "z", "d", "u", "v"});
  104. }
  105. TEST_F(LRUCacheTest, MidpointInsertion) {
  106. // Allocate 2 cache entries to high-pri pool.
  107. NewCache(5, 0.45);
  108. Insert("a", Cache::Priority::LOW);
  109. Insert("b", Cache::Priority::LOW);
  110. Insert("c", Cache::Priority::LOW);
  111. Insert("x", Cache::Priority::HIGH);
  112. Insert("y", Cache::Priority::HIGH);
  113. ValidateLRUList({"a", "b", "c", "x", "y"}, 2);
  114. // Low-pri entries inserted to the tail of low-pri list (the midpoint).
  115. // After lookup, it will move to the tail of the full list.
  116. Insert("d", Cache::Priority::LOW);
  117. ValidateLRUList({"b", "c", "d", "x", "y"}, 2);
  118. ASSERT_TRUE(Lookup("d"));
  119. ValidateLRUList({"b", "c", "x", "y", "d"}, 2);
  120. // High-pri entries will be inserted to the tail of full list.
  121. Insert("z", Cache::Priority::HIGH);
  122. ValidateLRUList({"c", "x", "y", "d", "z"}, 2);
  123. }
  124. TEST_F(LRUCacheTest, EntriesWithPriority) {
  125. // Allocate 2 cache entries to high-pri pool.
  126. NewCache(5, 0.45);
  127. Insert("a", Cache::Priority::LOW);
  128. Insert("b", Cache::Priority::LOW);
  129. Insert("c", Cache::Priority::LOW);
  130. ValidateLRUList({"a", "b", "c"}, 0);
  131. // Low-pri entries can take high-pri pool capacity if available
  132. Insert("u", Cache::Priority::LOW);
  133. Insert("v", Cache::Priority::LOW);
  134. ValidateLRUList({"a", "b", "c", "u", "v"}, 0);
  135. Insert("X", Cache::Priority::HIGH);
  136. Insert("Y", Cache::Priority::HIGH);
  137. ValidateLRUList({"c", "u", "v", "X", "Y"}, 2);
  138. // High-pri entries can overflow to low-pri pool.
  139. Insert("Z", Cache::Priority::HIGH);
  140. ValidateLRUList({"u", "v", "X", "Y", "Z"}, 2);
  141. // Low-pri entries will be inserted to head of low-pri pool.
  142. Insert("a", Cache::Priority::LOW);
  143. ValidateLRUList({"v", "X", "a", "Y", "Z"}, 2);
  144. // Low-pri entries will be inserted to head of high-pri pool after lookup.
  145. ASSERT_TRUE(Lookup("v"));
  146. ValidateLRUList({"X", "a", "Y", "Z", "v"}, 2);
  147. // High-pri entries will be inserted to the head of the list after lookup.
  148. ASSERT_TRUE(Lookup("X"));
  149. ValidateLRUList({"a", "Y", "Z", "v", "X"}, 2);
  150. ASSERT_TRUE(Lookup("Z"));
  151. ValidateLRUList({"a", "Y", "v", "X", "Z"}, 2);
  152. Erase("Y");
  153. ValidateLRUList({"a", "v", "X", "Z"}, 2);
  154. Erase("X");
  155. ValidateLRUList({"a", "v", "Z"}, 1);
  156. Insert("d", Cache::Priority::LOW);
  157. Insert("e", Cache::Priority::LOW);
  158. ValidateLRUList({"a", "v", "d", "e", "Z"}, 1);
  159. Insert("f", Cache::Priority::LOW);
  160. Insert("g", Cache::Priority::LOW);
  161. ValidateLRUList({"d", "e", "f", "g", "Z"}, 1);
  162. ASSERT_TRUE(Lookup("d"));
  163. ValidateLRUList({"e", "f", "g", "Z", "d"}, 2);
  164. }
  165. } // namespace ROCKSDB_NAMESPACE
  166. int main(int argc, char** argv) {
  167. ::testing::InitGoogleTest(&argc, argv);
  168. return RUN_ALL_TESTS();
  169. }