Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a93f01f
Run centos and debian workflows on push and PR
igchor Nov 2, 2021
2a8fa60
Adds createPutToken and switches findEviction
byrnedj Feb 4, 2023
c3a4db9
Add memory usage statistics for allocation classes
igchor Jul 6, 2022
2529f0a
Initial multi-tier support implementation (rebased with NUMA and cs p…
igchor Sep 28, 2021
3cc41bd
AC stats multi-tier
byrnedj Jan 17, 2023
bf4c244
This commit contains the additional memory tiers tests
byrnedj Feb 8, 2023
c432df6
This is the additional multi-tier support needed
guptask Nov 14, 2022
4cefc44
added per pool class rolling average latency (upstream PR version)
guptask Jul 21, 2022
1f62a63
added per tier pool class rolling average latency (based on upstream PR)
guptask Jul 21, 2022
489ef20
MM2Q promotion iterators (#1)
byrnedj Aug 9, 2022
048c809
CS Patch Part 2 for mulit-tier cachelib:
byrnedj Feb 7, 2023
ed7b70f
basic multi-tier test based on numa bindings
igchor Dec 30, 2021
94c4974
Aadding new configs to hit_ratio/graph_cache_leader_fobj
vinser52 Jan 27, 2022
afd1456
Do not block reader if a child item is moving
igchor Dec 19, 2022
4f8f425
Background data movement (#20)
byrnedj Oct 21, 2022
6203a95
fix race in moveRegularItemWith sync where insertOrReplace can cause …
byrnedj Feb 16, 2023
6abb498
Fix race in acquire (#68)
igchor Mar 16, 2023
add2e5f
Per tier pool stats (#70)
byrnedj Mar 23, 2023
aedaf97
dummy change to trigger container image rebuild
guptask Mar 28, 2023
1f21fce
Fix token creation and stats (#79)
igchor Apr 27, 2023
9e27d35
Updated the docker gcc version to 12 (#83)
guptask May 9, 2023
da7a6bb
NUMA bindigs support for private memory (#82)
vinser52 May 17, 2023
b5ac462
Do not run cachelib-centos-8-5 on PRs (#85)
igchor Jun 6, 2023
50d3ae5
correct handling for expired items in eviction (#86)
byrnedj Jun 6, 2023
5632d18
Add option to insert items to first free tier (#87)
igchor Jun 8, 2023
09d7bab
Chained item movement between tiers - sync on the parent item (#84)
byrnedj Jun 28, 2023
08d8f33
edit dockerfile
byrnedj Jul 24, 2023
316133c
these submodules work
byrnedj Jul 25, 2023
8d2c390
Track latency of per item eviction/promotion between memory tiers
guptask Jul 28, 2023
b99f2b3
Merge pull request #91 from guptask/tier_eviction_latency
guptask Jul 31, 2023
a14f058
modified the cachebench output to make it friendly for parsing
guptask Aug 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Initial multi-tier support implementation (rebased with NUMA and cs p…
…art 2)

fix for compressed ptr (upstream) -> compress from false to true
  • Loading branch information
igchor authored and byrnedj committed Jul 23, 2023
commit 2529f0a215b2bde527547733b12bbaa40b56d962
458 changes: 303 additions & 155 deletions cachelib/allocator/CacheAllocator-inl.h

Large diffs are not rendered by default.

106 changes: 79 additions & 27 deletions cachelib/allocator/CacheAllocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,7 @@ class CacheAllocator : public CacheBase {
// @param config new config for the pool
//
// @throw std::invalid_argument if the poolId is invalid
void overridePoolConfig(PoolId pid, const MMConfig& config);
void overridePoolConfig(TierId tid, PoolId pid, const MMConfig& config);

// update an existing pool's rebalance strategy
//
Expand Down Expand Up @@ -847,8 +847,9 @@ class CacheAllocator : public CacheBase {
// @return true if the operation succeeded. false if the size of the pool is
// smaller than _bytes_
// @throw std::invalid_argument if the poolId is invalid.
// TODO: should call shrinkPool for specific tier?
bool shrinkPool(PoolId pid, size_t bytes) {
return allocator_->shrinkPool(pid, bytes);
return allocator_[currentTier()]->shrinkPool(pid, bytes);
}

// grow an existing pool by _bytes_. This will fail if there is no
Expand All @@ -857,8 +858,9 @@ class CacheAllocator : public CacheBase {
// @return true if the pool was grown. false if the necessary number of
// bytes were not available.
// @throw std::invalid_argument if the poolId is invalid.
// TODO: should call growPool for specific tier?
bool growPool(PoolId pid, size_t bytes) {
return allocator_->growPool(pid, bytes);
return allocator_[currentTier()]->growPool(pid, bytes);
}

// move bytes from one pool to another. The source pool should be at least
Expand All @@ -871,7 +873,7 @@ class CacheAllocator : public CacheBase {
// correct size to do the transfer.
// @throw std::invalid_argument if src or dest is invalid pool
bool resizePools(PoolId src, PoolId dest, size_t bytes) override {
return allocator_->resizePools(src, dest, bytes);
return allocator_[currentTier()]->resizePools(src, dest, bytes);
}

// Add a new compact cache with given name and size
Expand Down Expand Up @@ -1076,12 +1078,13 @@ class CacheAllocator : public CacheBase {
// @throw std::invalid_argument if the memory does not belong to this
// cache allocator
AllocInfo getAllocInfo(const void* memory) const {
return allocator_->getAllocInfo(memory);
return allocator_[getTierId(memory)]->getAllocInfo(memory);
}

// return the ids for the set of existing pools in this cache.
std::set<PoolId> getPoolIds() const override final {
return allocator_->getPoolIds();
// all tiers have the same pool ids. TODO: deduplicate
return allocator_[0]->getPoolIds();
}

// return a list of pool ids that are backing compact caches. This includes
Expand All @@ -1093,27 +1096,28 @@ class CacheAllocator : public CacheBase {

// return the pool with speicified id.
const MemoryPool& getPool(PoolId pid) const override final {
return allocator_->getPool(pid);
return allocator_[currentTier()]->getPool(pid);
}

// calculate the number of slabs to be advised/reclaimed in each pool
PoolAdviseReclaimData calcNumSlabsToAdviseReclaim() override final {
auto regularPoolIds = getRegularPoolIds();
return allocator_->calcNumSlabsToAdviseReclaim(regularPoolIds);
return allocator_[currentTier()]->calcNumSlabsToAdviseReclaim(regularPoolIds);
}

// update number of slabs to advise in the cache
void updateNumSlabsToAdvise(int32_t numSlabsToAdvise) override final {
allocator_->updateNumSlabsToAdvise(numSlabsToAdvise);
allocator_[currentTier()]->updateNumSlabsToAdvise(numSlabsToAdvise);
}

// returns a valid PoolId corresponding to the name or kInvalidPoolId if the
// name is not a recognized pool
PoolId getPoolId(folly::StringPiece name) const noexcept;

// returns the pool's name by its poolId.
std::string getPoolName(PoolId poolId) const override {
return allocator_->getPoolName(poolId);
std::string getPoolName(PoolId poolId) const {
// all tiers have the same pool names.
return allocator_[0]->getPoolName(poolId);
}

// get stats related to all kinds of slab release events.
Expand Down Expand Up @@ -1391,19 +1395,27 @@ class CacheAllocator : public CacheBase {

using MMContainerPtr = std::unique_ptr<MMContainer>;
using MMContainers =
std::array<std::array<MMContainerPtr, MemoryAllocator::kMaxClasses>,
MemoryPoolManager::kMaxPools>;
std::vector<std::array<std::array<MMContainerPtr, MemoryAllocator::kMaxClasses>,
MemoryPoolManager::kMaxPools>>;

void createMMContainers(const PoolId pid, MMConfig config);

TierId getTierId(const Item& item) const;
TierId getTierId(const void* ptr) const;

// acquire the MMContainer corresponding to the the Item's class and pool.
//
// @return pointer to the MMContainer.
// @throw std::invalid_argument if the Item does not point to a valid
// allocation from the memory allocator.
MMContainer& getMMContainer(const Item& item) const noexcept;

MMContainer& getMMContainer(PoolId pid, ClassId cid) const noexcept;
MMContainer& getMMContainer(TierId tid, PoolId pid, ClassId cid) const noexcept;

// Get stats of the specified pid and cid.
// If such mmcontainer is not valid (pool id or cid out of bound)
// or the mmcontainer is not initialized, return an empty stat.
MMContainerStat getMMContainerStat(TierId tid, PoolId pid, ClassId cid) const noexcept;

// create a new cache allocation. The allocation can be initialized
// appropriately and made accessible through insert or insertOrReplace.
Expand Down Expand Up @@ -1435,6 +1447,17 @@ class CacheAllocator : public CacheBase {
uint32_t creationTime,
uint32_t expiryTime);

// create a new cache allocation on specific memory tier.
// For description see allocateInternal.
//
// @param tid id a memory tier
WriteHandle allocateInternalTier(TierId tid,
PoolId id,
Key key,
uint32_t size,
uint32_t creationTime,
uint32_t expiryTime);

// Allocate a chained item
//
// The resulting chained item does not have a parent item and
Expand Down Expand Up @@ -1525,6 +1548,15 @@ class CacheAllocator : public CacheBase {
// not exist.
FOLLY_ALWAYS_INLINE WriteHandle findFastImpl(Key key, AccessMode mode);

// Moves a regular item to a different memory tier.
//
// @param oldItem Reference to the item being moved
// @param newItemHdl Reference to the handle of the new item being moved into
//
// @return true If the move was completed, and the containers were updated
// successfully.
bool moveRegularItemOnEviction(Item& oldItem, WriteHandle& newItemHdl);

// Moves a regular item to a different slab. This should only be used during
// slab release after the item's exclusive bit has been set. The user supplied
// callback is responsible for copying the contents and fixing the semantics
Expand Down Expand Up @@ -1679,23 +1711,26 @@ class CacheAllocator : public CacheBase {
// Implementation to find a suitable eviction from the container. The
// two parameters together identify a single container.
//
// @param tid the id of the tier to look for evictions inside
// @param pid the id of the pool to look for evictions inside
// @param cid the id of the class to look for evictions inside
// @return An evicted item or nullptr if there is no suitable candidate found
// within the configured number of attempts.
Item* findEviction(PoolId pid, ClassId cid);
Item* findEviction(TierId tid, PoolId pid, ClassId cid);

// Get next eviction candidate from MMContainer, remove from AccessContainer,
// MMContainer and insert into NVMCache if enabled.
//
// @param tid the id of the tier to look for evictions inside
// @param pid the id of the pool to look for evictions inside
// @param cid the id of the class to look for evictions inside
// @param searchTries number of search attempts so far.
//
// @return pair of [candidate, toRecycle]. Pair of null if reached the end of
// the eviction queue or no suitable candidate found
// within the configured number of attempts
std::pair<Item*, Item*> getNextCandidate(PoolId pid,
std::pair<Item*, Item*> getNextCandidate(TierId tid,
PoolId pid,
ClassId cid,
unsigned int& searchTries);

Expand All @@ -1713,7 +1748,7 @@ class CacheAllocator : public CacheBase {
const typename Item::PtrCompressor& compressor);

unsigned int reclaimSlabs(PoolId id, size_t numSlabs) final {
return allocator_->reclaimSlabsAndGrow(id, numSlabs);
return allocator_[currentTier()]->reclaimSlabsAndGrow(id, numSlabs);
}

FOLLY_ALWAYS_INLINE EventTracker* getEventTracker() const {
Expand Down Expand Up @@ -1772,7 +1807,7 @@ class CacheAllocator : public CacheBase {
const void* hint = nullptr) final;

// @param releaseContext slab release context
void releaseSlabImpl(const SlabReleaseContext& releaseContext);
void releaseSlabImpl(TierId tid, const SlabReleaseContext& releaseContext);

// @return true when successfully marked as moving,
// fasle when this item has already been freed
Expand Down Expand Up @@ -1834,7 +1869,7 @@ class CacheAllocator : public CacheBase {
// primitives. So we consciously exempt ourselves here from TSAN data race
// detection.
folly::annotate_ignore_thread_sanitizer_guard g(__FILE__, __LINE__);
auto slabsSkipped = allocator_->forEachAllocation(std::forward<Fn>(f));
auto slabsSkipped = allocator_[currentTier()]->forEachAllocation(std::forward<Fn>(f));
stats().numReaperSkippedSlabs.add(slabsSkipped);
}

Expand Down Expand Up @@ -1878,10 +1913,10 @@ class CacheAllocator : public CacheBase {
std::unique_ptr<T>& worker,
std::chrono::seconds timeout = std::chrono::seconds{0});

ShmSegmentOpts createShmCacheOpts();
std::unique_ptr<MemoryAllocator> createNewMemoryAllocator();
std::unique_ptr<MemoryAllocator> restoreMemoryAllocator();
std::unique_ptr<CCacheManager> restoreCCacheManager();
ShmSegmentOpts createShmCacheOpts(TierId tid);
std::unique_ptr<MemoryAllocator> createNewMemoryAllocator(TierId tid);
std::unique_ptr<MemoryAllocator> restoreMemoryAllocator(TierId tid);
std::unique_ptr<CCacheManager> restoreCCacheManager(TierId tid);

PoolIds filterCompactCachePools(const PoolIds& poolIds) const;

Expand All @@ -1901,7 +1936,7 @@ class CacheAllocator : public CacheBase {
}

typename Item::PtrCompressor createPtrCompressor() const {
return allocator_->createPtrCompressor<Item>();
return allocator_[0 /* TODO */]->createPtrCompressor<Item>();
}

// helper utility to throttle and optionally log.
Expand All @@ -1924,9 +1959,14 @@ class CacheAllocator : public CacheBase {

// @param type the type of initialization
// @return nullptr if the type is invalid
// @return pointer to memory allocator
// @return vector of pointers to memory allocator
// @throw std::runtime_error if type is invalid
std::unique_ptr<MemoryAllocator> initAllocator(InitMemType type);
std::vector<std::unique_ptr<MemoryAllocator>> initAllocator(InitMemType type);

std::vector<std::unique_ptr<MemoryAllocator>> createPrivateAllocator();
std::vector<std::unique_ptr<MemoryAllocator>> createAllocators();
std::vector<std::unique_ptr<MemoryAllocator>> restoreAllocators();

// @param type the type of initialization
// @return nullptr if the type is invalid
// @return pointer to access container
Expand Down Expand Up @@ -1984,6 +2024,17 @@ class CacheAllocator : public CacheBase {

// BEGIN private members

TierId currentTier() const {
// TODO: every function which calls this method should be refactored.
// We should go case by case and either make such function work on
// all tiers or expose separate parameter to describe the tier ID.
return 0;
}

unsigned getNumTiers() const {
return config_.memoryTierConfigs.size();
}

// Whether the memory allocator for this cache allocator was created on shared
// memory. The hash table, chained item hash table etc is also created on
// shared memory except for temporary shared memory mode when they're created
Expand All @@ -2009,9 +2060,10 @@ class CacheAllocator : public CacheBase {
const MMConfig mmConfig_{};

// the memory allocator for allocating out of the available memory.
std::unique_ptr<MemoryAllocator> allocator_;
std::vector<std::unique_ptr<MemoryAllocator>> allocator_;

// compact cache allocator manager
// TODO: per tier?
std::unique_ptr<CCacheManager> compactCacheManager_;

// compact cache instances reside here when user "add" or "attach" compact
Expand Down
2 changes: 2 additions & 0 deletions cachelib/allocator/PoolOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ void PoolOptimizer::optimizeRegularPoolSizes() {

void PoolOptimizer::optimizeCompactCacheSizes() {
try {
// TODO: should optimizer look at each tier individually?
// If yes, then resizePools should be per-tier
auto strategy = cache_.getPoolOptimizeStrategy();
if (!strategy) {
strategy = strategy_;
Expand Down
7 changes: 7 additions & 0 deletions cachelib/allocator/memory/MemoryAllocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,13 @@ class MemoryAllocator {
memoryPoolManager_.updateNumSlabsToAdvise(numSlabs);
}

// returns ture if ptr points to memory which is managed by this
// allocator
bool isMemoryInAllocator(const void *ptr) {
return ptr && ptr >= slabAllocator_.getSlabMemoryBegin()
&& ptr < slabAllocator_.getSlabMemoryEnd();
}

private:
// @param memory pointer to the memory.
// @return the MemoryPool corresponding to the memory.
Expand Down
17 changes: 11 additions & 6 deletions cachelib/allocator/memory/SlabAllocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,17 @@ class SlabAllocator {
return PtrCompressor<PtrType, SlabAllocator>(*this);
}

// returns starting address of memory we own.
const Slab* getSlabMemoryBegin() const noexcept {
return reinterpret_cast<Slab*>(memoryStart_);
}

// returns first byte after the end of memory region we own.
const Slab* getSlabMemoryEnd() const noexcept {
return reinterpret_cast<Slab*>(reinterpret_cast<uint8_t*>(memoryStart_) +
memorySize_);
}

private:
// null Slab* presenttation. With 4M Slab size, a valid slab index would never
// reach 2^16 - 1;
Expand All @@ -339,12 +350,6 @@ class SlabAllocator {
// @throw std::invalid_argument if the state is invalid.
void checkState() const;

// returns first byte after the end of memory region we own.
const Slab* getSlabMemoryEnd() const noexcept {
return reinterpret_cast<Slab*>(reinterpret_cast<uint8_t*>(memoryStart_) +
memorySize_);
}

// returns true if we have slabbed all the memory that is available to us.
// false otherwise.
bool allMemorySlabbed() const noexcept {
Expand Down
8 changes: 4 additions & 4 deletions cachelib/allocator/tests/AllocatorResizeTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -966,23 +966,23 @@ class AllocatorResizeTest : public AllocatorTest<AllocatorT> {
for (i = 1; i <= numItersToMaxAdviseAway + 1; i++) {
alloc.memMonitor_->adviseAwaySlabs();
std::this_thread::sleep_for(std::chrono::seconds{2});
ASSERT_EQ(alloc.allocator_->getAdvisedMemorySize(), i * perIterAdvSize);
ASSERT_EQ(alloc.allocator_[0 /* TODO - extend test */]->getAdvisedMemorySize(), i * perIterAdvSize);
}
i--;
// This should fail
alloc.memMonitor_->adviseAwaySlabs();
std::this_thread::sleep_for(std::chrono::seconds{2});
auto totalAdvisedAwayMemory = alloc.allocator_->getAdvisedMemorySize();
auto totalAdvisedAwayMemory = alloc.allocator_[0 /* TODO - extend test */]->getAdvisedMemorySize();
ASSERT_EQ(totalAdvisedAwayMemory, i * perIterAdvSize);

// Try to reclaim back
for (i = 1; i <= numItersToMaxAdviseAway + 1; i++) {
alloc.memMonitor_->reclaimSlabs();
std::this_thread::sleep_for(std::chrono::seconds{2});
ASSERT_EQ(alloc.allocator_->getAdvisedMemorySize(),
ASSERT_EQ(alloc.allocator_[0 /* TODO - extend test */]->getAdvisedMemorySize(),
totalAdvisedAwayMemory - i * perIterAdvSize);
}
totalAdvisedAwayMemory = alloc.allocator_->getAdvisedMemorySize();
totalAdvisedAwayMemory = alloc.allocator_[0 /* TODO - extend test */]->getAdvisedMemorySize();
ASSERT_EQ(totalAdvisedAwayMemory, 0);
}
}
Expand Down
8 changes: 4 additions & 4 deletions cachelib/allocator/tests/BaseAllocatorTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -4257,13 +4257,13 @@ class BaseAllocatorTest : public AllocatorTest<AllocatorT> {
// Had a bug: D4799860 where we allocated the wrong size for chained item
{
const auto parentAllocInfo =
alloc.allocator_->getAllocInfo(itemHandle->getMemory());
alloc.allocator_[0 /* TODO - extend test */]->getAllocInfo(itemHandle->getMemory());
const auto child1AllocInfo =
alloc.allocator_->getAllocInfo(chainedItemHandle->getMemory());
alloc.allocator_[0 /* TODO - extend test */]->getAllocInfo(chainedItemHandle->getMemory());
const auto child2AllocInfo =
alloc.allocator_->getAllocInfo(chainedItemHandle2->getMemory());
alloc.allocator_[0 /* TODO - extend test */]->getAllocInfo(chainedItemHandle2->getMemory());
const auto child3AllocInfo =
alloc.allocator_->getAllocInfo(chainedItemHandle3->getMemory());
alloc.allocator_[0 /* TODO - extend test */]->getAllocInfo(chainedItemHandle3->getMemory());

const auto parentCid = parentAllocInfo.classId;
const auto child1Cid = child1AllocInfo.classId;
Expand Down
4 changes: 2 additions & 2 deletions cachelib/allocator/tests/TestBase-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ void AllocatorTest<AllocatorT>::testShmIsRemoved(
ASSERT_FALSE(AllocatorT::ShmManager::segmentExists(
config.getCacheDir(), detail::kShmHashTableName, config.usePosixShm));
ASSERT_FALSE(AllocatorT::ShmManager::segmentExists(
config.getCacheDir(), detail::kShmCacheName, config.usePosixShm));
config.getCacheDir(), detail::kShmCacheName + std::to_string(0), config.usePosixShm));
ASSERT_FALSE(AllocatorT::ShmManager::segmentExists(
config.getCacheDir(), detail::kShmChainedItemHashTableName,
config.usePosixShm));
Expand All @@ -326,7 +326,7 @@ void AllocatorTest<AllocatorT>::testShmIsNotRemoved(
ASSERT_TRUE(AllocatorT::ShmManager::segmentExists(
config.getCacheDir(), detail::kShmHashTableName, config.usePosixShm));
ASSERT_TRUE(AllocatorT::ShmManager::segmentExists(
config.getCacheDir(), detail::kShmCacheName, config.usePosixShm));
config.getCacheDir(), detail::kShmCacheName + std::to_string(0), config.usePosixShm));
ASSERT_TRUE(AllocatorT::ShmManager::segmentExists(
config.getCacheDir(), detail::kShmChainedItemHashTableName,
config.usePosixShm));
Expand Down