Skip to content

Commit aa02c05

Browse files
committed
MM2Q promotion iterator
----------------------- Hot queue iterator for 2Q. Will start at Hot queue and move to Warm queue if hot queue is exhausted. Useful for promotion semantics if using 2Q replacement. rebased on to develop and added some tests.
1 parent 5d61e8a commit aa02c05

File tree

4 files changed

+113
-10
lines changed

4 files changed

+113
-10
lines changed

cachelib/allocator/MM2Q.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,11 @@ class MM2Q {
500500
// Iterator passed as parameter.
501501
template <typename F>
502502
void withEvictionIterator(F&& f);
503+
504+
// Execute provided function under container lock. Function gets
505+
// iterator passed as parameter.
506+
template <typename F>
507+
void withPromotionIterator(F&& f);
503508

504509
// Execute provided function under container lock.
505510
template <typename F>
@@ -921,6 +926,15 @@ void MM2Q::Container<T, HookPtr>::withEvictionIterator(F&& fun) {
921926
}
922927
}
923928

929+
// returns the head of the hot queue for promotion
930+
template <typename T, MM2Q::Hook<T> T::*HookPtr>
931+
template <typename F>
932+
void
933+
MM2Q::Container<T, HookPtr>::withPromotionIterator(F&& fun) {
934+
lruMutex_->lock_combine([this, &fun]() {
935+
fun(LockedIterator{LockHolder{}, lru_.begin(LruType::Hot)});
936+
});
937+
}
924938
template <typename T, MM2Q::Hook<T> T::*HookPtr>
925939
template <typename F>
926940
void MM2Q::Container<T, HookPtr>::withContainerLock(F&& fun) {

cachelib/allocator/datastruct/DList.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ class DList {
219219
curr_ = dir_ == Direction::FROM_HEAD ? dlist_->head_ : dlist_->tail_;
220220
}
221221

222+
Direction getDirection() noexcept {
223+
return dir_;
224+
}
225+
222226
protected:
223227
void goForward() noexcept;
224228
void goBackward() noexcept;

cachelib/allocator/datastruct/MultiDList.h

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,18 @@ class MultiDList {
108108
}
109109

110110
explicit Iterator(const MultiDList<T, HookPtr>& mlist,
111-
size_t listIdx) noexcept
111+
size_t listIdx, bool head) noexcept
112112
: currIter_(mlist.lists_[mlist.lists_.size() - 1]->rbegin()),
113113
mlist_(mlist) {
114114
XDCHECK_LT(listIdx, mlist.lists_.size());
115-
initToValidRBeginFrom(listIdx);
115+
if (head) {
116+
initToValidBeginFrom(listIdx);
117+
} else {
118+
initToValidRBeginFrom(listIdx);
119+
}
116120
// We should either point to an element or the end() iterator
117121
// which has an invalid index_.
118-
XDCHECK(index_ == kInvalidIndex || currIter_.get() != nullptr);
122+
XDCHECK(index_ == kInvalidIndex || index_ == mlist.lists_.size() || currIter_.get() != nullptr);
119123
}
120124
virtual ~Iterator() = default;
121125

