3333#include " cachelib/allocator/memory/serialize/gen-cpp2/objects_types.h"
3434#include " cachelib/common/CompilerUtils.h"
3535#include " cachelib/common/FIFOHashSet.h"
36+ #include " cachelib/common/FIFOConcurrentHashSet.h"
3637
3738namespace facebook ::cachelib {
3839
@@ -112,8 +113,6 @@ class MMS3FIFO {
112113
113114 // The size of ghost queue, as a percentage of total size
114115 size_t ghostSizePercent{90 };
115-
116- size_t reserveCapacity{0 };
117116 };
118117
119118 // The container object which can be used to keep track of objects of type
@@ -138,9 +137,6 @@ class MMS3FIFO {
138137 Container (Config c, PtrCompressor compressor)
139138 : lru_(LruType::NumTypes, std::move(compressor)),
140139 config_ (std::move(c)) {
141- if (config_.reserveCapacity > 0 ) {
142- ghostQueue_.reserve (config_.reserveCapacity );
143- }
144140 }
145141 Container (serialization::MMS3FIFOObject object, PtrCompressor compressor);
146142
@@ -216,7 +212,7 @@ class MMS3FIFO {
216212 ++it;
217213 // Skip accessed items
218214 skipAccessed (it);
219- return *this ;
215+ return *this ;
220216 }
221217
222218 ListIterator& skipAccessed (ListIterator& it) noexcept {
@@ -389,10 +385,15 @@ class MMS3FIFO {
389385 void maybeResizeGhostLocked () noexcept ;
390386
391387 // Returns the hash of node's key
392- static size_t hashNode (const T& node) noexcept {
388+ static size_t hashNode64 (const T& node) noexcept {
393389 return folly::hasher<folly::StringPiece>()(node.getKey ());
394390 }
395391
392+ static uint32_t hashNode (const T& node) noexcept {
393+ return static_cast <uint32_t >(
394+ folly::hasher<folly::StringPiece>()(node.getKey ()));
395+ }
396+
396397 void removeLocked (T& node) noexcept ;
397398
398399 static bool isTiny (const T& node) noexcept {
@@ -432,7 +433,8 @@ class MMS3FIFO {
432433 // the lru
433434 LruList lru_;
434435
435- facebook::cachelib::util::FIFOHashSet ghostQueue_;
436+ facebook::cachelib::util::FIFOConcurrentHashSet32 ghostQueue_;
437+ // facebook::cachelib::util::FIFOHashSet32 ghostQueue_;
436438
437439 // Config for this lru.
438440 // Write access to the MMS3FIFO Config is serialized.
@@ -483,7 +485,7 @@ bool MMS3FIFO::Container<T, HookPtr>::recordAccess(T& node,
483485 }
484486 const auto currTime = static_cast <Time>(util::getCurrentTimeSec ());
485487
486- // Remove lock from record access wince we only set bits
488+ // Remove lock from record access since we only set bits
487489 if (node.isInMMContainer ()) {
488490 if (!isAccessed (node)) {
489491 markAccessed (node);
@@ -538,12 +540,12 @@ bool MMS3FIFO::Container<T, HookPtr>::add(T& node) noexcept {
538540 const auto currTime = static_cast <Time>(util::getCurrentTimeSec ());
539541
540542 const auto nodeHash = hashNode (node);
541- return lruMutex_->lock_combine ([this , &node, currTime, nodeHash]() {
543+ const auto ghostContains = ghostQueue_.contains (nodeHash);
544+ return lruMutex_->lock_combine ([this , &node, currTime, ghostContains]() {
542545 if (node.isInMMContainer ()) {
543546 return false ;
544547 }
545548
546- const auto ghostContains = ghostQueue_.contains (nodeHash);
547549 if (ghostContains) {
548550 // Insert to main queue
549551 auto & mainLru = lru_.getList (LruType::Main);
@@ -625,6 +627,7 @@ template <typename T, MMS3FIFO::Hook<T> T::* HookPtr>
625627typename MMS3FIFO::Container<T, HookPtr>::LockedIterator
626628MMS3FIFO::Container<T, HookPtr>::getEvictionIterator() noexcept {
627629 LockHolder l (*lruMutex_);
630+ // This is cheap
628631 maybeResizeGhostLocked ();
629632 rebalanceForEviction ();
630633 // Cache is full now so we know it's max size
@@ -643,13 +646,12 @@ void MMS3FIFO::Container<T, HookPtr>::withContainerLock(F&& fun) {
643646 lruMutex_->lock_combine ([&fun]() { fun (); });
644647}
645648
649+ // Ghost queue insertion done on callee, outside lock
646650template <typename T, MMS3FIFO::Hook<T> T::* HookPtr>
647651void MMS3FIFO::Container<T, HookPtr>::removeLocked(T& node) noexcept {
648652 if (isTiny (node)) {
649653 lru_.getList (LruType::Tiny).remove (node);
650654 unmarkTiny (node);
651- // Insert into ghost queue upon eviction from tiny queue
652- ghostQueue_.insert (hashNode (node));
653655 } else {
654656 lru_.getList (LruType::Main).remove (node);
655657 }
@@ -661,21 +663,51 @@ void MMS3FIFO::Container<T, HookPtr>::removeLocked(T& node) noexcept {
661663
662664template <typename T, MMS3FIFO::Hook<T> T::* HookPtr>
663665bool MMS3FIFO::Container<T, HookPtr>::remove(T& node) noexcept {
664- return lruMutex_->lock_combine ([this , &node]() {
666+ bool isTiny_ = isTiny (node);
667+ auto result = lruMutex_->lock_combine ([this , &node]() {
665668 if (!node.isInMMContainer ()) {
666669 return false ;
667670 }
668671 removeLocked (node);
669672 return true ;
670673 });
674+ if (result && isTiny_) {
675+ // Insert to ghost queue
676+ ghostQueue_.insert (hashNode (node));
677+ }
678+ return result;
671679}
672680
673681template <typename T, MMS3FIFO::Hook<T> T::* HookPtr>
674682void MMS3FIFO::Container<T, HookPtr>::remove(LockedIterator& it) noexcept {
675683 T& node = *it;
676684 XDCHECK (node.isInMMContainer ());
677- ++it;
678- removeLocked (node);
685+ ++it;
686+
687+ bool evictedFromTiny = false ;
688+ if (isTiny (node)) {
689+ lru_.getList (LruType::Tiny).remove (node);
690+ unmarkTiny (node);
691+ evictedFromTiny = true ;
692+ // Insert into ghost queue upon eviction from tiny queue
693+ } else {
694+ lru_.getList (LruType::Main).remove (node);
695+ }
696+
697+ unmarkAccessed (node);
698+ node.unmarkInMMContainer ();
699+
700+ if (evictedFromTiny) {
701+ // We need to insert to ghost queue.
702+ // Release lock early
703+ // AFAIK every call to remove(locked itr) is followed by the iterator's destruction.
704+ // If this is not the case, we may need to rethink this.
705+ if (it.l_ .owns_lock ()) {
706+ it.l_ .unlock ();
707+ }
708+ ghostQueue_.insert (hashNode (node));
709+ }
710+ return ;
679711}
680712
681713template <typename T, MMS3FIFO::Hook<T> T::* HookPtr>
0 commit comments