Skip to content

MM2Q promotion iterators #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cachelib/allocator/MM2Q-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,15 @@ MM2Q::Container<T, HookPtr>::withEvictionIterator(F&& fun) {
});
}

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

template <typename T, MM2Q::Hook<T> T::*HookPtr>
void MM2Q::Container<T, HookPtr>::removeLocked(T& node,
Expand Down
5 changes: 5 additions & 0 deletions cachelib/allocator/MM2Q.h
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,11 @@ class MM2Q {
// iterator passed as parameter.
template <typename F>
void withEvictionIterator(F&& f);

// Execute provided function under container lock. Function gets
// iterator passed as parameter.
template <typename F>
void withPromotionIterator(F&& f);

// get the current config as a copy
Config getConfig() const;
Expand Down
4 changes: 4 additions & 0 deletions cachelib/allocator/datastruct/DList.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ class DList {
curr_ = dir_ == Direction::FROM_HEAD ? dlist_->head_ : dlist_->tail_;
}

Direction getDirection() noexcept {
return dir_;
}

protected:
void goForward() noexcept;
void goBackward() noexcept;
Expand Down
56 changes: 49 additions & 7 deletions cachelib/allocator/datastruct/MultiDList-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,26 @@ void MultiDList<T, HookPtr>::Iterator::goForward() noexcept {
}
// Move iterator forward
++currIter_;
// If we land at the rend of this list, move to the previous list.
while (index_ != kInvalidIndex &&
currIter_ == mlist_.lists_[index_]->rend()) {
--index_;
if (index_ != kInvalidIndex) {
currIter_ = mlist_.lists_[index_]->rbegin();

if (currIter_.getDirection() == DListIterator::Direction::FROM_HEAD) {
// If we land at the rend of this list, move to the previous list.
while (index_ != kInvalidIndex && index_ != mlist_.lists_.size() &&
currIter_ == mlist_.lists_[index_]->end()) {
++index_;
if (index_ != kInvalidIndex && index_ != mlist_.lists_.size()) {
currIter_ = mlist_.lists_[index_]->begin();
} else {
return;
}
}
} else {
// If we land at the rend of this list, move to the previous list.
while (index_ != kInvalidIndex &&
currIter_ == mlist_.lists_[index_]->rend()) {
--index_;
if (index_ != kInvalidIndex) {
currIter_ = mlist_.lists_[index_]->rbegin();
}
}
}
}
Expand Down Expand Up @@ -71,6 +85,25 @@ void MultiDList<T, HookPtr>::Iterator::initToValidRBeginFrom(
: mlist_.lists_[index_]->rbegin();
}

template <typename T, DListHook<T> T::*HookPtr>
void MultiDList<T, HookPtr>::Iterator::initToValidBeginFrom(
size_t listIdx) noexcept {
// Find the first non-empty list.
index_ = listIdx;
while (index_ != mlist_.lists_.size() &&
mlist_.lists_[index_]->size() == 0) {
++index_;
}
if (index_ == mlist_.lists_.size()) {
//we reached the end - we should get set to
//invalid index
index_ = std::numeric_limits<size_t>::max();
}
currIter_ = index_ == std::numeric_limits<size_t>::max()
? mlist_.lists_[0]->begin()
: mlist_.lists_[index_]->begin();
}

template <typename T, DListHook<T> T::*HookPtr>
typename MultiDList<T, HookPtr>::Iterator&
MultiDList<T, HookPtr>::Iterator::operator++() noexcept {
Expand All @@ -97,7 +130,16 @@ typename MultiDList<T, HookPtr>::Iterator MultiDList<T, HookPtr>::rbegin(
if (listIdx >= lists_.size()) {
throw std::invalid_argument("Invalid list index for MultiDList iterator.");
}
return MultiDList<T, HookPtr>::Iterator(*this, listIdx);
return MultiDList<T, HookPtr>::Iterator(*this, listIdx, false);
}

template <typename T, DListHook<T> T::*HookPtr>
typename MultiDList<T, HookPtr>::Iterator MultiDList<T, HookPtr>::begin(
size_t listIdx) const {
if (listIdx >= lists_.size()) {
throw std::invalid_argument("Invalid list index for MultiDList iterator.");
}
return MultiDList<T, HookPtr>::Iterator(*this, listIdx, true);
}

template <typename T, DListHook<T> T::*HookPtr>
Expand Down
16 changes: 13 additions & 3 deletions cachelib/allocator/datastruct/MultiDList.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,18 @@ class MultiDList {
}

explicit Iterator(const MultiDList<T, HookPtr>& mlist,
size_t listIdx) noexcept
size_t listIdx, bool head) noexcept
: currIter_(mlist.lists_[mlist.lists_.size() - 1]->rbegin()),
mlist_(mlist) {
XDCHECK_LT(listIdx, mlist.lists_.size());
initToValidRBeginFrom(listIdx);
if (head) {
initToValidBeginFrom(listIdx);
} else {
initToValidRBeginFrom(listIdx);
}
// We should either point to an element or the end() iterator
// which has an invalid index_.
XDCHECK(index_ == kInvalidIndex || currIter_.get() != nullptr);
XDCHECK(index_ == kInvalidIndex || index_ == mlist.lists_.size() || currIter_.get() != nullptr);
}
virtual ~Iterator() = default;

Expand Down Expand Up @@ -169,6 +173,9 @@ class MultiDList {

// reset iterator to the beginning of a speicific queue
void initToValidRBeginFrom(size_t listIdx) noexcept;

// reset iterator to the head of a specific queue
void initToValidBeginFrom(size_t listIdx) noexcept;

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

// provides an iterator starting from the tail of a specific list.
Iterator rbegin(size_t idx) const;

// provides an iterator starting from the head of a specific list.
Iterator begin(size_t idx) const;

// Iterator to compare against for the end.
Iterator rend() const noexcept;
Expand Down
33 changes: 33 additions & 0 deletions cachelib/allocator/tests/MM2QTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,19 @@ void MMTypeTest<MMType>::testIterate(std::vector<std::unique_ptr<Node>>& nodes,
}
}

template <typename MMType>
void MMTypeTest<MMType>::testIterateHot(std::vector<std::unique_ptr<Node>>& nodes,
Container& c) {
auto it = nodes.rbegin();
c.withPromotionIterator([&it,&c](auto &&it2q) {
while (it2q && c.isHot(*it2q)) {
ASSERT_EQ(it2q->getId(), (*it)->getId());
++it2q;
++it;
}
});
}

template <typename MMType>
void MMTypeTest<MMType>::testMatch(std::string expected,
MMTypeTest<MMType>::Container& c) {
Expand All @@ -234,6 +247,23 @@ void MMTypeTest<MMType>::testMatch(std::string expected,
ASSERT_EQ(expected, actual);
}

template <typename MMType>
void MMTypeTest<MMType>::testMatchHot(std::string expected,
MMTypeTest<MMType>::Container& c) {
int index = -1;
std::string actual;
c.withPromotionIterator([&c,&actual,&index](auto &&it2q) {
while (it2q) {
++index;
actual += folly::stringPrintf(
"%d:%s, ", it2q->getId(),
(c.isHot(*it2q) ? "H" : (c.isCold(*it2q) ? "C" : "W")));
++it2q;
}
});
ASSERT_EQ(expected, actual);
}

TEST_F(MM2QTest, DetailedTest) {
MM2Q::Config config;
config.lruRefreshTime = 0;
Expand All @@ -255,8 +285,11 @@ TEST_F(MM2QTest, DetailedTest) {
}

testIterate(nodes, c);
testIterateHot(nodes, c);

testMatch("0:C, 1:C, 2:C, 3:C, 4:H, 5:H, ", c);
testMatchHot("5:H, 4:H, 3:C, 2:C, 1:C, 0:C, ", c);

// Move 3 to top of the hot cache
c.recordAccess(*(nodes[4]), AccessMode::kRead);
testMatch("0:C, 1:C, 2:C, 3:C, 5:H, 4:H, ", c);
Expand Down
2 changes: 2 additions & 0 deletions cachelib/allocator/tests/MMTypeTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ class MMTypeTest : public testing::Test {
void testRecordAccessBasic(Config c);
void testSerializationBasic(Config c);
void testIterate(std::vector<std::unique_ptr<Node>>& nodes, Container& c);
void testIterateHot(std::vector<std::unique_ptr<Node>>& nodes, Container& c);
void testMatch(std::string expected, Container& c);
void testMatchHot(std::string expected, Container& c);
size_t getListSize(const Container& c, typename MMType::LruType list);
};

Expand Down