high_pri_pool_ratio_(high_pri_pool_ratio),
high_pri_pool_capacity_(0),
usage_(0),
- lru_usage_(0) {
+ lru_usage_(0),
+ bin_count_(1),
+ age_bins_() {
// Make empty circular linked list
lru_.next = &lru_;
lru_.prev = &lru_;
lru_low_pri_ = &lru_;
SetCapacity(capacity);
+ RotateBins();
}
LRUCacheShard::~LRUCacheShard() {}
return high_pri_pool_ratio_;
}
+void LRUCacheShard::SetBinCount(uint64_t count) {
+ MutexLock l(&mutex_);
+ if (count > 0) {
+ bin_count_ = count;
+ }
+}
+
+void LRUCacheShard::RotateBins() {
+ MutexLock l(&mutex_);
+ age_bins_.push_front(std::make_shared<size_t>(0));
+ age_bins_.resize(bin_count_);
+}
+
+size_t LRUCacheShard::GetBinnedUsage(uint64_t bin) const {
+ MutexLock l(&mutex_);
+ // Bin out of range, ie no data.
+ if (bin > age_bins_.size() - 1) {
+ return 0;
+ }
+ return *(age_bins_[bin]);
+}
+
+size_t LRUCacheShard::GetBinnedUsage(uint64_t first_bin, uint64_t last_bin) const {
+ MutexLock l(&mutex_);
+
+ // first bin out of range, return 0
+ if (first_bin > age_bins_.size() - 1) {
+ return 0;
+ }
+
+ // last bin out of range, don't walk off the end.
+ if (last_bin > age_bins_.size() - 1) {
+ last_bin = age_bins_.size() - 1;
+ }
+
+ size_t bytes = 0;
+ for (uint64_t i = first_bin; i < last_bin; i++) {
+ bytes += *(age_bins_[i]);
+ }
+ return bytes;
+}
+
void LRUCacheShard::LRU_Remove(LRUHandle* e) {
assert(e->next != nullptr);
assert(e->prev != nullptr);
if (e->InHighPriPool()) {
assert(high_pri_pool_usage_ >= e->charge);
high_pri_pool_usage_ -= e->charge;
+ } else {
+// *(e->cache_age_bin) -= e->charge;
}
}
{
MutexLock l(&mutex_);
+ // Set the current age_bin
+ e->cache_age_bin = age_bins_[0];
+
// Free the space following strict LRU policy until enough space
// is freed or the lru list is empty
EvictFromLRU(charge, &last_reference_list);
// space was freed
LRUHandle* old = table_.Insert(e);
usage_ += e->charge;
+ // Count High Pri items separately
+ if (!e->IsHighPri()) {
+ *(e->cache_age_bin) += e->charge;
+ }
if (old != nullptr) {
old->SetInCache(false);
if (Unref(old)) {
#pragma once
#include <string>
+#include <deque>
#include "cache/sharded_cache.h"
// RUCache::Release (to move into state 2) or LRUCacheShard::Erase (for state 3)
struct LRUHandle {
+ std::shared_ptr<size_t> cache_age_bin; // cache age bin
void* value;
void (*deleter)(const Slice&, void* value);
LRUHandle* next_hash;
virtual size_t GetHighPriPoolUsage() const;
virtual double GetHighPriPoolRatio() const;
+ // Age binned byte counter methods
+ virtual void SetBinCount(uint64_t count) override;
+ virtual void RotateBins() override;
+ virtual size_t GetBinnedUsage(uint64_t bin) const override;
+ virtual size_t GetBinnedUsage(uint64_t first_bin, uint64_t last_bin) const override;
+
private:
void LRU_Remove(LRUHandle* e);
void LRU_Insert(LRUHandle* e);
// We don't count mutex_ as the cache's internal state so semantically we
// don't mind mutex_ invoking the non-const actions.
mutable port::Mutex mutex_;
+
+ // Age binned cache byte counters
+ uint64_t bin_count_;
+ std::deque<std::shared_ptr<size_t>> age_bins_;
};
class LRUCache : public ShardedCache {
virtual size_t GetHighPriPoolUsage() const override;
virtual double GetHighPriPoolRatio() const override;
virtual void SetHighPriPoolRatio(double high_pri_pool_ratio) override;
+
// Retrieves number of elements in LRU, for unit test purpose only
size_t TEST_GetLRUSize();
return usage;
}
+// Aged based counter binning
+void ShardedCache::SetBinCount(uint64_t count) {
+ int num_shards = 1 << num_shard_bits_;
+ for (int s = 0; s < num_shards; s++) {
+ GetShard(s)->SetBinCount(count);
+ }
+}
+
+void ShardedCache::RotateBins() {
+ int num_shards = 1 << num_shard_bits_;
+ for (int s = 0; s < num_shards; s++) {
+ GetShard(s)->RotateBins();
+ }
+}
+
+size_t ShardedCache::GetBinnedUsage(uint64_t bin) const {
+ int num_shards = 1 << num_shard_bits_;
+ size_t usage = 0;
+ for (int s = 0; s < num_shards; s++) {
+ usage += GetShard(s)->GetBinnedUsage(bin);
+ }
+ return usage;
+}
+
+size_t ShardedCache::GetBinnedUsage(uint64_t first_bin, uint64_t last_bin) const {
+ int num_shards = 1 << num_shard_bits_;
+ size_t usage = 0;
+ for (int s = 0; s < num_shards; s++) {
+ usage += GetShard(s)->GetBinnedUsage(first_bin, last_bin);
+ }
+ return usage;
+}
+
void ShardedCache::ApplyToAllCacheEntries(void (*callback)(void*, size_t),
bool thread_safe) {
int num_shards = 1 << num_shard_bits_;
bool thread_safe) = 0;
virtual void EraseUnRefEntries() = 0;
virtual std::string GetPrintableOptions() const { return ""; }
+
+ virtual void SetBinCount(uint64_t count) = 0;
+ virtual void RotateBins() = 0;
+ virtual size_t GetBinnedUsage(uint64_t bin) const = 0;
+ virtual size_t GetBinnedUsage(uint64_t first_bin, uint64_t last_bin) const = 0;
};
// Generic cache interface which shards cache by hash of keys. 2^num_shard_bits
virtual size_t GetHighPriPoolUsage() const override = 0;
virtual double GetHighPriPoolRatio() const override = 0;
+ // Aged based counter binning
+ virtual void SetBinCount(uint64_t count);
+ virtual void RotateBins();
+ virtual size_t GetBinnedUsage(uint64_t bin) const;
+ virtual size_t GetBinnedUsage(uint64_t first_bin, uint64_t last_bin) const;
+
virtual void ApplyToAllCacheEntries(void (*callback)(void*, size_t),
bool thread_safe) override;
virtual void EraseUnRefEntries() override;
return 0;
}
+ // Set the maximum number of age bins to track
+ virtual void SetBinCount(uint64_t count) = 0;
+
+ // Add a new age bin and remove any bins over the bin count.
+ virtual void RotateBins() = 0;
+
+ // Get the byte usage for a given age bin.
+ virtual size_t GetBinnedUsage(uint64_t bin) const = 0;
+
+ // Get the byte usage for a given range of age bins.
+ virtual size_t GetBinnedUsage(uint64_t first_bin, uint64_t last_bin) const = 0;
+
// Call this on shutdown if you want to speed it up. Cache will disown
// any underlying data and will not free it on delete. This call will leak
// memory - call this only if you're shutting down the process.
return cache_activity_logger_.bg_status();
}
+ virtual void SetBinCount(uint64_t count) override {
+ key_only_cache_->SetBinCount(count);
+ }
+
+ virtual void RotateBins() override {
+ key_only_cache_->RotateBins();
+ }
+
+ virtual size_t GetBinnedUsage(uint64_t bin) const override {
+ return key_only_cache_->GetBinnedUsage(bin);
+ }
+
+ virtual size_t GetBinnedUsage(uint64_t first_bin, uint64_t last_bin) const override {
+ return key_only_cache_->GetBinnedUsage(first_bin, last_bin);
+ }
+
private:
std::shared_ptr<Cache> cache_;
std::shared_ptr<Cache> key_only_cache_;