rocksdb/utilities/simulator_cache/sim_cache.cc
Aaron Gao 5d660258e7 add simulator Cache as class SimCache/SimLRUCache(with test)
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
2016-05-23 23:35:23 -07:00

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