Skip to content

Commit d84008c

Browse files
committed
update before rebase
1 parent 0883a5a commit d84008c

File tree

10 files changed

+941
-2
lines changed

10 files changed

+941
-2
lines changed

BUILD.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,10 @@ Use the `-O` option to skip the installation of system packages, e.g.
243243
As all dependencies use `git/cmake/make`, rebuilding the same code (if there
244244
were no updates) will be very fast.
245245
246+
Note - fmt library might fail a static assert - in this case it is recommended
247+
to use commit c5dce1caf9fc8dff4e309683252fee5fbc0b6c2b from the fmt repository
248+
with the following command in the cachelib/external/fmt directory:
249+
`git reset --hard c5dce1caf9fc8dff4e309683252fee5fbc0b6c2b`
246250
247251
## Downloading the source code without building
248252

cachelib/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ install(
314314
PATTERN "CMakeFiles" EXCLUDE
315315
)
316316

317-
317+
include(sanitizers)
318318
# Install CMake package configuration files for cachelib
319319
# TODO: @ONLY?
320320
include(CMakePackageConfigHelpers)
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Copyright (c) Intel and its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "cachelib/allocator/BackgroundEvictor.h"
18+
19+
#include <folly/logging/xlog.h>
20+
21+
#include <stdexcept>
22+
#include <thread>
23+
24+
namespace facebook {
25+
namespace cachelib {
26+
27+
BackgroundEvictor::BackgroundEvictor(CacheBase& cache,
28+
double targetFree,
29+
unsigned int tid)
30+
: cache_(cache),
31+
targetFree_(targetFree),
32+
tid_(tid) {
33+
34+
}
35+
36+
BackgroundEvictor::~BackgroundEvictor() { stop(std::chrono::seconds(0)); }
37+
38+
void BackgroundEvictor::work() {
39+
try {
40+
for (const auto pid : cache_.getRegularPoolIds()) {
41+
//check if we exceed threshold (per class basis static right now)
42+
checkAndRun(pid);
43+
}
44+
} catch (const std::exception& ex) {
45+
XLOGF(ERR, "BackgroundEvictor interrupted due to exception: {}", ex.what());
46+
}
47+
}
48+
49+
50+
// Look for classes that exceed the target memory capacity
51+
// and return those for eviction
52+
std::vector<ClassId> BackgrounEvictor::checkAndRun(PoolId pid) const {
53+
std::vector<ClassId> classesToEvict;
54+
const auto& mpStats = cache_.getPool(pid).getStats();
55+
for (auto& id : mpStats.classIds) {
56+
size_t totalMem = mpStats.acStats.at(id,tid_).getTotalMemory();
57+
size_t freeMem = mpStats.acStats.at(id,tid_).getTotalFreeMemory();
58+
double currFree = (double)freeMem/(double)totalMem;
59+
60+
if (currFree < targetFree_) {
61+
size_t targetMem = (targetFree_ * totalMem) - freeMem;
62+
size_t batch = (targetMem / mpStats.acStats.at(id,tid_).allocSize);
63+
//try evicting BATCH items from the class in order to reach free target
64+
tryEvicting(pid,cid,batch);
65+
}
66+
}
67+
}
68+
69+
//
70+
//try evicting a batch of items from a class that is over memory target
71+
//
72+
void BackgroundEvictor::tryEvicting(PoolId pid, ClassId cid, size_t batch) {
73+
auto& mmContainter = getMMContainer(tid_,pid,cid);
74+
// Keep searching for a candidate until we were able to evict it
75+
// or until the search limit has been exhausted
76+
unsigned int evictions = 0;
77+
auto itr = mmContainer.getEvictionIterator();
78+
while (evictions < batch && itr) {
79+
80+
Item* candidate = itr.get();
81+
// for chained items, the ownership of the parent can change. We try to
82+
// evict what we think as parent and see if the eviction of parent
83+
// recycles the child we intend to.
84+
85+
ItemHandle toReleaseHandle = tryEvictToNextMemoryTier(tid_, pid, itr);
86+
bool movedToNextTier = false;
87+
if(toReleaseHandle) {
88+
movedToNextTier = true;
89+
} else {
90+
toReleaseHandle =
91+
itr->isChainedItem()
92+
? advanceIteratorAndTryEvictChainedItem(tid_, pid, itr)
93+
: advanceIteratorAndTryEvictRegularItem(tid_, pid, mmContainer, itr);
94+
}
95+
96+
if (toReleaseHandle) {
97+
if (toReleaseHandle->hasChainedItem()) {
98+
(*stats_.chainedItemEvictions)[pid][cid].inc();
99+
} else {
100+
(*stats_.regularItemEvictions)[pid][cid].inc();
101+
}
102+
++evictions;
103+
104+
105+
// we must be the last handle and for chained items, this will be
106+
// the parent.
107+
XDCHECK(toReleaseHandle.get() == candidate || candidate->isChainedItem());
108+
XDCHECK_EQ(1u, toReleaseHandle->getRefCount());
109+
110+
// We manually release the item here because we don't want to
111+
// invoke the Item Handle's destructor which will be decrementing
112+
// an already zero refcount, which will throw exception
113+
auto& itemToRelease = *toReleaseHandle.release();
114+
115+
// Decrementing the refcount because we want to recycle the item
116+
const auto ref = decRef(itemToRelease);
117+
XDCHECK_EQ(0u, ref);
118+
119+
// check if by releasing the item we intend to, we actually
120+
// recycle the candidate.
121+
releaseBackToAllocator(itemToRelease, RemoveContext::kEviction,
122+
/* isNascent */ movedToNextTier, candidate);
123+
124+
}
125+
126+
// If we destroyed the itr to possibly evict and failed, we restart
127+
// from the beginning again
128+
if (!itr) {
129+
itr.resetToBegin();
130+
}
131+
}
132+
// Invalidate iterator since later on we may use this mmContainer
133+
// again, which cannot be done unless we drop this iterator
134+
itr.destroy();
135+
136+
}
137+
138+
} // namespace cachelib
139+
} // namespace facebook
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (c) Intel and its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <gtest/gtest_prod.h>
20+
21+
#include "cachelib/allocator/Cache.h"
22+
#include "cachelib/allocator/CacheStats.h"
23+
#include "cachelib/common/PeriodicWorker.h"
24+
25+
namespace facebook {
26+
namespace cachelib {
27+
28+
// Periodic worker that evicts items from tiers in batches
29+
// The primary aim is to reduce insertion times for new items in the
30+
// cache
31+
class BackgroundEvictor : public PeriodicWorker {
32+
public:
33+
// @param cache the cache interface
34+
// @param target_free the target amount of memory to keep free in
35+
// this tier
36+
// @param tier id memory tier to perform eviction on
37+
BackgroundEvictor(CacheBase& cache,
38+
double targetFree
39+
unsigned int tid);
40+
41+
~BackgroundEvictor() override;
42+
43+
44+
private:
45+
// cache allocator's interface for evicting
46+
CacheBase& cache_;
47+
double targetFree_;
48+
unsigned int tid_;
49+
50+
// implements the actual logic of running the background evictor
51+
void work() final;
52+
};
53+
} // namespace cachelib
54+
} // namespace facebook
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
namespace facebook {
18+
namespace cachelib {
19+
namespace detail {
20+
template <typename T>
21+
bool areBytesSame(const T& one, const T& two) {
22+
return std::memcmp(&one, &two, sizeof(T)) == 0;
23+
}
24+
} // namespace detail
25+
26+
/* Container Interface Implementation */
27+
template <typename T, MMLruApproxApprox::Hook<T> T::*HookPtr>
28+
MMLruApprox::Container<T, HookPtr>::Container(serialization::MMLruApproxObject object,
29+
PtrCompressor compressor)
30+
: compressor_(std::move(compressor)),
31+
lru_(*object.lru_ref(), compressor_),
32+
insertionPoint_(compressor_.unCompress(
33+
CompressedPtr{*object.compressedInsertionPoint_ref()})),
34+
tailSize_(*object.tailSize_ref()),
35+
config_(*object.config_ref()) {
36+
lruRefreshTime_ = config_.lruRefreshTime;
37+
nextReconfigureTime_ = config_.mmReconfigureIntervalSecs.count() == 0
38+
? std::numeric_limits<Time>::max()
39+
: static_cast<Time>(util::getCurrentTimeSec()) +
40+
config_.mmReconfigureIntervalSecs.count();
41+
}
42+
43+
template <typename T, MMLruApprox::Hook<T> T::*HookPtr>
44+
bool MMLruApprox::Container<T, HookPtr>::recordAccess(T& node,
45+
AccessMode mode) noexcept {
46+
if ((mode == AccessMode::kWrite && !config_.updateOnWrite) ||
47+
(mode == AccessMode::kRead && !config_.updateOnRead)) {
48+
return false;
49+
}
50+
51+
const auto curr = static_cast<Time>(util::getCurrentTimeSec());
52+
// check if the node is still being memory managed
53+
if (node.isInMMContainer() &&
54+
((curr >= getUpdateTime(node) +
55+
lruRefreshTime_.load(std::memory_order_relaxed)) ||
56+
!isAccessed(node))) {
57+
if (!isAccessed(node)) {
58+
markAccessed(node);
59+
}
60+
61+
auto func = [this, &node, curr]() {
62+
if (node.isInMMContainer()) {
63+
setUpdateTime(node, curr);
64+
}
65+
};
66+
67+
return true;
68+
}
69+
return false;
70+
}
71+
72+
template <typename T, MMLruApprox::Hook<T> T::*HookPtr>
73+
cachelib::EvictionAgeStat MMLruApprox::Container<T, HookPtr>::getEvictionAgeStat(
74+
uint64_t projectedLen) const noexcept {
75+
}
76+
77+
template <typename T, MMLruApprox::Hook<T> T::*HookPtr>
78+
cachelib::EvictionAgeStat
79+
MMLruApprox::Container<T, HookPtr>::getEvictionAgeStatLocked(
80+
uint64_t projectedLength) const noexcept {
81+
EvictionAgeStat stat{};
82+
const auto currTime = static_cast<Time>(util::getCurrentTimeSec());
83+
}
84+
85+
template <typename T, MMLruApprox::Hook<T> T::*HookPtr>
86+
void MMLruApprox::Container<T, HookPtr>::setConfig(const Config& newConfig) {
87+
}
88+
89+
template <typename T, MMLruApprox::Hook<T> T::*HookPtr>
90+
typename MMLruApprox::Config MMLruApprox::Container<T, HookPtr>::getConfig() const {
91+
return;
92+
}
93+
94+
//template <typename T, MMLruApprox::Hook<T> T::*HookPtr>
95+
//bool MMLruApprox::Container<T, HookPtr>::add(T& node) noexcept {
96+
// const auto currTime = static_cast<Time>(util::getCurrentTimeSec());
97+
//
98+
// if (node.isInMMContainer()) {
99+
// return false;
100+
// }
101+
// node.markInMMContainer();
102+
// setUpdateTime(node, currTime);
103+
// unmarkAccessed(node);
104+
// return true;
105+
//}
106+
107+
template <typename T, MMLruApprox::Hook<T> T::*HookPtr>
108+
typename MMLruApprox::Container<T, HookPtr>::Iterator
109+
MMLruApprox::Container<T, HookPtr>::getEvictionIterator() const noexcept {
110+
//LockHolder l(*lruMutex_);
111+
//return Iterator{std::move(l), lru_.rbegin()};
112+
//generate a set of candidates for eviction
113+
114+
}
115+
116+
117+
template <typename T, MMLruApprox::Hook<T> T::*HookPtr>
118+
serialization::MMLruApproxObject MMLruApprox::Container<T, HookPtr>::saveState()
119+
const noexcept {
120+
serialization::MMLruApproxConfig configObject;
121+
*configObject.lruRefreshTime_ref() =
122+
lruRefreshTime_.load(std::memory_order_relaxed);
123+
*configObject.lruRefreshRatio_ref() = config_.lruRefreshRatio;
124+
*configObject.updateOnWrite_ref() = config_.updateOnWrite;
125+
*configObject.updateOnRead_ref() = config_.updateOnRead;
126+
*configObject.tryLockUpdate_ref() = config_.tryLockUpdate;
127+
*configObject.lruInsertionPointSpec_ref() = config_.lruInsertionPointSpec;
128+
129+
serialization::MMLruApproxObject object;
130+
*object.config_ref() = configObject;
131+
*object.compressedInsertionPoint_ref() =
132+
compressor_.compress(insertionPoint_).saveState();
133+
*object.tailSize_ref() = tailSize_;
134+
*object.lru_ref() = lru_.saveState();
135+
return object;
136+
}
137+
138+
template <typename T, MMLruApprox::Hook<T> T::*HookPtr>
139+
MMContainerStat MMLruApprox::Container<T, HookPtr>::getStats() const noexcept {
140+
}
141+
142+
143+
// Iterator Context Implementation
144+
template <typename T, MMLruApprox::Hook<T> T::*HookPtr>
145+
MMLruApprox::Container<T, HookPtr>::Iterator::Iterator(
146+
LockHolder l, const typename LruList::Iterator& iter) noexcept
147+
: LruList::Iterator(iter), l_(std::move(l)) {}
148+
} // namespace cachelib
149+
} // namespace facebook

0 commit comments

Comments
 (0)