Skip to content

Commit 3c48024

Browse files
add bulk reinsertion
1 parent 2924c81 commit 3c48024

2 files changed

Lines changed: 45 additions & 13 deletions

File tree

cachelib/allocator/MMS3FIFO.h

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ void MMS3FIFO::Container<T, HookPtr>::lazyPromoteSmallTailLocked()
409409
auto& smallList = lru_.getList(LruType::Small);
410410

411411
// Only process Small when it exceeds target size.
412+
// At stable state, this op is constant time.
412413
while (smallList.size() > targetSmallSize) {
413414
auto* tail = smallList.getTail();
414415
if (!tail) {
@@ -430,20 +431,24 @@ template <typename T, MMS3FIFO::Hook<T> T::* HookPtr>
430431
void MMS3FIFO::Container<T, HookPtr>::lazyReinsertMainTailLocked()
431432
const noexcept {
432433
auto& mainList = lru_.getList(LruType::Main);
433-
// Cap at mainList.size() to guarantee termination — after one full
434-
// pass all access bits are cleared.
435-
size_t maxReinsertions = mainList.size();
436-
size_t reinserted = 0;
437-
while (reinserted < maxReinsertions) {
438-
auto* tail = mainList.getTail();
439-
if (!tail || !isAccessed(*tail)) {
440-
break;
441-
}
442-
mainList.remove(*tail);
443-
mainList.linkAtHead(*tail);
444-
unmarkAccessed(*tail);
445-
++reinserted;
434+
auto* tail = mainList.getTail();
435+
if (tail == nullptr || !isAccessed(*tail)) {
436+
return;
446437
}
438+
439+
auto* cur = tail;
440+
auto* first = tail;
441+
442+
// Find the contiguous accessed suffix at Main tail and clear access bits.
443+
while (cur && isAccessed(*cur)) {
444+
unmarkAccessed(*cur);
445+
first = cur;
446+
cur = mainList.getPrev(*cur);
447+
}
448+
449+
// Move that entire suffix to head in one splice.
450+
// Reinsertion is expensive in a workload with high hit ratio.
451+
mainList.moveSuffixToHead(*first);
447452
}
448453

449454
template <typename T, MMS3FIFO::Hook<T> T::* HookPtr>

cachelib/allocator/datastruct/DList.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,12 @@ class DList {
173173
// list.
174174
void moveToHead(T& node) noexcept;
175175

176+
// Splices the contiguous suffix [first, ..., tail_] to the head of the
177+
// list in O(1). After the call, `first` is the new head and the node
178+
// previously preceding `first` becomes the new tail.
179+
// @param first the leftmost node of the suffix to move; must be in the list
180+
void moveSuffixToHead(T& first) noexcept;
181+
176182
T* getHead() const noexcept { return head_; }
177183
T* getTail() const noexcept { return tail_; }
178184

@@ -393,6 +399,27 @@ void DList<T, HookPtr>::moveToHead(T& node) noexcept {
393399
linkAtHead(node);
394400
}
395401

402+
template <typename T, DListHook<T> T::*HookPtr>
403+
void DList<T, HookPtr>::moveSuffixToHead(T& first) noexcept {
404+
if (&first == head_) {
405+
return;
406+
}
407+
408+
T* const newTail = getPrev(first);
409+
XDCHECK(newTail != nullptr);
410+
411+
// Detach suffix [first, ..., tail_] from the prefix.
412+
setNext(*newTail, nullptr);
413+
414+
// Splice the suffix in front of the old head.
415+
setNext(*tail_, head_);
416+
setPrev(*head_, tail_);
417+
setPrev(first, nullptr);
418+
419+
head_ = &first;
420+
tail_ = newTail;
421+
}
422+
396423
/* Iterator Implementation */
397424
template <typename T, DListHook<T> T::*HookPtr>
398425
void DList<T, HookPtr>::Iterator::goForward() noexcept {

0 commit comments

Comments
 (0)