hash_table_test.cc 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // Copyright (c) 2013, 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. //
  6. #include "utilities/persistent_cache/hash_table.h"
  7. #include <cstdlib>
  8. #include <iostream>
  9. #include <set>
  10. #include <string>
  11. #include "db/db_test_util.h"
  12. #include "memory/arena.h"
  13. #include "test_util/testharness.h"
  14. #include "util/random.h"
  15. #include "utilities/persistent_cache/hash_table_evictable.h"
  16. namespace ROCKSDB_NAMESPACE {
  17. struct HashTableTest : public testing::Test {
  18. ~HashTableTest() override { map_.Clear(&HashTableTest::ClearNode); }
  19. struct Node {
  20. Node() = default;
  21. explicit Node(const uint64_t key, const std::string& val = std::string())
  22. : key_(key), val_(val) {}
  23. uint64_t key_ = 0;
  24. std::string val_;
  25. };
  26. struct Equal {
  27. bool operator()(const Node& lhs, const Node& rhs) {
  28. return lhs.key_ == rhs.key_;
  29. }
  30. };
  31. struct Hash {
  32. uint64_t operator()(const Node& node) {
  33. return std::hash<uint64_t>()(node.key_);
  34. }
  35. };
  36. static void ClearNode(Node /*node*/) {}
  37. HashTable<Node, Hash, Equal> map_;
  38. };
  39. struct EvictableHashTableTest : public testing::Test {
  40. ~EvictableHashTableTest() override {
  41. map_.Clear(&EvictableHashTableTest::ClearNode);
  42. }
  43. struct Node : LRUElement<Node> {
  44. Node() = default;
  45. explicit Node(const uint64_t key, const std::string& val = std::string())
  46. : key_(key), val_(val) {}
  47. uint64_t key_ = 0;
  48. std::string val_;
  49. std::atomic<uint32_t> refs_{0};
  50. };
  51. struct Equal {
  52. bool operator()(const Node* lhs, const Node* rhs) {
  53. return lhs->key_ == rhs->key_;
  54. }
  55. };
  56. struct Hash {
  57. uint64_t operator()(const Node* node) {
  58. return std::hash<uint64_t>()(node->key_);
  59. }
  60. };
  61. static void ClearNode(Node* /*node*/) {}
  62. EvictableHashTable<Node, Hash, Equal> map_;
  63. };
  64. TEST_F(HashTableTest, TestInsert) {
  65. const uint64_t max_keys = 1024 * 1024;
  66. // insert
  67. for (uint64_t k = 0; k < max_keys; ++k) {
  68. map_.Insert(Node(k, std::string(1000, k % 255)));
  69. }
  70. // verify
  71. for (uint64_t k = 0; k < max_keys; ++k) {
  72. Node val;
  73. port::RWMutex* rlock = nullptr;
  74. assert(map_.Find(Node(k), &val, &rlock));
  75. rlock->ReadUnlock();
  76. assert(val.val_ == std::string(1000, k % 255));
  77. }
  78. }
  79. TEST_F(HashTableTest, TestErase) {
  80. const uint64_t max_keys = 1024 * 1024;
  81. // insert
  82. for (uint64_t k = 0; k < max_keys; ++k) {
  83. map_.Insert(Node(k, std::string(1000, k % 255)));
  84. }
  85. auto rand = Random64(time(nullptr));
  86. // erase a few keys randomly
  87. std::set<uint64_t> erased;
  88. for (int i = 0; i < 1024; ++i) {
  89. uint64_t k = rand.Next() % max_keys;
  90. if (erased.find(k) != erased.end()) {
  91. continue;
  92. }
  93. assert(map_.Erase(Node(k), /*ret=*/nullptr));
  94. erased.insert(k);
  95. }
  96. // verify
  97. for (uint64_t k = 0; k < max_keys; ++k) {
  98. Node val;
  99. port::RWMutex* rlock = nullptr;
  100. bool status = map_.Find(Node(k), &val, &rlock);
  101. if (erased.find(k) == erased.end()) {
  102. assert(status);
  103. rlock->ReadUnlock();
  104. assert(val.val_ == std::string(1000, k % 255));
  105. } else {
  106. assert(!status);
  107. }
  108. }
  109. }
  110. TEST_F(EvictableHashTableTest, TestEvict) {
  111. #ifndef __clang_analyzer__
  112. const uint64_t max_keys = 1024 * 1024;
  113. // insert
  114. for (uint64_t k = 0; k < max_keys; ++k) {
  115. map_.Insert(new Node(k, std::string(1000, k % 255)));
  116. }
  117. // verify
  118. for (uint64_t k = 0; k < max_keys; ++k) {
  119. Node* val = map_.Evict();
  120. // unfortunately we can't predict eviction value since it is from any one of
  121. // the lock stripe
  122. assert(val);
  123. assert(val->val_ == std::string(1000, val->key_ % 255));
  124. delete val;
  125. }
  126. #endif // __clang_analyzer__
  127. }
  128. } // namespace ROCKSDB_NAMESPACE
  129. int main(int argc, char** argv) {
  130. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  131. ::testing::InitGoogleTest(&argc, argv);
  132. return RUN_ALL_TESTS();
  133. }