Skip to content

Commit 4a6307f

Browse files
byrnedjvinser52
authored andcommitted
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 43ad67f commit 4a6307f

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
@@ -264,6 +264,15 @@ MM2Q::Container<T, HookPtr>::withEvictionIterator(F&& fun) {
264264
});
265265
}
266266

267+
// returns the head of the hot queue for promotion
268+
template <typename T, MM2Q::Hook<T> T::*HookPtr>
269+
template <typename F>
270+
void
271+
MM2Q::Container<T, HookPtr>::withPromotionIterator(F&& fun) {
272+
lruMutex_->lock_combine([this, &fun]() {
273+
fun(Iterator{LockHolder{}, lru_.begin(LruType::Hot)});
274+
});
275+
}
267276

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

cachelib/allocator/MM2Q.h

+5
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,11 @@ class MM2Q {
451451
// iterator passed as parameter.
452452
template <typename F>
453453
void withEvictionIterator(F&& f);
454+
455+
// Execute provided function under container lock. Function gets
456+
// iterator passed as parameter.
457+
template <typename F>
458+
void withPromotionIterator(F&& f);
454459

455460
// get the current config as a copy
456461
Config getConfig() const;

cachelib/allocator/datastruct/DList.h

+4
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ class DList {
221221
curr_ = dir_ == Direction::FROM_HEAD ? dlist_->head_ : dlist_->tail_;
222222
}
223223

224+
Direction getDirection() noexcept {
225+
return dir_;
226+
}
227+
224228
protected:
225229
void goForward() noexcept;
226230
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)