Cache simulator: Refactor the cache simulator so that we can add alternative policies easily (#5517)
Summary: This PR creates cache_simulator.h file. It contains a CacheSimulator that runs against a block cache trace record. We can add alternative cache simulators derived from CacheSimulator later. For example, this PR adds a PrioritizedCacheSimulator that inserts filter/index/uncompressed dictionary blocks with high priority. Pull Request resolved: https://github.com/facebook/rocksdb/pull/5517 Test Plan: make clean && COMPILE_WITH_ASAN=1 make check -j32 Differential Revision: D16043689 Pulled By: HaoyuHuang fbshipit-source-id: 65f28ed52b866ffb0e6eceffd7f9ca7c45bb680dmain
parent
3886dddc3b
commit
9f0bd56889
@ -0,0 +1,104 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
#include "utilities/simulator_cache/cache_simulator.h" |
||||||
|
|
||||||
|
namespace rocksdb { |
||||||
|
CacheSimulator::CacheSimulator(std::shared_ptr<SimCache> sim_cache) |
||||||
|
: sim_cache_(sim_cache) {} |
||||||
|
|
||||||
|
void CacheSimulator::Access(const BlockCacheTraceRecord& access) { |
||||||
|
auto handle = sim_cache_->Lookup(access.block_key); |
||||||
|
if (handle == nullptr && !access.no_insert) { |
||||||
|
sim_cache_->Insert(access.block_key, /*value=*/nullptr, access.block_size, |
||||||
|
/*deleter=*/nullptr, /*handle=*/nullptr); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void PrioritizedCacheSimulator::Access(const BlockCacheTraceRecord& access) { |
||||||
|
auto handle = sim_cache_->Lookup(access.block_key); |
||||||
|
if (handle == nullptr && !access.no_insert) { |
||||||
|
Cache::Priority priority = Cache::Priority::LOW; |
||||||
|
if (access.block_type == TraceType::kBlockTraceFilterBlock || |
||||||
|
access.block_type == TraceType::kBlockTraceIndexBlock || |
||||||
|
access.block_type == TraceType::kBlockTraceUncompressionDictBlock) { |
||||||
|
priority = Cache::Priority::HIGH; |
||||||
|
} |
||||||
|
sim_cache_->Insert(access.block_key, /*value=*/nullptr, access.block_size, |
||||||
|
/*deleter=*/nullptr, /*handle=*/nullptr, priority); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
double CacheSimulator::miss_ratio() { |
||||||
|
uint64_t hits = sim_cache_->get_hit_counter(); |
||||||
|
uint64_t misses = sim_cache_->get_miss_counter(); |
||||||
|
uint64_t total_accesses = hits + misses; |
||||||
|
return static_cast<double>(misses * 100.0 / total_accesses); |
||||||
|
} |
||||||
|
|
||||||
|
uint64_t CacheSimulator::total_accesses() { |
||||||
|
return sim_cache_->get_hit_counter() + sim_cache_->get_miss_counter(); |
||||||
|
} |
||||||
|
|
||||||
|
BlockCacheTraceSimulator::BlockCacheTraceSimulator( |
||||||
|
uint64_t warmup_seconds, uint32_t downsample_ratio, |
||||||
|
const std::vector<CacheConfiguration>& cache_configurations) |
||||||
|
: warmup_seconds_(warmup_seconds), |
||||||
|
downsample_ratio_(downsample_ratio), |
||||||
|
cache_configurations_(cache_configurations) {} |
||||||
|
|
||||||
|
Status BlockCacheTraceSimulator::InitializeCaches() { |
||||||
|
for (auto const& config : cache_configurations_) { |
||||||
|
for (auto cache_capacity : config.cache_capacities) { |
||||||
|
// Scale down the cache capacity since the trace contains accesses on
|
||||||
|
// 1/'downsample_ratio' blocks.
|
||||||
|
uint64_t simulate_cache_capacity = cache_capacity / downsample_ratio_; |
||||||
|
std::shared_ptr<CacheSimulator> sim_cache; |
||||||
|
if (config.cache_name == "lru") { |
||||||
|
sim_cache = std::make_shared<CacheSimulator>(NewSimCache( |
||||||
|
NewLRUCache(simulate_cache_capacity, config.num_shard_bits, |
||||||
|
/*strict_capacity_limit=*/false, |
||||||
|
/*high_pri_pool_ratio=*/0), |
||||||
|
/*real_cache=*/nullptr, config.num_shard_bits)); |
||||||
|
} else if (config.cache_name == "lru_priority") { |
||||||
|
sim_cache = std::make_shared<PrioritizedCacheSimulator>(NewSimCache( |
||||||
|
NewLRUCache(simulate_cache_capacity, config.num_shard_bits, |
||||||
|
/*strict_capacity_limit=*/false, |
||||||
|
/*high_pri_pool_ratio=*/0.5), |
||||||
|
/*real_cache=*/nullptr, config.num_shard_bits)); |
||||||
|
} else { |
||||||
|
// Not supported.
|
||||||
|
return Status::InvalidArgument("Unknown cache name " + |
||||||
|
config.cache_name); |
||||||
|
} |
||||||
|
sim_caches_[config].push_back(sim_cache); |
||||||
|
} |
||||||
|
} |
||||||
|
return Status::OK(); |
||||||
|
} |
||||||
|
|
||||||
|
void BlockCacheTraceSimulator::Access(const BlockCacheTraceRecord& access) { |
||||||
|
if (trace_start_time_ == 0) { |
||||||
|
trace_start_time_ = access.access_timestamp; |
||||||
|
} |
||||||
|
// access.access_timestamp is in microseconds.
|
||||||
|
if (!warmup_complete_ && |
||||||
|
trace_start_time_ + warmup_seconds_ * kMicrosInSecond <= |
||||||
|
access.access_timestamp) { |
||||||
|
for (auto& config_caches : sim_caches_) { |
||||||
|
for (auto& sim_cache : config_caches.second) { |
||||||
|
sim_cache->reset_counter(); |
||||||
|
} |
||||||
|
} |
||||||
|
warmup_complete_ = true; |
||||||
|
} |
||||||
|
for (auto& config_caches : sim_caches_) { |
||||||
|
for (auto& sim_cache : config_caches.second) { |
||||||
|
sim_cache->Access(access); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace rocksdb
|
@ -0,0 +1,98 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "rocksdb/utilities/sim_cache.h" |
||||||
|
#include "trace_replay/block_cache_tracer.h" |
||||||
|
|
||||||
|
namespace rocksdb { |
||||||
|
|
||||||
|
const uint64_t kMicrosInSecond = 1000000; |
||||||
|
|
||||||
|
// A cache configuration provided by user.
|
||||||
|
struct CacheConfiguration { |
||||||
|
std::string cache_name; // LRU.
|
||||||
|
uint32_t num_shard_bits; |
||||||
|
std::vector<uint64_t> |
||||||
|
cache_capacities; // simulate cache capacities in bytes.
|
||||||
|
|
||||||
|
bool operator=(const CacheConfiguration& o) const { |
||||||
|
return cache_name == o.cache_name && num_shard_bits == o.num_shard_bits; |
||||||
|
} |
||||||
|
bool operator<(const CacheConfiguration& o) const { |
||||||
|
return cache_name < o.cache_name || |
||||||
|
(cache_name == o.cache_name && num_shard_bits < o.num_shard_bits); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// A cache simulator that runs against a block cache trace.
|
||||||
|
class CacheSimulator { |
||||||
|
public: |
||||||
|
CacheSimulator(std::shared_ptr<SimCache> sim_cache); |
||||||
|
virtual ~CacheSimulator() = default; |
||||||
|
// No copy and move.
|
||||||
|
CacheSimulator(const CacheSimulator&) = delete; |
||||||
|
CacheSimulator& operator=(const CacheSimulator&) = delete; |
||||||
|
CacheSimulator(CacheSimulator&&) = delete; |
||||||
|
CacheSimulator& operator=(CacheSimulator&&) = delete; |
||||||
|
|
||||||
|
virtual void Access(const BlockCacheTraceRecord& access); |
||||||
|
void reset_counter() { sim_cache_->reset_counter(); } |
||||||
|
double miss_ratio(); |
||||||
|
uint64_t total_accesses(); |
||||||
|
|
||||||
|
protected: |
||||||
|
std::shared_ptr<SimCache> sim_cache_; |
||||||
|
}; |
||||||
|
|
||||||
|
// A prioritized cache simulator that runs against a block cache trace.
|
||||||
|
// It inserts missing index/filter/uncompression-dictionary blocks with high
|
||||||
|
// priority in the cache.
|
||||||
|
class PrioritizedCacheSimulator : public CacheSimulator { |
||||||
|
public: |
||||||
|
PrioritizedCacheSimulator(std::shared_ptr<SimCache> sim_cache) |
||||||
|
: CacheSimulator(sim_cache) {} |
||||||
|
void Access(const BlockCacheTraceRecord& access) override; |
||||||
|
}; |
||||||
|
|
||||||
|
// A block cache simulator that reports miss ratio curves given a set of cache
|
||||||
|
// configurations.
|
||||||
|
class BlockCacheTraceSimulator { |
||||||
|
public: |
||||||
|
// warmup_seconds: The number of seconds to warmup simulated caches. The
|
||||||
|
// hit/miss counters are reset after the warmup completes.
|
||||||
|
BlockCacheTraceSimulator( |
||||||
|
uint64_t warmup_seconds, uint32_t downsample_ratio, |
||||||
|
const std::vector<CacheConfiguration>& cache_configurations); |
||||||
|
~BlockCacheTraceSimulator() = default; |
||||||
|
// No copy and move.
|
||||||
|
BlockCacheTraceSimulator(const BlockCacheTraceSimulator&) = delete; |
||||||
|
BlockCacheTraceSimulator& operator=(const BlockCacheTraceSimulator&) = delete; |
||||||
|
BlockCacheTraceSimulator(BlockCacheTraceSimulator&&) = delete; |
||||||
|
BlockCacheTraceSimulator& operator=(BlockCacheTraceSimulator&&) = delete; |
||||||
|
|
||||||
|
Status InitializeCaches(); |
||||||
|
|
||||||
|
void Access(const BlockCacheTraceRecord& access); |
||||||
|
|
||||||
|
const std::map<CacheConfiguration, |
||||||
|
std::vector<std::shared_ptr<CacheSimulator>>>& |
||||||
|
sim_caches() const { |
||||||
|
return sim_caches_; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
const uint64_t warmup_seconds_; |
||||||
|
const uint32_t downsample_ratio_; |
||||||
|
const std::vector<CacheConfiguration> cache_configurations_; |
||||||
|
|
||||||
|
bool warmup_complete_ = false; |
||||||
|
std::map<CacheConfiguration, std::vector<std::shared_ptr<CacheSimulator>>> |
||||||
|
sim_caches_; |
||||||
|
uint64_t trace_start_time_ = 0; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace rocksdb
|
Loading…
Reference in new issue