cachelib/allocator/MarginalHitsOptimizeStrategy.cpp (121 lines of code) (raw):
/*
* Copyright (c) Facebook, Inc. and its 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/MarginalHitsOptimizeStrategy.h"
#include <folly/logging/xlog.h>
#include <algorithm>
#include <functional>
#include "cachelib/allocator/Util.h"
namespace facebook {
namespace cachelib {
PoolOptimizeContext
MarginalHitsOptimizeStrategy::pickVictimAndReceiverFromRankings(
const MarginalHitsState<PoolId>& state,
const std::unordered_map<PoolId, bool>& validVictim,
const std::unordered_map<PoolId, bool>& validReceiver) {
auto victimAndReceiver = state.pickVictimAndReceiverFromRankings(
validVictim, validReceiver, Slab::kInvalidPoolId);
PoolOptimizeContext ctx{victimAndReceiver.first, victimAndReceiver.second};
if (ctx.victimPoolId == Slab::kInvalidPoolId ||
ctx.receiverPoolId == Slab::kInvalidPoolId ||
ctx.victimPoolId == ctx.receiverPoolId) {
return kNoOpContext;
}
XLOGF(DBG,
"Optimizing: receiver = {}, smoothed rank = {}, victim = {}, smoothed "
"rank = {}",
static_cast<int>(ctx.receiverPoolId),
state.smoothedRanks.at(ctx.receiverPoolId),
static_cast<int>(ctx.victimPoolId),
state.smoothedRanks.at(ctx.victimPoolId));
return ctx;
}
std::unordered_map<ClassId, uint64_t>
MarginalHitsOptimizeStrategy::getTailHitsAndUpdate(const PoolStats& poolStats,
PoolId pid) {
std::unordered_map<ClassId, uint64_t> tailHits;
const auto& cacheStats = poolStats.cacheStats;
for (auto it : accuTailHitsRegularPool[pid]) {
XDCHECK(cacheStats.find(it.first) != cacheStats.end());
tailHits[it.first] =
cacheStats.at(it.first).containerStat.numTailAccesses - it.second;
it.second = cacheStats.at(it.first).containerStat.numTailAccesses;
}
return tailHits;
}
uint64_t MarginalHitsOptimizeStrategy::getTailHitsAndUpdate(
const CCacheStats& stats, PoolId pid) {
uint64_t tailHits = stats.tailHits - accuTailHitsCompactCache[pid];
accuTailHitsCompactCache[pid] = stats.tailHits;
return tailHits;
}
PoolOptimizeContext
MarginalHitsOptimizeStrategy::pickVictimAndReceiverRegularPoolsImpl(
const CacheBase& cache) {
const auto config = getConfigCopy();
std::unordered_map<PoolId, double> scores;
std::unordered_map<PoolId, bool> validVictim;
std::unordered_map<PoolId, bool> validReceiver;
// If no data is stored yet, initialize stats and return nothing
if (regularPoolState_.entities.empty()) {
auto regularPools = cache.getRegularPoolIds();
for (auto pid : regularPools) {
if (cache.autoResizeEnabledForPool(pid)) {
regularPoolState_.entities.push_back(pid);
regularPoolState_.smoothedRanks[pid] = 0;
auto poolStats = cache.getPoolStats(pid);
for (auto cid : poolStats.getClassIds()) {
accuTailHitsRegularPool[pid][cid] =
poolStats.cacheStats.at(cid).containerStat.numTailAccesses;
}
}
}
return kNoOpContext;
}
for (auto pid : regularPoolState_.entities) {
const auto poolStats = cache.getPoolStats(pid);
auto classScores = getTailHitsAndUpdate(poolStats, pid);
uint64_t score = 0;
for (auto it : classScores) {
score = std::max(score, it.second);
}
scores[pid] = score;
validVictim[pid] =
poolStats.numEvictions() > 0 &&
poolStats.poolSize > config.poolMinSizeSlabs * Slab::kSize;
validReceiver[pid] =
poolStats.mpStats.freeMemory() < config.poolMaxFreeSlabs * Slab::kSize;
}
regularPoolState_.updateRankings(scores, config.movingAverageParam);
return pickVictimAndReceiverFromRankings(
regularPoolState_, validVictim, validReceiver);
}
PoolOptimizeContext
MarginalHitsOptimizeStrategy::pickVictimAndReceiverCompactCachesImpl(
const CacheBase& cache) {
const auto config = getConfigCopy();
std::unordered_map<PoolId, double> scores;
std::unordered_map<PoolId, bool> validVictim;
std::unordered_map<PoolId, bool> validReceiver;
// If no data is stored yet, initialize stats and return nothing
if (compactCacheState_.entities.empty()) {
for (const auto pid : cache.getCCachePoolIds()) {
if (cache.autoResizeEnabledForPool(pid)) {
compactCacheState_.entities.push_back(pid);
compactCacheState_.smoothedRanks[pid] = 0;
accuTailHitsCompactCache[pid] =
cache.getCompactCache(pid).getStats().tailHits;
}
}
return kNoOpContext;
}
for (auto pid : compactCacheState_.entities) {
const auto& ccache = cache.getCompactCache(pid);
scores[pid] = getTailHitsAndUpdate(ccache.getStats(), pid);
validVictim[pid] = ccache.getSize() > Slab::kSize * config.poolMinSizeSlabs;
validReceiver[pid] =
ccache.getConfiguredSize() > 0 &&
ccache.getSize() + Slab::kSize * config.poolMaxFreeSlabs >
ccache.getConfiguredSize();
}
compactCacheState_.updateRankings(scores, config.movingAverageParam);
return pickVictimAndReceiverFromRankings(
compactCacheState_, validVictim, validReceiver);
}
} // namespace cachelib
} // namespace facebook