Skip to content

Commit acdfa0b

Browse files
authored
MM2Q promotion iterators (#1)
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 11fadaa commit acdfa0b

File tree

7 files changed

+115
-10
lines changed

7 files changed

+115
-10
lines changed

cachelib/allocator/MM2Q-inl.h

+9
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,15 @@ MM2Q::Container<T, HookPtr>::withEvictionIterator(F&& fun) {
252252
});
253253
}
254254

255+
// returns the head of the hot queue for promotion
256+
template <typename T, MM2Q::Hook<T> T::*HookPtr>
257+
template <typename F>
258+
void
259+
MM2Q::Container<T, HookPtr>::withPromotionIterator(F&& fun) {
260+
lruMutex_->lock_combine([this, &fun]() {
261+
fun(Iterator{LockHolder{}, lru_.begin(LruType::Hot)});
262+
});
263+
}
255264

256265
template <typename T, MM2Q::Hook<T> T::*HookPtr>
257266
void MM2Q::Container<T, HookPtr>::removeLocked(T& node,

cachelib/allocator/MM2Q.h

+5
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,11 @@ class MM2Q {
442442
// iterator passed as parameter.
443443
template <typename F>
444444
void withEvictionIterator(F&& f);
445+
446+
// Execute provided function under container lock. Function gets
447+
// iterator passed as parameter.
448+
template <typename F>
449+
void withPromotionIterator(F&& f);
445450

446451
// get the current config as a copy
447452
Config getConfig() const;

cachelib/allocator/datastruct/DList.h

+4
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ class DList {
216216
curr_ = dir_ == Direction::FROM_HEAD ? dlist_->head_ : dlist_->tail_;
217217
}
218218

219+
Direction getDirection() noexcept {
220+
return dir_;
221+
}
222+
219223
protected:
220224
void goForward() noexcept;
221225
void goBackward() noexcept;

cachelib/allocator/datastruct/MultiDList-inl.h

+49-7
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,26 @@ void MultiDList<T, HookPtr>::Iterator::goForward() noexcept {
2525
}
2626
// Move iterator forward
2727
++currIter_;
28-
// If we land at the rend of this list, move to the previous list.
29-
while (index_ != kInvalidIndex &&
30-
currIter_ == mlist_.lists_[index_]->rend()) {
31-
--index_;
32-
if (index_ != kInvalidIndex) {
33-
currIter_ = mlist_.lists_[index_]->rbegin();
28+
29+
if (currIter_.getDirection() == DListIterator::Direction::FROM_HEAD) {
30+
// If we land at the rend of this list, move to the previous list.
31+
while (index_ != kInvalidIndex && index_ != mlist_.lists_.size() &&
32+
currIter_ == mlist_.lists_[index_]->end()) {
33+
++index_;
34+
if (index_ != kInvalidIndex && index_ != mlist_.lists_.size()) {
35+
currIter_ = mlist_.lists_[index_]->begin();
36+
} else {
37+
return;
38+
}
39+
}
40+
} else {
41+
// If we land at the rend of this list, move to the previous list.
42+
while (index_ != kInvalidIndex &&
43+
currIter_ == mlist_.lists_[index_]->rend()) {
44+
--index_;
45+
if (index_ != kInvalidIndex) {
46+
currIter_ = mlist_.lists_[index_]->rbegin();
47+
}
3448
}
3549
}
3650
}
@@ -71,6 +85,25 @@ void MultiDList<T, HookPtr>::Iterator::initToValidRBeginFrom(
7185
: mlist_.lists_[index_]->rbegin();
7286
}
7387

88+
template <typename T, DListHook<T> T::*HookPtr>
89+
void MultiDList<T, HookPtr>::Iterator::initToValidBeginFrom(
90+
size_t listIdx) noexcept {
91+
// Find the first non-empty list.
92+
index_ = listIdx;
93+
while (index_ != mlist_.lists_.size() &&
94+
mlist_.lists_[index_]->size() == 0) {
95+
++index_;
96+
}
97+
if (index_ == mlist_.lists_.size()) {
98+
//we reached the end - we should get set to
99+
//invalid index
100+
index_ = std::numeric_limits<size_t>::max();
101+
}
102+
currIter_ = index_ == std::numeric_limits<size_t>::max()
103+
? mlist_.lists_[0]->begin()
104+
: mlist_.lists_[index_]->begin();
105+
}
106+
74107
template <typename T, DListHook<T> T::*HookPtr>
75108
typename MultiDList<T, HookPtr>::Iterator&
76109
MultiDList<T, HookPtr>::Iterator::operator++() noexcept {
@@ -97,7 +130,16 @@ typename MultiDList<T, HookPtr>::Iterator MultiDList<T, HookPtr>::rbegin(
97130
if (listIdx >= lists_.size()) {
98131
throw std::invalid_argument("Invalid list index for MultiDList iterator.");
99132
}
100-
return MultiDList<T, HookPtr>::Iterator(*this, listIdx);
133+
return MultiDList<T, HookPtr>::Iterator(*this, listIdx, false);
134+
}
135+
136+
template <typename T, DListHook<T> T::*HookPtr>
137+
typename MultiDList<T, HookPtr>::Iterator MultiDList<T, HookPtr>::begin(
138+
size_t listIdx) const {
139+
if (listIdx >= lists_.size()) {
140+
throw std::invalid_argument("Invalid list index for MultiDList iterator.");
141+
}
142+
return MultiDList<T, HookPtr>::Iterator(*this, listIdx, true);
101143
}
102144

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

cachelib/allocator/datastruct/MultiDList.h

+13-3
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,18 @@ class MultiDList {
110110
}
111111

112112
explicit Iterator(const MultiDList<T, HookPtr>& mlist,
113-
size_t listIdx) noexcept
113+
size_t listIdx, bool head) noexcept
114114
: currIter_(mlist.lists_[mlist.lists_.size() - 1]->rbegin()),
115115
mlist_(mlist) {
116116
XDCHECK_LT(listIdx, mlist.lists_.size());
117-
initToValidRBeginFrom(listIdx);
117+
if (head) {
118+
initToValidBeginFrom(listIdx);
119+
} else {
120+
initToValidRBeginFrom(listIdx);
121+
}
118122
// We should either point to an element or the end() iterator
119123
// which has an invalid index_.
120-
XDCHECK(index_ == kInvalidIndex || currIter_.get() != nullptr);
124+
XDCHECK(index_ == kInvalidIndex || index_ == mlist.lists_.size() || currIter_.get() != nullptr);
121125
}
122126
virtual ~Iterator() = default;
123127

@@ -169,6 +173,9 @@ class MultiDList {
169173

170174
// reset iterator to the beginning of a speicific queue
171175
void initToValidRBeginFrom(size_t listIdx) noexcept;
176+
177+
// reset iterator to the head of a specific queue
178+
void initToValidBeginFrom(size_t listIdx) noexcept;
172179

173180
// Index of current list
174181
size_t index_{0};
@@ -184,6 +191,9 @@ class MultiDList {
184191

185192
// provides an iterator starting from the tail of a specific list.
186193
Iterator rbegin(size_t idx) const;
194+
195+
// provides an iterator starting from the head of a specific list.
196+
Iterator begin(size_t idx) const;
187197

188198
// Iterator to compare against for the end.
189199
Iterator rend() const noexcept;

cachelib/allocator/tests/MM2QTest.cpp

+33
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,19 @@ void MMTypeTest<MMType>::testIterate(std::vector<std::unique_ptr<Node>>& nodes,
218218
}
219219
}
220220

221+
template <typename MMType>
222+
void MMTypeTest<MMType>::testIterateHot(std::vector<std::unique_ptr<Node>>& nodes,
223+
Container& c) {
224+
auto it = nodes.rbegin();
225+
c.withPromotionIterator([&it,&c](auto &&it2q) {
226+
while (it2q && c.isHot(*it2q)) {
227+
ASSERT_EQ(it2q->getId(), (*it)->getId());
228+
++it2q;
229+
++it;
230+
}
231+
});
232+
}
233+
221234
template <typename MMType>
222235
void MMTypeTest<MMType>::testMatch(std::string expected,
223236
MMTypeTest<MMType>::Container& c) {
@@ -234,6 +247,23 @@ void MMTypeTest<MMType>::testMatch(std::string expected,
234247
ASSERT_EQ(expected, actual);
235248
}
236249

250+
template <typename MMType>
251+
void MMTypeTest<MMType>::testMatchHot(std::string expected,
252+
MMTypeTest<MMType>::Container& c) {
253+
int index = -1;
254+
std::string actual;
255+
c.withPromotionIterator([&c,&actual,&index](auto &&it2q) {
256+
while (it2q) {
257+
++index;
258+
actual += folly::stringPrintf(
259+
"%d:%s, ", it2q->getId(),
260+
(c.isHot(*it2q) ? "H" : (c.isCold(*it2q) ? "C" : "W")));
261+
++it2q;
262+
}
263+
});
264+
ASSERT_EQ(expected, actual);
265+
}
266+
237267
TEST_F(MM2QTest, DetailedTest) {
238268
MM2Q::Config config;
239269
config.lruRefreshTime = 0;
@@ -255,8 +285,11 @@ TEST_F(MM2QTest, DetailedTest) {
255285
}
256286

257287
testIterate(nodes, c);
288+
testIterateHot(nodes, c);
258289

259290
testMatch("0:C, 1:C, 2:C, 3:C, 4:H, 5:H, ", c);
291+
testMatchHot("5:H, 4:H, 3:C, 2:C, 1:C, 0:C, ", c);
292+
260293
// Move 3 to top of the hot cache
261294
c.recordAccess(*(nodes[4]), AccessMode::kRead);
262295
testMatch("0:C, 1:C, 2:C, 3:C, 5:H, 4:H, ", c);

cachelib/allocator/tests/MMTypeTest.h

+2
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@ class MMTypeTest : public testing::Test {
147147
void testRecordAccessBasic(Config c);
148148
void testSerializationBasic(Config c);
149149
void testIterate(std::vector<std::unique_ptr<Node>>& nodes, Container& c);
150+
void testIterateHot(std::vector<std::unique_ptr<Node>>& nodes, Container& c);
150151
void testMatch(std::string expected, Container& c);
152+
void testMatchHot(std::string expected, Container& c);
151153
size_t getListSize(const Container& c, typename MMType::LruType list);
152154
};
153155

0 commit comments

Comments
 (0)