Skip to content

Commit 42f6cf7

Browse files
pbhandar2meta-codesync[bot]
authored andcommitted
Reuse time based reinsertion
Summary: Compute reuse time using a rotating list of bloom filters which is implemented in AccessTracker in Cachelib. Will add configerator based configuration in the future along with additional options. Reviewed By: rlyerly Differential Revision: D81211919 fbshipit-source-id: 4dcabbc355d9159efdd83ce832b093d2491e4e08
1 parent 460cdd4 commit 42f6cf7

5 files changed

Lines changed: 396 additions & 0 deletions

File tree

‎cachelib/allocator/nvmcache/BlockCacheReinsertionPolicy.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ class BlockCacheReinsertionPolicy {
3434
virtual bool shouldReinsert(folly::StringPiece key,
3535
folly::StringPiece value) = 0;
3636

37+
// This function is called when a key is looked up in block cache in function
38+
// BlockCache::lookup(HashedKey hk, Buffer& value). The function is used to
39+
// by reinsertion policy that track access history of items.
40+
virtual void onLookup(folly::StringPiece) {}
41+
3742
// Exports policy stats via CounterVisitor.
3843
virtual void getCounters(const util::CounterVisitor& visitor) const = 0;
3944
};

‎cachelib/navy/block_cache/BlockCache.cpp‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,9 @@ Status BlockCache::lookup(HashedKey hk, Buffer& value) {
304304
}
305305
regionManager_.close(std::move(desc));
306306
lookupCount_.inc();
307+
if (reinsertionPolicy_) {
308+
reinsertionPolicy_->onLookup(hk.key());
309+
}
307310
return status;
308311
}
309312
case OpenStatus::Retry:
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "ReuseTimeReinsertionPolicy.h"
18+
19+
#include "cachelib/allocator/nvmcache/NvmItem.h"
20+
21+
namespace facebook::cachelib::navy {
22+
23+
ReuseTimeReinsertionPolicy::ReuseTimeReinsertionPolicy(
24+
const cachelib::navy::Index& index,
25+
size_t numBuckets,
26+
size_t bucketSize,
27+
uint32_t reinsertionThreshold,
28+
std::shared_ptr<Ticker> ticker)
29+
: index_(index),
30+
numBuckets_(numBuckets),
31+
bucketSize_(bucketSize),
32+
reuseTimeThreshold_(reinsertionThreshold) {
33+
auto config = AccessTracker::Config();
34+
config.numBuckets = numBuckets_;
35+
config.useCounts = false;
36+
config.numTicksPerBucket = bucketSize_;
37+
config.ticker = std::move(ticker);
38+
tracker_ = std::make_unique<AccessTracker>(std::move(config));
39+
}
40+
41+
ReuseTimeReinsertionPolicy::ReuseTimeReinsertionPolicy(
42+
const cachelib::navy::Index& index,
43+
size_t numBuckets,
44+
size_t bucketSize,
45+
uint32_t reinsertionThreshold)
46+
: ReuseTimeReinsertionPolicy(
47+
index, numBuckets, bucketSize, reinsertionThreshold, nullptr) {}
48+
49+
bool ReuseTimeReinsertionPolicy::shouldReinsert(folly::StringPiece key,
50+
folly::StringPiece value) {
51+
reinsertAttempts_.inc();
52+
53+
const auto lr = index_.peek(
54+
makeHK(cachelib::navy::BufferView{
55+
key.size(), reinterpret_cast<const uint8_t*>(key.data())})
56+
.keyHash());
57+
58+
if (!(lr.found())) {
59+
keyNotFound_.inc();
60+
return false;
61+
}
62+
63+
if (isExpired(value)) {
64+
expired_.inc();
65+
return false;
66+
}
67+
68+
auto reuseTime = getReuseTime(key);
69+
if ((reuseTime == 0) || (reuseTime >= reuseTimeThreshold_)) {
70+
return false;
71+
}
72+
reinserted_.inc();
73+
return true;
74+
}
75+
76+
void ReuseTimeReinsertionPolicy::onLookup(folly::StringPiece key) {
77+
tracker_->recordAccess(key);
78+
}
79+
80+
void ReuseTimeReinsertionPolicy::getCounters(
81+
const util::CounterVisitor& visitor) const {
82+
visitor("bc_reinsert_reuse_time_attempts",
83+
reinsertAttempts_.get(),
84+
cachelib::util::CounterVisitor::CounterType::RATE);
85+
visitor("bc_reinsert_reuse_time_key_not_found",
86+
keyNotFound_.get(),
87+
cachelib::util::CounterVisitor::CounterType::RATE);
88+
visitor("bc_reinsert_reuse_time_success",
89+
reinserted_.get(),
90+
cachelib::util::CounterVisitor::CounterType::RATE);
91+
visitor("bc_reinsert_expired",
92+
expired_.get(),
93+
cachelib::util::CounterVisitor::CounterType::RATE);
94+
visitor("bc_reinsert_no_prev_access",
95+
noPrevAccess_.get(),
96+
cachelib::util::CounterVisitor::CounterType::RATE);
97+
// Report percentile stats for reuse time values
98+
reuseTimeStats_.visitQuantileEstimator(visitor, "bc_reinsert_reuse_time");
99+
}
100+
101+
size_t ReuseTimeReinsertionPolicy::getReuseTime(folly::StringPiece key) {
102+
/*
103+
* Compute the reuse time for the key. The reuse time is the maximum of
104+
* time since last access and the most recent reuse time. If we only have
105+
* a history of access in a single bucket, then we only have time since
106+
* access. If we have a history of access in multiple buckets, then we
107+
* have both time since last access and the most recent reuse time.
108+
*
109+
* The granularity of reuse time is based on bucket size. Even if the key
110+
* was accessed in last few seconds, if there is access in the most recent
111+
* bucket, the reuse time will be equal to bucket size.
112+
*/
113+
std::tuple<int64_t, int64_t> reuseTuple = getPrevAccessBuckets(key);
114+
auto reuseTime = 0;
115+
if (std::get<0>(reuseTuple) >= 0) {
116+
size_t timeSinceLastAccess = bucketSize_ * (std::get<0>(reuseTuple) + 1);
117+
size_t latestReuseTime = 0;
118+
if (std::get<1>(reuseTuple) >= 0) {
119+
latestReuseTime =
120+
(std::get<1>(reuseTuple) - std::get<0>(reuseTuple)) * bucketSize_;
121+
}
122+
reuseTime = std::max(timeSinceLastAccess, latestReuseTime);
123+
reuseTimeStats_.trackValue(static_cast<double>(reuseTime));
124+
} else {
125+
noPrevAccess_.inc();
126+
}
127+
return reuseTime;
128+
}
129+
130+
std::tuple<int64_t, int64_t> ReuseTimeReinsertionPolicy::getPrevAccessBuckets(
131+
folly::StringPiece key) {
132+
/*
133+
* Get the index of the latest and second latest access buckets for the key.
134+
* This information allows us to calculate time since last access and the most
135+
* recent reuse time.
136+
* If we can't find the latest or second latest access bucket,
137+
* we assign -1 as the bucket index. The key that was not
138+
* accessed in the time window being tracked will return a tuple of (-1, -1).
139+
*/
140+
std::vector<double> accesses = tracker_->getAccesses(key);
141+
int64_t latestReuseBucket = -1;
142+
int64_t secondLatestReuseBucket = -1;
143+
144+
for (size_t bucketIdx = 0; bucketIdx < accesses.size(); bucketIdx++) {
145+
if (accesses.at(bucketIdx) > 0) {
146+
if (latestReuseBucket == -1) {
147+
latestReuseBucket = bucketIdx;
148+
} else if (secondLatestReuseBucket == -1) {
149+
secondLatestReuseBucket = bucketIdx;
150+
} else {
151+
break;
152+
}
153+
}
154+
}
155+
156+
return std::make_tuple(latestReuseBucket, secondLatestReuseBucket);
157+
}
158+
159+
uint32_t ReuseTimeReinsertionPolicy::isExpired(folly::StringPiece value) {
160+
if (value.size() < sizeof(cachelib::NvmItem)) {
161+
return 0;
162+
}
163+
cachelib::NvmItem nvmItem{0, 0, 0, std::vector<cachelib::Blob>{}};
164+
::memcpy(&nvmItem, value.data(), sizeof(cachelib::NvmItem));
165+
166+
return nvmItem.isExpired();
167+
}
168+
169+
} // namespace facebook::cachelib::navy
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <cachelib/navy/block_cache/Index.h>
20+
#include <folly/container/F14Map.h>
21+
22+
#include <tuple>
23+
24+
#include "cachelib/allocator/nvmcache/BlockCacheReinsertionPolicy.h"
25+
#include "cachelib/common/AccessTracker.h"
26+
#include "cachelib/common/AtomicCounter.h"
27+
#include "cachelib/common/PercentileStats.h"
28+
29+
namespace facebook::cachelib::navy {
30+
31+
namespace tests {
32+
class ReuseTimeReinsertionPolicyTest_PrevAccessBucketTracking_Test;
33+
class ReuseTimeReinsertionPolicyTest_ReuseTimeComputation_Test;
34+
} // namespace tests
35+
36+
class ReuseTimeReinsertionPolicy : public BlockCacheReinsertionPolicy {
37+
public:
38+
ReuseTimeReinsertionPolicy(const cachelib::navy::Index& index,
39+
size_t numBuckets,
40+
size_t bucketSize,
41+
uint32_t defaultReinsertionThreshold);
42+
43+
ReuseTimeReinsertionPolicy(const cachelib::navy::Index& index,
44+
size_t numBuckets,
45+
size_t bucketSize,
46+
uint32_t defaultReinsertionThreshold,
47+
std::shared_ptr<Ticker> ticker);
48+
49+
bool shouldReinsert(folly::StringPiece key,
50+
folly::StringPiece value) override;
51+
52+
void onLookup(folly::StringPiece key) override;
53+
54+
void getCounters(const util::CounterVisitor& visitor) const override;
55+
56+
private:
57+
static uint32_t isExpired(folly::StringPiece value);
58+
std::tuple<int64_t, int64_t> getPrevAccessBuckets(folly::StringPiece key);
59+
size_t getReuseTime(folly::StringPiece key);
60+
61+
const cachelib::navy::Index& index_;
62+
size_t numBuckets_{0};
63+
size_t bucketSize_{0};
64+
uint32_t reuseTimeThreshold_{0};
65+
std::unique_ptr<AccessTracker> tracker_;
66+
AtomicCounter reinsertAttempts_{0};
67+
AtomicCounter keyNotFound_{0};
68+
AtomicCounter expired_{0};
69+
AtomicCounter reinserted_{0};
70+
AtomicCounter noPrevAccess_{0};
71+
mutable util::PercentileStats reuseTimeStats_;
72+
73+
friend class tests::ReuseTimeReinsertionPolicyTest_ReuseTimeComputation_Test;
74+
friend class tests::
75+
ReuseTimeReinsertionPolicyTest_PrevAccessBucketTracking_Test;
76+
};
77+
78+
} // namespace facebook::cachelib::navy

0 commit comments

Comments
 (0)