cache_simulator.h 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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. #pragma once
  6. #include <unordered_map>
  7. #include "cache/lru_cache.h"
  8. #include "trace_replay/block_cache_tracer.h"
  9. namespace ROCKSDB_NAMESPACE {
  10. // A cache configuration provided by user.
  11. struct CacheConfiguration {
  12. std::string cache_name; // LRU.
  13. uint32_t num_shard_bits;
  14. uint64_t ghost_cache_capacity; // ghost cache capacity in bytes.
  15. std::vector<uint64_t>
  16. cache_capacities; // simulate cache capacities in bytes.
  17. bool operator==(const CacheConfiguration& o) const {
  18. return cache_name == o.cache_name && num_shard_bits == o.num_shard_bits &&
  19. ghost_cache_capacity == o.ghost_cache_capacity;
  20. }
  21. bool operator<(const CacheConfiguration& o) const {
  22. return cache_name < o.cache_name ||
  23. (cache_name == o.cache_name && num_shard_bits < o.num_shard_bits) ||
  24. (cache_name == o.cache_name && num_shard_bits == o.num_shard_bits &&
  25. ghost_cache_capacity < o.ghost_cache_capacity);
  26. }
  27. };
  28. class MissRatioStats {
  29. public:
  30. void reset_counter() {
  31. num_misses_ = 0;
  32. num_accesses_ = 0;
  33. user_accesses_ = 0;
  34. user_misses_ = 0;
  35. }
  36. double miss_ratio() const {
  37. if (num_accesses_ == 0) {
  38. return -1;
  39. }
  40. return static_cast<double>(num_misses_ * 100.0 / num_accesses_);
  41. }
  42. uint64_t total_accesses() const { return num_accesses_; }
  43. uint64_t total_misses() const { return num_misses_; }
  44. const std::map<uint64_t, uint64_t>& num_accesses_timeline() const {
  45. return num_accesses_timeline_;
  46. }
  47. const std::map<uint64_t, uint64_t>& num_misses_timeline() const {
  48. return num_misses_timeline_;
  49. }
  50. double user_miss_ratio() const {
  51. if (user_accesses_ == 0) {
  52. return -1;
  53. }
  54. return static_cast<double>(user_misses_ * 100.0 / user_accesses_);
  55. }
  56. uint64_t user_accesses() const { return user_accesses_; }
  57. uint64_t user_misses() const { return user_misses_; }
  58. void UpdateMetrics(uint64_t timestamp_in_ms, bool is_user_access,
  59. bool is_cache_miss);
  60. private:
  61. uint64_t num_accesses_ = 0;
  62. uint64_t num_misses_ = 0;
  63. uint64_t user_accesses_ = 0;
  64. uint64_t user_misses_ = 0;
  65. std::map<uint64_t, uint64_t> num_accesses_timeline_;
  66. std::map<uint64_t, uint64_t> num_misses_timeline_;
  67. };
  68. // A ghost cache admits an entry on its second access.
  69. class GhostCache {
  70. public:
  71. explicit GhostCache(std::shared_ptr<Cache> sim_cache);
  72. ~GhostCache() = default;
  73. // No copy and move.
  74. GhostCache(const GhostCache&) = delete;
  75. GhostCache& operator=(const GhostCache&) = delete;
  76. GhostCache(GhostCache&&) = delete;
  77. GhostCache& operator=(GhostCache&&) = delete;
  78. // Returns true if the lookup_key is in the ghost cache.
  79. // Returns false otherwise.
  80. bool Admit(const Slice& lookup_key);
  81. private:
  82. std::shared_ptr<Cache> sim_cache_;
  83. };
  84. // A cache simulator that runs against a block cache trace.
  85. class CacheSimulator {
  86. public:
  87. CacheSimulator(std::unique_ptr<GhostCache>&& ghost_cache,
  88. std::shared_ptr<Cache> sim_cache);
  89. virtual ~CacheSimulator() = default;
  90. // No copy and move.
  91. CacheSimulator(const CacheSimulator&) = delete;
  92. CacheSimulator& operator=(const CacheSimulator&) = delete;
  93. CacheSimulator(CacheSimulator&&) = delete;
  94. CacheSimulator& operator=(CacheSimulator&&) = delete;
  95. virtual void Access(const BlockCacheTraceRecord& access);
  96. void reset_counter() { miss_ratio_stats_.reset_counter(); }
  97. const MissRatioStats& miss_ratio_stats() const { return miss_ratio_stats_; }
  98. protected:
  99. MissRatioStats miss_ratio_stats_;
  100. std::unique_ptr<GhostCache> ghost_cache_;
  101. std::shared_ptr<Cache> sim_cache_;
  102. };
  103. // A prioritized cache simulator that runs against a block cache trace.
  104. // It inserts missing index/filter/uncompression-dictionary blocks with high
  105. // priority in the cache.
  106. class PrioritizedCacheSimulator : public CacheSimulator {
  107. public:
  108. PrioritizedCacheSimulator(std::unique_ptr<GhostCache>&& ghost_cache,
  109. std::shared_ptr<Cache> sim_cache)
  110. : CacheSimulator(std::move(ghost_cache), sim_cache) {}
  111. void Access(const BlockCacheTraceRecord& access) override;
  112. protected:
  113. // Access the key-value pair and returns true upon a cache miss.
  114. void AccessKVPair(const Slice& key, uint64_t value_size,
  115. Cache::Priority priority,
  116. const BlockCacheTraceRecord& access, bool no_insert,
  117. bool is_user_access, bool* is_cache_miss, bool* admitted,
  118. bool update_metrics);
  119. Cache::Priority ComputeBlockPriority(
  120. const BlockCacheTraceRecord& access) const;
  121. };
  122. // A hybrid row and block cache simulator. It looks up/inserts key-value pairs
  123. // referenced by Get/MultiGet requests, and not their accessed index/filter/data
  124. // blocks.
  125. //
  126. // Upon a Get/MultiGet request, it looks up the referenced key first.
  127. // If it observes a cache hit, future block accesses on this key-value pair is
  128. // skipped since the request is served already. Otherwise, it continues to look
  129. // up/insert its index/filter/data blocks. It also inserts the referenced
  130. // key-value pair in the cache for future lookups.
  131. class HybridRowBlockCacheSimulator : public PrioritizedCacheSimulator {
  132. public:
  133. HybridRowBlockCacheSimulator(std::unique_ptr<GhostCache>&& ghost_cache,
  134. std::shared_ptr<Cache> sim_cache,
  135. bool insert_blocks_upon_row_kvpair_miss)
  136. : PrioritizedCacheSimulator(std::move(ghost_cache), sim_cache),
  137. insert_blocks_upon_row_kvpair_miss_(
  138. insert_blocks_upon_row_kvpair_miss) {}
  139. void Access(const BlockCacheTraceRecord& access) override;
  140. private:
  141. enum InsertResult : char {
  142. INSERTED,
  143. ADMITTED,
  144. NO_INSERT,
  145. };
  146. // We set is_complete to true when the referenced row-key of a get request
  147. // hits the cache. If is_complete is true, we treat future accesses of this
  148. // get request as hits.
  149. //
  150. // For each row key, it stores an enum. It is INSERTED when the
  151. // kv-pair has been inserted into the cache, ADMITTED if it should be inserted
  152. // but haven't been, NO_INSERT if it should not be inserted.
  153. //
  154. // A kv-pair is in ADMITTED state when we encounter this kv-pair but do not
  155. // know its size. This may happen if the first access on the referenced key is
  156. // an index/filter block.
  157. struct GetRequestStatus {
  158. bool is_complete = false;
  159. std::map<std::string, InsertResult> row_key_status;
  160. };
  161. // A map stores get_id to a map of row keys.
  162. std::map<uint64_t, GetRequestStatus> getid_status_map_;
  163. bool insert_blocks_upon_row_kvpair_miss_;
  164. };
  165. // A block cache simulator that reports miss ratio curves given a set of cache
  166. // configurations.
  167. class BlockCacheTraceSimulator {
  168. public:
  169. // warmup_seconds: The number of seconds to warmup simulated caches. The
  170. // hit/miss counters are reset after the warmup completes.
  171. BlockCacheTraceSimulator(
  172. uint64_t warmup_seconds, uint32_t downsample_ratio,
  173. const std::vector<CacheConfiguration>& cache_configurations);
  174. ~BlockCacheTraceSimulator() = default;
  175. // No copy and move.
  176. BlockCacheTraceSimulator(const BlockCacheTraceSimulator&) = delete;
  177. BlockCacheTraceSimulator& operator=(const BlockCacheTraceSimulator&) = delete;
  178. BlockCacheTraceSimulator(BlockCacheTraceSimulator&&) = delete;
  179. BlockCacheTraceSimulator& operator=(BlockCacheTraceSimulator&&) = delete;
  180. Status InitializeCaches();
  181. void Access(const BlockCacheTraceRecord& access);
  182. const std::map<CacheConfiguration,
  183. std::vector<std::shared_ptr<CacheSimulator>>>&
  184. sim_caches() const {
  185. return sim_caches_;
  186. }
  187. private:
  188. const uint64_t warmup_seconds_;
  189. const uint32_t downsample_ratio_;
  190. const std::vector<CacheConfiguration> cache_configurations_;
  191. bool warmup_complete_ = false;
  192. std::map<CacheConfiguration, std::vector<std::shared_ptr<CacheSimulator>>>
  193. sim_caches_;
  194. uint64_t trace_start_time_ = 0;
  195. };
  196. } // namespace ROCKSDB_NAMESPACE