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