From 1fcd9061239b4e4578c788e07ed7278c6d4c397a Mon Sep 17 00:00:00 2001 From: Chameleon Cloud User Date: Fri, 6 Mar 2026 00:07:05 +0000 Subject: [PATCH] init adaptive marginal hits --- cachelib/allocator/CMakeLists.txt | 1 + cachelib/allocator/CacheAllocatorConfig.h | 2 +- .../allocator/MarginalHitsStrategyNew.cpp | 223 ++++++++++++++++++ cachelib/allocator/MarginalHitsStrategyNew.h | 142 +++++++++++ cachelib/allocator/RebalanceInfo.h | 48 +++- cachelib/allocator/RebalanceStrategy.cpp | 68 +++++- cachelib/allocator/RebalanceStrategy.h | 42 ++++ cachelib/cachebench/util/CacheConfig.cpp | 8 + 8 files changed, 530 insertions(+), 4 deletions(-) create mode 100644 cachelib/allocator/MarginalHitsStrategyNew.cpp create mode 100644 cachelib/allocator/MarginalHitsStrategyNew.h diff --git a/cachelib/allocator/CMakeLists.txt b/cachelib/allocator/CMakeLists.txt index aad79b0a52..603cfeeeec 100644 --- a/cachelib/allocator/CMakeLists.txt +++ b/cachelib/allocator/CMakeLists.txt @@ -49,6 +49,7 @@ add_library (cachelib_allocator LruTailAgeStrategy.cpp MarginalHitsOptimizeStrategy.cpp MarginalHitsStrategy.cpp + MarginalHitsStrategyNew.cpp memory/AllocationClass.cpp memory/MemoryAllocator.cpp memory/MemoryPool.cpp diff --git a/cachelib/allocator/CacheAllocatorConfig.h b/cachelib/allocator/CacheAllocatorConfig.h index da02939bb5..0120ee2d42 100644 --- a/cachelib/allocator/CacheAllocatorConfig.h +++ b/cachelib/allocator/CacheAllocatorConfig.h @@ -1170,7 +1170,7 @@ bool CacheAllocatorConfig::validateStrategy( auto type = strategy->getType(); return type != RebalanceStrategy::NumTypes && - (type != RebalanceStrategy::MarginalHits || trackTailHits); + ((type != RebalanceStrategy::MarginalHits && type != RebalanceStrategy::MarginalHitsNew) || trackTailHits); } template diff --git a/cachelib/allocator/MarginalHitsStrategyNew.cpp b/cachelib/allocator/MarginalHitsStrategyNew.cpp new file mode 100644 index 0000000000..036b5cf49f --- /dev/null +++ b/cachelib/allocator/MarginalHitsStrategyNew.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cachelib/allocator/MarginalHitsStrategyNew.h" + +#include + +#include +#include + +namespace facebook::cachelib { + +MarginalHitsStrategyNew::MarginalHitsStrategyNew(Config config) + : RebalanceStrategy(MarginalHits), config_(std::move(config)) {} + +RebalanceContext MarginalHitsStrategyNew::pickVictimAndReceiverImpl( + const CacheBase& cache, PoolId pid, const PoolStats& poolStats) { + return pickVictimAndReceiverCandidates(cache, pid, poolStats, false); +} + +RebalanceContext MarginalHitsStrategyNew::pickVictimAndReceiverCandidates( + const CacheBase& cache, PoolId pid, const PoolStats& poolStats, bool force) { + const auto config = getConfigCopy(); + if (!cache.getPool(pid).allSlabsAllocated()) { + XLOGF(DBG, + "Pool Id: {} does not have all its slabs allocated" + " and does not need rebalancing.", + static_cast(pid)); + return kNoOpContext; + } + + + auto scores = computeClassMarginalHits(pid, poolStats, config.movingAverageParam); + auto classesSet = poolStats.getClassIds(); + std::vector classes(classesSet.begin(), classesSet.end()); + std::unordered_map validVictim; + std::unordered_map validReceiver; + for (auto it : classes) { + auto acStats = poolStats.mpStats.acStats; + // a class can be a victim only if it has more than config.minSlabs slabs + validVictim[it] = acStats.at(it).totalSlabs() > config.minSlabs; + // a class can be a receiver only if its free memory (free allocs, free + // slabs, etc) is small + validReceiver[it] = acStats.at(it).getTotalFreeMemory() < + config.maxFreeMemSlabs * Slab::kSize; + } + if (classStates_[pid].entities.empty()) { + // initialization + classStates_[pid].entities = classes; + for (auto cid : classes) { + classStates_[pid].smoothedRanks[cid] = 0; + } + } + // we don't rely on this decay anymore + classStates_[pid].updateRankings(scores, 0.0); + RebalanceContext ctx = pickVictimAndReceiverFromRankings(pid, validVictim, validReceiver); + + auto numRequestObserved = computeNumRequests(pid, poolStats); + if(!force && numRequestObserved < config.minRequestsObserved) { + XLOGF(DBG, "haven't observed enough requests: {}/{}, wait until next round", numRequestObserved, config.minRequestsObserved); + ctx = kNoOpContext; + } + if(!force && ctx.isEffective()) { + //extra filterings + auto receiverScore = scores.at(ctx.receiverClassId); + auto victimScore = scores.at(ctx.victimClassId); + auto improvement = receiverScore - victimScore; + auto improvementRatio = improvement / (victimScore == 0 ? 1 : victimScore); + ctx.diffValue = improvement; + if ((config.minDiff > 0 && improvement < config.minDiff) || + (config.minDiffRatio > 0 && improvementRatio < config.minDiffRatio)){ + XLOGF(DBG, "Not enough to trigger rebalancing, receiver id: {}, victim id: {}, receiver score: {}, victim score: {}, improvement: {}, improvement ratio: {}, thresh1: {}, thresh2: {}", + ctx.receiverClassId, ctx.victimClassId, receiverScore, victimScore, improvement, improvementRatio, config.minDiff, config.minDiffRatio); + ctx = kNoOpContext; + } else { + XLOGF(DBG, "rebalancing, receiver id: {}, victim id: {}, receiver score: {}, victim score: {}, improvement: {}, improvement ratio: {}", + ctx.receiverClassId, ctx.victimClassId, receiverScore, victimScore, improvement, improvementRatio); + + } + } + + if(!ctx.isEffective()){ + ctx = kNoOpContext; + } + auto& poolState = getPoolState(pid); + auto deltaRequestsSinceLastDecay = computeRequestsSinceLastDecay(pid, poolStats); + if((ctx.isEffective() || !config.onlyUpdateHitIfRebalance) || deltaRequestsSinceLastDecay >= config.minDecayInterval) { + for (const auto i : poolStats.getClassIds()) { + poolState[i].updateTailHits(poolStats, config.movingAverageParam); + } + } + + if(numRequestObserved >= config.minRequestsObserved) { + for (const auto i : poolStats.getClassIds()) { + poolState[i].updateRequests(poolStats); + } + } + + // self-tuning threshold for the next round. + if(ctx.isEffective()){ + // max window size: 2 * n_classes + + size_t classWithHits = 0; + for (const auto& cid : classes) { + if (poolState.at(cid).deltaHits(poolStats) > 0) { + ++classWithHits; + } + } + recordRebalanceEvent(pid, ctx, classWithHits * 2); + auto effectiveMoveRate = queryEffectiveMoveRate(pid); + auto windowSize = getRebalanceEventQueueSize(pid); + XLOGF(DBG, + "Rebalancing: effective move rate = {}, window size = {}, diff = {}, threshold = {}, ({}->{})", + effectiveMoveRate, + windowSize, ctx.diffValue, config.minDiff, static_cast(ctx.victimClassId), static_cast(ctx.receiverClassId)); + + if(effectiveMoveRate <= config.emrLow && windowSize >= config.thresholdIncMinWindowSize) { + if(config.thresholdAI) { + auto currentMin = getMinDiffValueFromRebalanceEvents(pid); + if(updateMinDff(currentMin + config.thresholdAIADStep)) { + clearPoolRebalanceEvent(pid); + } + } else if (config.thresholdMI){ + if(updateMinDff(config.minDiff * config.thresholdMIMDFactor)) { + clearPoolRebalanceEvent(pid); + } + } + + } else if (effectiveMoveRate >= config.emrHigh && windowSize >= classWithHits) { + if(config.thresholdAD) { + if(updateMinDff(std::max(2.0, config.minDiff - config.thresholdAIADStep))) { + clearPoolRebalanceEvent(pid); + } + } else if (config.thresholdMD){ + if(updateMinDff(std::max(2.0, config.minDiff / config.thresholdMIMDFactor))) { + clearPoolRebalanceEvent(pid); + } + } + } + } + + return ctx; +} + +ClassId MarginalHitsStrategyNew::pickVictimImpl(const CacheBase& cache, + PoolId pid, + const PoolStats& stats) { + return pickVictimAndReceiverCandidates(cache, pid, stats, true).victimClassId; +} + +std::unordered_map +MarginalHitsStrategyNew::computeClassMarginalHits(PoolId pid, + const PoolStats& poolStats, + double decayFactor) { + const auto& poolState = getPoolState(pid); + std::unordered_map scores; + for (auto info : poolState) { + if (info.id != Slab::kInvalidClassId) { + // this score is the latest delta. + scores[info.id] = info.getDecayedMarginalHits(poolStats, decayFactor); + } + } + return scores; +} + +size_t MarginalHitsStrategyNew::computeNumRequests( + PoolId pid, const PoolStats& poolStats) const { + const auto& poolState = getPoolState(pid); + size_t totalRequests = 0; + auto classesSet = poolStats.getClassIds(); + for (const auto& cid : classesSet) { + totalRequests += poolState.at(cid).deltaRequests(poolStats); + } + return totalRequests; +} + +size_t MarginalHitsStrategyNew::computeRequestsSinceLastDecay( + PoolId pid, const PoolStats& poolStats) const { + const auto& poolState = getPoolState(pid); + size_t totalRequests = 0; + auto classesSet = poolStats.getClassIds(); + for (const auto& cid : classesSet) { + totalRequests += poolState.at(cid).deltaRequestsSinceLastDecay(poolStats); + } + return totalRequests; +} + +RebalanceContext MarginalHitsStrategyNew::pickVictimAndReceiverFromRankings( + PoolId pid, + const std::unordered_map& validVictim, + const std::unordered_map& validReceiver) { + auto victimAndReceiver = classStates_[pid].pickVictimAndReceiverFromRankings( + validVictim, validReceiver, Slab::kInvalidClassId); + RebalanceContext ctx{victimAndReceiver.first, victimAndReceiver.second}; + if (ctx.victimClassId == Slab::kInvalidClassId || + ctx.receiverClassId == Slab::kInvalidClassId || + ctx.victimClassId == ctx.receiverClassId) { + return kNoOpContext; + } + + XLOGF(DBG, + "Rebalancing: receiver = {}, smoothed rank = {}, victim = {}, smoothed " + "rank = {}", + static_cast(ctx.receiverClassId), + classStates_[pid].smoothedRanks[ctx.receiverClassId], + static_cast(ctx.victimClassId), + classStates_[pid].smoothedRanks[ctx.victimClassId]); + return ctx; +} +} // namespace facebook::cachelib \ No newline at end of file diff --git a/cachelib/allocator/MarginalHitsStrategyNew.h b/cachelib/allocator/MarginalHitsStrategyNew.h new file mode 100644 index 0000000000..5b87b9ee6d --- /dev/null +++ b/cachelib/allocator/MarginalHitsStrategyNew.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "cachelib/allocator/MarginalHitsState.h" +#include "cachelib/allocator/RebalanceStrategy.h" + +namespace facebook { +namespace cachelib { + +// This strategy computes number of hits in the tail slab of LRU to estimate +// the potential (given one more slab, how many more hits can this LRU serve). +// And use a smoothed ranking of those potentials to decide victim and receiver. +class MarginalHitsStrategyNew : public RebalanceStrategy { + public: + // Config class for marginal hits strategy + struct Config : public BaseConfig { + // parameter for moving average, to smooth the ranking + double movingAverageParam{0.3}; + + // minimum number of slabs to retain in every allocation class. + unsigned int minSlabs{1}; + + // maximum free memory (equivalent to this many slabs) in every allocation + // class + unsigned int maxFreeMemSlabs{1}; + + bool onlyUpdateHitIfRebalance{true}; + + // enforcing thresholds between the victim and receiver class + double minDiff{2.0}; + double minDiffRatio{0.00}; + + ////// these parameters are for controlling the threshold auto-tuning + unsigned int thresholdIncMinWindowSize{5}; + bool thresholdAI{true}; + bool thresholdMI{false}; + bool thresholdAD{false}; + bool thresholdMD{true}; + + double emrLow{0.5}; + double emrHigh{0.95}; + double thresholdAIADStep{2.0}; + double thresholdMIMDFactor{2.0}; + /////////////////////////// + + uint64_t minRequestsObserved{50000}; + uint64_t minDecayInterval{50000}; + + Config() noexcept {} + explicit Config(double param) noexcept : Config(param, 1, 1) {} + Config(double param, unsigned int minSlab, unsigned int maxFree) noexcept + : movingAverageParam(param), + minSlabs(minSlab), + maxFreeMemSlabs(maxFree) {} + }; + + // Update the config. This will not affect the current rebalancing, but + // will take effect in the next round + void updateConfig(const BaseConfig& baseConfig) override final { + std::lock_guard l(configLock_); + config_ = static_cast(baseConfig); + } + + bool updateMinDff(double newValue) { + if(config_.minDiff == newValue){ + return false; + } + std::lock_guard l(configLock_); + XLOGF(DBG, "marginal-hits, threshold auto-tuning, updating from {} to {}", config_.minDiff, newValue); + config_.minDiff = newValue; + return true; + } + + explicit MarginalHitsStrategyNew(Config config = {}); + + protected: + // This returns a copy of the current config. + // This ensures that we're always looking at the same config even though + // someone else may have updated the config during rebalancing + Config getConfigCopy() const { + std::lock_guard l(configLock_); + return config_; + } + + // pick victim and receiver classes from a pool + RebalanceContext pickVictimAndReceiverImpl( + const CacheBase& cache, + PoolId pid, + const PoolStats& poolStats) override final; + + // pick victim class from a pool to shrink + ClassId pickVictimImpl(const CacheBase& cache, + PoolId pid, + const PoolStats& poolStats) override final; + + size_t computeNumRequests(PoolId pid, const PoolStats& poolStats) const; + + size_t computeRequestsSinceLastDecay(PoolId pid, const PoolStats& poolStats) const; + + private: + // compute delta of tail hits for every class in this pool + std::unordered_map computeClassMarginalHits( + PoolId pid, const PoolStats& poolStats, double decayFactor); + + // pick victim and receiver according to smoothed rankings + RebalanceContext pickVictimAndReceiverFromRankings( + PoolId pid, + const std::unordered_map& validVictim, + const std::unordered_map& validReceiver); + + RebalanceContext pickVictimAndReceiverCandidates( + const CacheBase& cache, + PoolId pid, + const PoolStats& poolStats, + bool force); + + // marginal hits states for classes in each pools + std::unordered_map> classStates_; + + // Config for this strategy, this can be updated anytime. + // Do not access this directly, always use `getConfig()` to + // obtain a copy first + Config config_; + mutable std::mutex configLock_; +}; +} // namespace cachelib +} // namespace facebook \ No newline at end of file diff --git a/cachelib/allocator/RebalanceInfo.h b/cachelib/allocator/RebalanceInfo.h index a6288839f2..4c241b4028 100644 --- a/cachelib/allocator/RebalanceInfo.h +++ b/cachelib/allocator/RebalanceInfo.h @@ -47,6 +47,13 @@ struct Info { // accumulative number of hits in the tail slab of this allocation class uint64_t accuTailHits{0}; + double decayedAccuTailHits{0.0}; + + // accumalative number of requests seen for this allocation class + uint64_t numRequests{0}; + + uint64_t numRequestsAtLastDecay{0}; + // TODO(sugak) this is changed to unblock the LLVM upgrade The fix is not // completely understood, but it's a safe change T16521551 - Info() noexcept // = default; @@ -55,8 +62,11 @@ struct Info { unsigned long long slabs, unsigned long long evicts, uint64_t h, - uint64_t th) noexcept - : id(_id), nSlabs(slabs), evictions(evicts), hits(h), accuTailHits(th) {} + uint64_t th, + double dath, + uint64_t nr, + uint64_t nrld) noexcept + : id(_id), nSlabs(slabs), evictions(evicts), hits(h), accuTailHits(th), decayedAccuTailHits(dath), numRequests(nr), numRequestsAtLastDecay(nrld) {} // number of rounds we hold off for when we acquire a slab. static constexpr unsigned int kNumHoldOffRounds = 10; @@ -101,6 +111,22 @@ struct Info { return poolStats.numHitsForClass(id) - hits; } + uint64_t deltaRequests(const PoolStats& poolStats) const { + const auto& cacheStats = poolStats.cacheStats.at(id); + auto totalRequests = poolStats.numHitsForClass(id) + cacheStats.allocAttempts; + return totalRequests > numRequests + ? totalRequests - numRequests + : 0; + } + + uint64_t deltaRequestsSinceLastDecay(const PoolStats& poolStats) const { + const auto& cacheStats = poolStats.cacheStats.at(id); + auto totalRequests = poolStats.numHitsForClass(id) + cacheStats.allocAttempts; + return totalRequests > numRequestsAtLastDecay + ? totalRequests - numRequestsAtLastDecay + : 0; + } + // return the delta of alloc failures for this alloc class from the current // state. // @@ -144,6 +170,11 @@ struct Info { accuTailHits; } + double getDecayedMarginalHits(const PoolStats& poolStats, double decayFactor=0.0) const { + // decayed past + now + return decayedAccuTailHits + getMarginalHits(poolStats) * (1 - decayFactor); + } + // returns true if the hold off is active for this alloc class. bool isOnHoldOff() const noexcept { return holdOffRemaining > 0; } @@ -162,6 +193,19 @@ struct Info { hits = poolStats.numHitsForClass(id); } + void updateRequests(const PoolStats& poolStats) noexcept { + const auto& cacheStats = poolStats.cacheStats.at(id); + numRequests = poolStats.numHitsForClass(id) + cacheStats.allocAttempts; + } + + void updateTailHits(const PoolStats& poolStats, double decayFactor=0.0) noexcept { + const auto& cacheStats = poolStats.cacheStats.at(id); + decayedAccuTailHits = (decayedAccuTailHits + getMarginalHits(poolStats)) * decayFactor; + accuTailHits = cacheStats.containerStat.numTailAccesses; + numRequestsAtLastDecay = poolStats.numHitsForClass(id) + cacheStats.allocAttempts; + } + + // updates the current record to store the current state of slabs and the // evictions we see. void updateRecord(const PoolStats& poolStats) { diff --git a/cachelib/allocator/RebalanceStrategy.cpp b/cachelib/allocator/RebalanceStrategy.cpp index 2d02931ec6..9ea313f319 100644 --- a/cachelib/allocator/RebalanceStrategy.cpp +++ b/cachelib/allocator/RebalanceStrategy.cpp @@ -59,7 +59,10 @@ void RebalanceStrategy::initPoolState(PoolId pid, const PoolStats& stats) { curr[id] = Info{id, stats.mpStats.acStats.at(id).totalSlabs(), stats.cacheStats.at(id).numEvictions(), stats.numHitsForClass(id), - stats.cacheStats.at(id).containerStat.numTailAccesses}; + stats.cacheStats.at(id).containerStat.numTailAccesses, + 0, + stats.numHitsForClass(id) + stats.cacheStats.at(id).allocAttempts, + stats.numHitsForClass(id) + stats.cacheStats.at(id).allocAttempts}; // hits + allocs => nr of requests } } @@ -321,4 +324,67 @@ T RebalanceStrategy::executeAndRecordCurrentState( return rv; } +///// for keeping rebalance decision histories +void RebalanceStrategy::recordRebalanceEvent(PoolId pid, RebalanceContext ctx, size_t maxQueueSize) { + if(ctx.isEffective()) { + auto& eventQueue = recentRebalanceEvents_[pid]; + eventQueue.emplace_back(ctx); + if (eventQueue.size() > maxQueueSize) { + eventQueue.pop_front(); + } + } +} + +unsigned int RebalanceStrategy::getRebalanceEventQueueSize(PoolId pid) const { + const auto it = recentRebalanceEvents_.find(pid); + if (it == recentRebalanceEvents_.end()) { + return 0; + } + return it->second.size(); +} + +void RebalanceStrategy::clearPoolRebalanceEvent(PoolId pid) { + recentRebalanceEvents_.erase(pid); +} + +double RebalanceStrategy::queryEffectiveMoveRate(PoolId pid) const{ + const auto it = recentRebalanceEvents_.find(pid); + if (it == recentRebalanceEvents_.end() || it->second.empty()) { + return 1.0; + } + + const auto& events = it->second; + std::unordered_map netChanges; + + for (const auto& ctx : events) { + netChanges[ctx.victimClassId]--; + netChanges[ctx.receiverClassId]++; + } + + int currentAbsNet = 0; + for (const auto& [classId, net] : netChanges) { + currentAbsNet += std::abs(net); + } + int totalEffectiveMoves = currentAbsNet / 2; + + return static_cast(totalEffectiveMoves) / events.size(); +} + +double RebalanceStrategy::getMinDiffValueFromRebalanceEvents(PoolId pid) const { + const auto it = recentRebalanceEvents_.find(pid); + if (it == recentRebalanceEvents_.end() || it->second.empty()) { + return 0.0; // Return 0.0 if the queue is empty or the pool ID is not found + } + + const auto& events = it->second; + + // Find the minimum diffValue in the queue + double minDiffValue = std::numeric_limits::max(); + for (const auto& ctx : events) { + minDiffValue = std::min(minDiffValue, ctx.diffValue); + } + + return minDiffValue; +} + } // namespace facebook::cachelib diff --git a/cachelib/allocator/RebalanceStrategy.h b/cachelib/allocator/RebalanceStrategy.h index 07f0902354..15ddb8239d 100644 --- a/cachelib/allocator/RebalanceStrategy.h +++ b/cachelib/allocator/RebalanceStrategy.h @@ -28,9 +28,35 @@ struct RebalanceContext { ClassId victimClassId{Slab::kInvalidClassId}; ClassId receiverClassId{Slab::kInvalidClassId}; + // to support multiple pairs at a time + std::vector> victimReceiverPairs{}; + // tracking the diff between victim and receiver + double diffValue{0.0}; + RebalanceContext() = default; RebalanceContext(ClassId victim, ClassId receiver) : victimClassId(victim), receiverClassId(receiver) {} + + explicit RebalanceContext(const std::vector>& pairs) + : victimReceiverPairs(pairs) {} + + bool isEffective() const { + auto isPairEffective = [](ClassId victim, ClassId receiver) { + return victim != Slab::kInvalidClassId && + receiver != Slab::kInvalidClassId && + victim != receiver; + }; + + bool singleEffective = isPairEffective(victimClassId, receiverClassId); + + bool pairEffective = std::any_of( + victimReceiverPairs.begin(), victimReceiverPairs.end(), + [&](const std::pair& p) { + return isPairEffective(p.first, p.second); + }); + + return singleEffective || pairEffective; + } }; // Base class for rebalance strategy. @@ -48,6 +74,7 @@ class RebalanceStrategy { PickNothingOrTest = 0, Random, MarginalHits, + MarginalHitsNew, FreeMem, HitsPerSlab, LruTailAge, @@ -81,6 +108,17 @@ class RebalanceStrategy { virtual void updateConfig(const BaseConfig&) {} + + void recordRebalanceEvent(PoolId pid, RebalanceContext ctx, size_t maxQueueSize); + + double getMinDiffValueFromRebalanceEvents(PoolId pid) const; + + unsigned int getRebalanceEventQueueSize(PoolId pid) const; + + void clearPoolRebalanceEvent(PoolId pid); + + double queryEffectiveMoveRate(PoolId pid) const; + Type getType() const { return type_; } std::string getTypeString() const { @@ -91,6 +129,8 @@ class RebalanceStrategy { return "Random"; case MarginalHits: return "MarginalHits"; + case MarginalHitsNew: + return "MarginalHitsNew"; case FreeMem: return "FreeMem"; case HitsPerSlab: @@ -192,6 +232,8 @@ class RebalanceStrategy { size_t threshold, const PoolState& prevState); + std::unordered_map> recentRebalanceEvents_; + private: // picks any of the class id ordered by the total slabs. ClassId pickAnyClassIdForResizing(const CacheBase& cache, diff --git a/cachelib/cachebench/util/CacheConfig.cpp b/cachelib/cachebench/util/CacheConfig.cpp index 6d8f40874b..2e79f9bf25 100644 --- a/cachelib/cachebench/util/CacheConfig.cpp +++ b/cachelib/cachebench/util/CacheConfig.cpp @@ -17,6 +17,8 @@ #include "cachelib/cachebench/util/CacheConfig.h" #include "cachelib/allocator/HitsPerSlabStrategy.h" +#include "cachelib/allocator/MarginalHitsStrategyNew.h" +#include "cachelib/allocator/MarginalHitsStrategy.h" #include "cachelib/allocator/LruTailAgeStrategy.h" #include "cachelib/allocator/RandomStrategy.h" @@ -135,6 +137,12 @@ std::shared_ptr CacheConfig::getRebalanceStrategy() const { auto config = HitsPerSlabStrategy::Config{ rebalanceDiffRatio, static_cast(rebalanceMinSlabs)}; return std::make_shared(config); + } else if (rebalanceStrategy == "marginal-hits") { + return std::make_shared( + MarginalHitsStrategy::Config{}); + } else if (rebalanceStrategy == "marginal-hits-new") { + return std::make_shared( + MarginalHitsStrategyNew::Config{}); } else { // use random strategy to just trigger some slab release. return std::make_shared(