@@ -167,6 +171,9 @@ class MultiDList {
167171

168172
// reset iterator to the beginning of a speicific queue
169173
void initToValidRBeginFrom(size_t listIdx) noexcept;
174+
175+
// reset iterator to the head of a specific queue
176+
void initToValidBeginFrom(size_t listIdx) noexcept;
170177

171178
// Index of current list
172179
size_t index_{0};
@@ -182,6 +189,9 @@ class MultiDList {
182189

183190
// provides an iterator starting from the tail of a specific list.
184191
Iterator rbegin(size_t idx) const;
192+
193+
// provides an iterator starting from the head of a specific list.
194+
Iterator begin(size_t idx) const;
185195

186196
// Iterator to compare against for the end.
187197
Iterator rend() const noexcept;
@@ -201,12 +211,26 @@ void MultiDList<T, HookPtr>::Iterator::goForward() noexcept {
201211
}
202212
// Move iterator forward
203213
++currIter_;
204-
// If we land at the rend of this list, move to the previous list.
205-
while (index_ != kInvalidIndex &&
206-
currIter_ == mlist_.lists_[index_]->rend()) {
207-
--index_;
208-
if (index_ != kInvalidIndex) {
209-
currIter_ = mlist_.lists_[index_]->rbegin();
214+
215+
if (currIter_.getDirection() == DListIterator::Direction::FROM_HEAD) {
216+
// If we land at the rend of this list, move to the previous list.
217+
while (index_ != kInvalidIndex && index_ != mlist_.lists_.size() &&
218+
currIter_ == mlist_.lists_[index_]->end()) {
219+
++index_;
220+
if (index_ != kInvalidIndex && index_ != mlist_.lists_.size()) {
221+
currIter_ = mlist_.lists_[index_]->begin();
222+
} else {
223+
return;
224+
}
225+
}
226+
} else {
227+
// If we land at the rend of this list, move to the previous list.
228+
while (index_ != kInvalidIndex &&
229+
currIter_ == mlist_.lists_[index_]->rend()) {
230+
--index_;
231+
if (index_ != kInvalidIndex) {
232+
currIter_ = mlist_.lists_[index_]->rbegin();
233+
}
210234
}
211235
}
212236
}
@@ -247,6 +271,25 @@ void MultiDList<T, HookPtr>::Iterator::initToValidRBeginFrom(
247271
: mlist_.lists_[index_]->rbegin();
248272
}
249273

274+
template <typename T, DListHook<T> T::*HookPtr>
275+
void MultiDList<T, HookPtr>::Iterator::initToValidBeginFrom(
276+
size_t listIdx) noexcept {
277+
// Find the first non-empty list.
278+
index_ = listIdx;
279+
while (index_ != mlist_.lists_.size() &&
280+
mlist_.lists_[index_]->size() == 0) {
281+
++index_;
282+
}
283+
if (index_ == mlist_.lists_.size()) {
284+
//we reached the end - we should get set to
285+
//invalid index
286+
index_ = std::numeric_limits<size_t>::max();
287+
}
288+
currIter_ = index_ == std::numeric_limits<size_t>::max()
289+
? mlist_.lists_[0]->begin()
290+
: mlist_.lists_[index_]->begin();
291+
}
292+
250293
template <typename T, DListHook<T> T::*HookPtr>
251294
typename MultiDList<T, HookPtr>::Iterator&
252295
MultiDList<T, HookPtr>::Iterator::operator++() noexcept {
@@ -273,7 +316,16 @@ typename MultiDList<T, HookPtr>::Iterator MultiDList<T, HookPtr>::rbegin(
273316
if (listIdx >= lists_.size()) {
274317
throw std::invalid_argument("Invalid list index for MultiDList iterator.");
275318
}
276-
return MultiDList<T, HookPtr>::Iterator(*this, listIdx);
319+
return MultiDList<T, HookPtr>::Iterator(*this, listIdx, false);
320+
}
321+
322+
template <typename T, DListHook<T> T::*HookPtr>
323+
typename MultiDList<T, HookPtr>::Iterator MultiDList<T, HookPtr>::begin(
324+
size_t listIdx) const {
325+
if (listIdx >= lists_.size()) {
326+
throw std::invalid_argument("Invalid list index for MultiDList iterator.");
327+
}
328+
return MultiDList<T, HookPtr>::Iterator(*this, listIdx, true);
277329
}
278330

279331
template <typename T, DListHook<T> T::*HookPtr>

cachelib/allocator/tests/MM2QTest.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,19 @@ void MMTypeTest<MMType>::testIterate(std::vector<std::unique_ptr<Node>>& nodes,
223223
}
224224
}
225225

226+
template <typename MMType>
227+
void MMTypeTest<MMType>::testIterateHot(std::vector<std::unique_ptr<Node>>& nodes,
228+
Container& c) {
229+
auto it = nodes.rbegin();
230+
c.withPromotionIterator([&it,&c](auto &&it2q) {
231+
while (it2q && c.isHot(*it2q)) {
232+
ASSERT_EQ(it2q->getId(), (*it)->getId());
233+
++it2q;
234+
++it;
235+
}
236+
});
237+
}
238+
226239
template <typename MMType>
227240
void MMTypeTest<MMType>::testMatch(std::string expected,
228241
MMTypeTest<MMType>::Container& c) {
@@ -238,6 +251,23 @@ void MMTypeTest<MMType>::testMatch(std::string expected,
238251
ASSERT_EQ(expected, actual);
239252
}
240253

254+
template <typename MMType>
255+
void MMTypeTest<MMType>::testMatchHot(std::string expected,
256+
MMTypeTest<MMType>::Container& c) {
257+
int index = -1;
258+
std::string actual;
259+
c.withPromotionIterator([&c,&actual,&index](auto &&it2q) {
260+
while (it2q) {
261+
++index;
262+
actual += folly::stringPrintf(
263+
"%d:%s, ", it2q->getId(),
264+
(c.isHot(*it2q) ? "H" : (c.isCold(*it2q) ? "C" : "W")));
265+
++it2q;
266+
}
267+
});
268+
ASSERT_EQ(expected, actual);
269+
}
270+
241271
TEST_F(MM2QTest, DetailedTest) {
242272
MM2Q::Config config;
243273
config.lruRefreshTime = 0;
@@ -259,8 +289,11 @@ TEST_F(MM2QTest, DetailedTest) {
259289
}
260290

261291
testIterate(nodes, c);
292+
testIterateHot(nodes, c);
262293

263294
testMatch("0:C, 1:C, 2:C, 3:C, 4:H, 5:H, ", c);
295+
testMatchHot("5:H, 4:H, 3:C, 2:C, 1:C, 0:C, ", c);
296+
264297
// Move 3 to top of the hot cache
265298
c.recordAccess(*(nodes[4]), AccessMode::kRead);
266299
testMatch("0:C, 1:C, 2:C, 3:C, 5:H, 4:H, ", c);

0 commit comments

Comments
 (0)