cachelib/navy/block_cache/LruPolicy.cpp (167 lines of code) (raw):
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cachelib/navy/block_cache/LruPolicy.h"
#include <folly/Format.h>
#include <folly/logging/xlog.h>
#include <cstdio>
#include "cachelib/navy/common/Utils.h"
namespace facebook {
namespace cachelib {
namespace navy {
constexpr std::chrono::seconds LruPolicy::kEstimatorWindow;
LruPolicy::LruPolicy(uint32_t expectedNumRegions)
: secSinceInsertionEstimator_{kEstimatorWindow},
secSinceAccessEstimator_{kEstimatorWindow},
hitsEstimator_{kEstimatorWindow} {
array_.reserve(expectedNumRegions);
XLOGF(INFO, "LRU policy: expected {} regions", expectedNumRegions);
}
void LruPolicy::touch(RegionId rid) {
XDCHECK(rid.valid());
auto i = rid.index();
std::lock_guard<std::mutex> lock{mutex_};
if (i >= array_.size()) {
array_.resize(i + 1);
}
if (array_[i].inList()) {
array_[i].hits++;
array_[i].lastUpdateTime = getSteadyClockSeconds();
unlink(i);
linkAtHead(i);
}
}
void LruPolicy::track(const Region& region) {
auto rid = region.id();
XDCHECK(rid.valid());
auto i = rid.index();
std::lock_guard<std::mutex> lock{mutex_};
if (i >= array_.size()) {
array_.resize(i + 1);
}
array_[i].hits = 0;
array_[i].creationTime = getSteadyClockSeconds();
array_[i].lastUpdateTime = getSteadyClockSeconds();
if (!array_[i].inList()) {
linkAtHead(i);
}
}
RegionId LruPolicy::evict() {
uint32_t retRegion{kInvalidIndex};
uint32_t secsSinceAccess{0};
uint32_t secsSinceCreate{0};
uint32_t hits{0};
{
std::lock_guard<std::mutex> lock{mutex_};
if (tail_ == kInvalidIndex) {
return RegionId{};
}
retRegion = tail_;
secsSinceCreate = array_[tail_].secondsSinceCreation().count();
secsSinceAccess = array_[tail_].secondsSinceAccess().count();
hits = array_[tail_].hits;
unlink(tail_);
}
secSinceInsertionEstimator_.trackValue(secsSinceCreate);
secSinceAccessEstimator_.trackValue(secsSinceAccess);
hitsEstimator_.trackValue(hits);
return RegionId{retRegion};
}
void LruPolicy::reset() {
std::lock_guard<std::mutex> lock{mutex_};
array_.clear();
head_ = kInvalidIndex;
tail_ = kInvalidIndex;
}
void LruPolicy::unlink(uint32_t i) {
auto& node = array_[i];
XDCHECK_NE(tail_, kInvalidIndex);
if (tail_ == i) {
tail_ = node.prev;
} else {
XDCHECK_NE(node.next, kInvalidIndex);
array_[node.next].prev = node.prev;
}
XDCHECK_NE(head_, kInvalidIndex);
if (head_ == i) {
head_ = node.next;
} else {
XDCHECK_NE(node.prev, kInvalidIndex);
array_[node.prev].next = node.next;
}
node.next = kInvalidIndex;
node.prev = kInvalidIndex;
}
void LruPolicy::linkAtHead(uint32_t i) {
if (i != head_) {
if (head_ != kInvalidIndex) {
array_[head_].prev = i;
}
array_[i].next = head_;
head_ = i;
if (tail_ == kInvalidIndex) {
tail_ = i;
}
}
}
void LruPolicy::linkAtTail(uint32_t i) {
if (i != tail_) {
if (tail_ != kInvalidIndex) {
array_[tail_].next = i;
}
array_[i].prev = tail_;
tail_ = i;
if (head_ == kInvalidIndex) {
head_ = i;
}
}
}
size_t LruPolicy::memorySize() const {
return sizeof(*this) + sizeof(ListNode) * array_.capacity();
}
void LruPolicy::dump(uint32_t n) const {
dumpList("head", n, head_, &ListNode::next);
dumpList("tail", n, tail_, &ListNode::prev);
}
void LruPolicy::dumpList(const char* tag,
uint32_t n,
uint32_t first,
uint32_t ListNode::*link) const {
if (first == kInvalidIndex) {
XLOGF(ERR, "LRU {} is empty", tag);
return;
}
char buf[600];
char* const bufEnd = buf + sizeof(buf);
char* p = buf;
int len = 0;
std::snprintf(p, bufEnd - p, "LRU from %s %u %n", tag, first, &len);
p += len;
uint32_t i = 0;
while (first != kInvalidIndex && (n == 0 || i < n) && p < bufEnd) {
std::snprintf(
p, bufEnd - p, "%u(h=%u) %n", first, array_[first].hits, &len);
p += len;
first = array_[first].*link;
i++;
}
if (first != kInvalidIndex && p < bufEnd) {
std::snprintf(p, bufEnd - p, "...");
}
XLOG(ERR, buf);
}
void LruPolicy::getCounters(const CounterVisitor& v) const {
secSinceInsertionEstimator_.visitQuantileEstimator(
v, "navy_bc_lru_secs_since_insertion");
secSinceAccessEstimator_.visitQuantileEstimator(
v, "navy_bc_lru_secs_since_access");
hitsEstimator_.visitQuantileEstimator(v, "navy_bc_lru_region_hits_estimate");
}
void LruPolicy::persist(RecordWriter& rw) const {
std::ignore = rw;
throw std::runtime_error("Not Implemented.");
}
void LruPolicy::recover(RecordReader& rr) {
std::ignore = rr;
throw std::runtime_error("Not Implemented.");
}
} // namespace navy
} // namespace cachelib
} // namespace facebook