5d660258e7
Summary: add class SimCache(base class with instrumentation api) and SimLRUCache(derived class with detailed implementation) which is used as an instrumented block cache that can predict hit rate for different cache size Test Plan: Add a test case in `db_block_cache_test.cc` called `SimCacheTest` to test basic logic of SimCache. Also add option `-simcache_size` in db_bench. if set with a value other than -1, then the benchmark will use this value as the size of the simulator cache and finally output the simulation result. ``` [gzh@dev9927.prn1 ~/local/rocksdb] ./db_bench -benchmarks "fillseq,readrandom" -cache_size 1000000 -simcache_size 1000000 RocksDB: version 4.8 Date: Tue May 17 16:56:16 2016 CPU: 32 * Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz CPUCache: 20480 KB Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 Prefix: 0 bytes Keys per prefix: 0 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) Write rate: 0 bytes/second Compression: Snappy Memtablerep: skip_list Perf Level: 0 WARNING: Assertions are enabled; benchmarks unnecessarily slow ------------------------------------------------ DB path: [/tmp/rocksdbtest-112628/dbbench] fillseq : 6.809 micros/op 146874 ops/sec; 16.2 MB/s DB path: [/tmp/rocksdbtest-112628/dbbench] readrandom : 6.343 micros/op 157665 ops/sec; 17.4 MB/s (1000000 of 1000000 found) SIMULATOR CACHE STATISTICS: SimCache LOOKUPs: 986559 SimCache HITs: 264760 SimCache HITRATE: 26.84% [gzh@dev9927.prn1 ~/local/rocksdb] ./db_bench -benchmarks "fillseq,readrandom" -cache_size 1000000 -simcache_size 10000000 RocksDB: version 4.8 Date: Tue May 17 16:57:10 2016 CPU: 32 * Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz CPUCache: 20480 KB Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 Prefix: 0 bytes Keys per prefix: 0 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) Write rate: 0 bytes/second Compression: Snappy Memtablerep: skip_list Perf Level: 0 WARNING: Assertions are enabled; benchmarks unnecessarily slow ------------------------------------------------ DB path: [/tmp/rocksdbtest-112628/dbbench] fillseq : 5.066 micros/op 197394 ops/sec; 21.8 MB/s DB path: [/tmp/rocksdbtest-112628/dbbench] readrandom : 6.457 micros/op 154870 ops/sec; 17.1 MB/s (1000000 of 1000000 found) SIMULATOR CACHE STATISTICS: SimCache LOOKUPs: 1059764 SimCache HITs: 374501 SimCache HITRATE: 35.34% [gzh@dev9927.prn1 ~/local/rocksdb] ./db_bench -benchmarks "fillseq,readrandom" -cache_size 1000000 -simcache_size 100000000 RocksDB: version 4.8 Date: Tue May 17 16:57:32 2016 CPU: 32 * Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz CPUCache: 20480 KB Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 Prefix: 0 bytes Keys per prefix: 0 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) Write rate: 0 bytes/second Compression: Snappy Memtablerep: skip_list Perf Level: 0 WARNING: Assertions are enabled; benchmarks unnecessarily slow ------------------------------------------------ DB path: [/tmp/rocksdbtest-112628/dbbench] fillseq : 5.632 micros/op 177572 ops/sec; 19.6 MB/s DB path: [/tmp/rocksdbtest-112628/dbbench] readrandom : 6.892 micros/op 145094 ops/sec; 16.1 MB/s (1000000 of 1000000 found) SIMULATOR CACHE STATISTICS: SimCache LOOKUPs: 1150767 SimCache HITs: 1034535 SimCache HITRATE: 89.90% ``` Reviewers: IslamAbdelRahman, andrewkr, sdong Reviewed By: sdong Subscribers: MarkCallaghan, andrewkr, dhruba, leveldb Differential Revision: https://reviews.facebook.net/D57999
156 lines
5.2 KiB
C++
156 lines
5.2 KiB
C++
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
|
// This source code is licensed under the BSD-style license found in the
|
|
// LICENSE file in the root directory of this source tree. An additional grant
|
|
// of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
#include "rocksdb/utilities/sim_cache.h"
|
|
#include <atomic>
|
|
|
|
namespace rocksdb {
|
|
|
|
namespace {
|
|
// SimCacheImpl definition
|
|
class SimCacheImpl : public SimCache {
|
|
public:
|
|
// capacity for real cache (ShardedLRUCache)
|
|
// test_capacity for key only cache
|
|
SimCacheImpl(std::shared_ptr<Cache> cache, size_t sim_capacity,
|
|
int num_shard_bits)
|
|
: cache_(cache),
|
|
key_only_cache_(NewLRUCache(sim_capacity, num_shard_bits)),
|
|
lookup_times_(0),
|
|
hit_times_(0) {}
|
|
|
|
virtual ~SimCacheImpl() {}
|
|
virtual void SetCapacity(size_t capacity) override {
|
|
cache_->SetCapacity(capacity);
|
|
}
|
|
|
|
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) override {
|
|
cache_->SetStrictCapacityLimit(strict_capacity_limit);
|
|
}
|
|
|
|
virtual Status Insert(const Slice& key, void* value, size_t charge,
|
|
void (*deleter)(const Slice& key, void* value),
|
|
Handle** handle) override {
|
|
// The handle and value passed in are for real cache, so we pass nullptr
|
|
// to key_only_cache_ for both instead. Also, the deleter function pointer
|
|
// will be called by user to perform some external operation which should
|
|
// be applied only once. Thus key_only_cache accepts an empty function.
|
|
// *Lambda function without capture can be assgined to a function pointer
|
|
Handle* h = key_only_cache_->Lookup(key);
|
|
if (h == nullptr) {
|
|
key_only_cache_->Insert(key, nullptr, charge,
|
|
[](const Slice& k, void* v) {}, nullptr);
|
|
} else {
|
|
key_only_cache_->Release(h);
|
|
}
|
|
return cache_->Insert(key, value, charge, deleter, handle);
|
|
}
|
|
|
|
virtual Handle* Lookup(const Slice& key) override {
|
|
inc_lookup_counter();
|
|
Handle* h = key_only_cache_->Lookup(key);
|
|
if (h != nullptr) {
|
|
key_only_cache_->Release(h);
|
|
inc_hit_counter();
|
|
}
|
|
return cache_->Lookup(key);
|
|
}
|
|
|
|
virtual void Release(Handle* handle) override { cache_->Release(handle); }
|
|
|
|
virtual void Erase(const Slice& key) override {
|
|
cache_->Erase(key);
|
|
key_only_cache_->Erase(key);
|
|
}
|
|
|
|
virtual void* Value(Handle* handle) override {
|
|
return reinterpret_cast<LRUHandle*>(handle)->value;
|
|
}
|
|
|
|
virtual uint64_t NewId() override { return cache_->NewId(); }
|
|
|
|
virtual size_t GetCapacity() const override { return cache_->GetCapacity(); }
|
|
|
|
virtual bool HasStrictCapacityLimit() const override {
|
|
return cache_->HasStrictCapacityLimit();
|
|
}
|
|
|
|
virtual size_t GetUsage() const override { return cache_->GetUsage(); }
|
|
|
|
virtual size_t GetUsage(Handle* handle) const override {
|
|
return reinterpret_cast<LRUHandle*>(handle)->charge;
|
|
}
|
|
|
|
virtual size_t GetPinnedUsage() const override {
|
|
return cache_->GetPinnedUsage();
|
|
}
|
|
|
|
virtual void DisownData() override {
|
|
cache_->DisownData();
|
|
key_only_cache_->DisownData();
|
|
}
|
|
|
|
virtual void ApplyToAllCacheEntries(void (*callback)(void*, size_t),
|
|
bool thread_safe) override {
|
|
// only apply to _cache since key_only_cache doesn't hold value
|
|
cache_->ApplyToAllCacheEntries(callback, thread_safe);
|
|
}
|
|
|
|
virtual void EraseUnRefEntries() override {
|
|
cache_->EraseUnRefEntries();
|
|
key_only_cache_->EraseUnRefEntries();
|
|
}
|
|
|
|
virtual size_t GetSimCapacity() const override {
|
|
return key_only_cache_->GetCapacity();
|
|
}
|
|
virtual size_t GetSimUsage() const override {
|
|
return key_only_cache_->GetUsage();
|
|
}
|
|
virtual void SetSimCapacity(size_t capacity) override {
|
|
key_only_cache_->SetCapacity(capacity);
|
|
}
|
|
|
|
virtual uint64_t get_lookup_counter() const override { return lookup_times_; }
|
|
virtual uint64_t get_hit_counter() const override { return hit_times_; }
|
|
virtual double get_hit_rate() const override {
|
|
return hit_times_ * 1.0f / lookup_times_;
|
|
}
|
|
virtual void reset_counter() override { hit_times_ = lookup_times_ = 0; }
|
|
|
|
virtual std::string ToString() const override {
|
|
std::string res;
|
|
res.append("SimCache LOOKUPs: " + std::to_string(get_lookup_counter()) +
|
|
"\n");
|
|
res.append("SimCache HITs: " + std::to_string(get_hit_counter()) + "\n");
|
|
char buff[100];
|
|
snprintf(buff, sizeof(buff), "SimCache HITRATE: %.2f%%\n",
|
|
get_hit_rate() * 100);
|
|
res.append(buff);
|
|
return res;
|
|
}
|
|
|
|
private:
|
|
std::shared_ptr<Cache> cache_;
|
|
std::shared_ptr<Cache> key_only_cache_;
|
|
std::atomic<uint64_t> lookup_times_;
|
|
std::atomic<uint64_t> hit_times_;
|
|
void inc_lookup_counter() { lookup_times_++; }
|
|
void inc_hit_counter() { hit_times_++; }
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
// For instrumentation purpose, use NewSimCache instead
|
|
std::shared_ptr<SimCache> NewSimCache(std::shared_ptr<Cache> cache,
|
|
size_t sim_capacity, int num_shard_bits) {
|
|
if (num_shard_bits >= 20) {
|
|
return nullptr; // the cache cannot be sharded into too many fine pieces
|
|
}
|
|
return std::make_shared<SimCacheImpl>(cache, sim_capacity, num_shard_bits);
|
|
}
|
|
|
|
} // end namespace rocksdb
|