Skip to content

Commit b3621d1

Browse files
igchorbyrnedj
authored andcommitted
Add memory usage statistics for slabs and allocation classes
1 parent 3ae2b4b commit b3621d1

File tree

14 files changed

+195
-6
lines changed

14 files changed

+195
-6
lines changed

cachelib/allocator/Cache.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ class CacheBase {
101101
// @param poolId the pool id
102102
virtual PoolStats getPoolStats(PoolId poolId) const = 0;
103103

104+
virtual AllocationClassBaseStat getAllocationClassStats(TierId, PoolId pid, ClassId cid)
105+
const = 0;
106+
104107
// @param poolId the pool id
105108
virtual AllSlabReleaseEvents getAllSlabReleaseEvents(PoolId poolId) const = 0;
106109

cachelib/allocator/CacheAllocator-inl.h

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2611,6 +2611,44 @@ PoolStats CacheAllocator<CacheTrait>::getPoolStats(PoolId poolId) const {
26112611
return ret;
26122612
}
26132613

2614+
template <typename CacheTrait>
2615+
double CacheAllocator<CacheTrait>::slabsApproxFreePercentage(TierId tid) const
2616+
{
2617+
return allocator_[tid]->approxFreeSlabsPercentage();
2618+
}
2619+
2620+
template <typename CacheTrait>
2621+
AllocationClassBaseStat CacheAllocator<CacheTrait>::getAllocationClassStats(
2622+
TierId tid, PoolId pid, ClassId cid) const {
2623+
const auto &ac = allocator_[tid]->getPool(pid).getAllocationClass(cid);
2624+
2625+
AllocationClassBaseStat stats{};
2626+
stats.allocSize = ac.getAllocSize();
2627+
stats.memorySize = ac.getNumSlabs() * Slab::kSize;
2628+
2629+
if (slabsApproxFreePercentage(tid) > 0.0) {
2630+
auto totalMemory = MemoryAllocator::getMemorySize(memoryTierSize(tid));
2631+
auto freeMemory = static_cast<double>(totalMemory) * slabsApproxFreePercentage(tid) / 100.0;
2632+
2633+
// amount of free memory which has the same ratio to entire free memory as
2634+
// this allocation class memory size has to used memory
2635+
auto scaledFreeMemory = static_cast<size_t>(freeMemory * stats.memorySize / totalMemory);
2636+
2637+
auto acAllocatedMemory = (100.0 - ac.approxFreePercentage()) / 100.0 * ac.getNumSlabs() * Slab::kSize;
2638+
auto acMaxAvailableMemory = ac.getNumSlabs() * Slab::kSize + scaledFreeMemory;
2639+
2640+
if (acMaxAvailableMemory == 0) {
2641+
stats.approxFreePercent = 100.0;
2642+
} else {
2643+
stats.approxFreePercent = 100.0 - 100.0 * acAllocatedMemory / acMaxAvailableMemory;
2644+
}
2645+
} else {
2646+
stats.approxFreePercent = ac.approxFreePercentage();
2647+
}
2648+
2649+
return stats;
2650+
}
2651+
26142652
template <typename CacheTrait>
26152653
PoolEvictionAgeStats CacheAllocator<CacheTrait>::getPoolEvictionAgeStats(
26162654
PoolId pid, unsigned int slabProjectionLength) const {
@@ -3722,6 +3760,10 @@ CacheMemoryStats CacheAllocator<CacheTrait>::getCacheMemoryStats() const {
37223760
size_t compactCacheSize = std::accumulate(
37233761
ccCachePoolIds.begin(), ccCachePoolIds.end(), 0ULL, addSize);
37243762

3763+
std::vector<double> slabsApproxFreePercentages;
3764+
for (TierId tid = 0; tid < getNumTiers(); ++tid)
3765+
slabsApproxFreePercentages.push_back(slabsApproxFreePercentage(tid));
3766+
37253767
return CacheMemoryStats{totalCacheSize,
37263768
regularCacheSize,
37273769
compactCacheSize,
@@ -3730,7 +3772,8 @@ CacheMemoryStats CacheAllocator<CacheTrait>::getCacheMemoryStats() const {
37303772
allocator_[currentTier()]->getUnreservedMemorySize(),
37313773
nvmCache_ ? nvmCache_->getSize() : 0,
37323774
util::getMemAvailable(),
3733-
util::getRSSBytes()};
3775+
util::getRSSBytes(),
3776+
slabsApproxFreePercentages};
37343777
}
37353778

37363779
template <typename CacheTrait>

cachelib/allocator/CacheAllocator.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,6 +1184,10 @@ class CacheAllocator : public CacheBase {
11841184
// return cache's memory usage stats
11851185
CacheMemoryStats getCacheMemoryStats() const override final;
11861186

1187+
// return basic stats for Allocation Class
1188+
AllocationClassBaseStat getAllocationClassStats(TierId tid, PoolId pid, ClassId cid)
1189+
const override final;
1190+
11871191
// return the nvm cache stats map
11881192
std::unordered_map<std::string, double> getNvmCacheStatsMap()
11891193
const override final;
@@ -1319,6 +1323,8 @@ class CacheAllocator : public CacheBase {
13191323
#pragma GCC diagnostic pop
13201324

13211325
private:
1326+
double slabsApproxFreePercentage(TierId tid) const;
1327+
13221328
// wrapper around Item's refcount and active handle tracking
13231329
FOLLY_ALWAYS_INLINE void incRef(Item& it);
13241330
FOLLY_ALWAYS_INLINE RefcountWithFlags::Value decRef(Item& it);

cachelib/allocator/CacheStats.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,17 @@ struct MMContainerStat {
9595
uint64_t numTailAccesses;
9696
};
9797

98+
struct AllocationClassBaseStat {
99+
// size of allocation class
100+
size_t allocSize{0};
101+
102+
// size of memory assigned to this allocation class
103+
size_t memorySize{0};
104+
105+
// percent of free memory in this class
106+
double approxFreePercent{0.0};
107+
};
108+
98109
// cache related stats for a given allocation class.
99110
struct CacheStat {
100111
// allocation size for this container.
@@ -548,6 +559,9 @@ struct CacheMemoryStats {
548559

549560
// rss size of the process
550561
size_t memRssSize{0};
562+
563+
// percentage of free slabs
564+
std::vector<double> slabsApproxFreePercentages{0.0};
551565
};
552566

553567
// Stats for compact cache

cachelib/allocator/memory/AllocationClass.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ AllocationClass::AllocationClass(ClassId classId,
5151
allocationSize_(allocSize),
5252
slabAlloc_(s),
5353
freedAllocations_{slabAlloc_.createSingleTierPtrCompressor<FreeAlloc>()} {
54+
curAllocatedSlabs_ = allocatedSlabs_.size();
5455
checkState();
5556
}
5657

@@ -87,6 +88,12 @@ void AllocationClass::checkState() const {
8788
"Current allocation slab {} is not in allocated slabs list",
8889
currSlab_));
8990
}
91+
92+
if (curAllocatedSlabs_ != allocatedSlabs_.size()) {
93+
throw std::invalid_argument(folly::sformat(
94+
"Mismatch in allocated slabs numbers"
95+
));
96+
}
9097
}
9198

9299
// TODO(stuclar): Add poolId to the metadata to be serialized when cache shuts
@@ -116,10 +123,12 @@ AllocationClass::AllocationClass(
116123
freeSlabs_.push_back(slabAlloc_.getSlabForIdx(freeSlabIdx));
117124
}
118125

126+
curAllocatedSlabs_ = allocatedSlabs_.size();
119127
checkState();
120128
}
121129

122130
void AllocationClass::addSlabLocked(Slab* slab) {
131+
curAllocatedSlabs_.fetch_add(1, std::memory_order_relaxed);
123132
canAllocate_ = true;
124133
auto header = slabAlloc_.getSlabHeader(slab);
125134
header->classId = classId_;
@@ -168,6 +177,7 @@ void* AllocationClass::allocateLocked() {
168177
}
169178

170179
XDCHECK(canAllocate_);
180+
curAllocatedSize_.fetch_add(getAllocSize(), std::memory_order_relaxed);
171181

172182
// grab from the free list if possible.
173183
if (!freedAllocations_.empty()) {
@@ -270,6 +280,7 @@ SlabReleaseContext AllocationClass::startSlabRelease(
270280
slab, getId()));
271281
}
272282
*allocIt = allocatedSlabs_.back();
283+
curAllocatedSlabs_.fetch_sub(1, std::memory_order_relaxed);
273284
allocatedSlabs_.pop_back();
274285

275286
// if slab is being carved currently, then update slabReleaseAllocMap
@@ -510,6 +521,7 @@ void AllocationClass::abortSlabRelease(const SlabReleaseContext& context) {
510521
}
511522
slabReleaseAllocMap_.erase(slabPtrVal);
512523
allocatedSlabs_.push_back(const_cast<Slab*>(slab));
524+
curAllocatedSlabs_.fetch_add(1, std::memory_order_relaxed);
513525
// restore the classId and allocSize
514526
header->classId = classId_;
515527
header->allocSize = allocationSize_;
@@ -660,6 +672,8 @@ void AllocationClass::free(void* memory) {
660672
freedAllocations_.insert(*reinterpret_cast<FreeAlloc*>(memory));
661673
canAllocate_ = true;
662674
});
675+
676+
curAllocatedSize_.fetch_sub(getAllocSize(), std::memory_order_relaxed);
663677
}
664678

665679
serialization::AllocationClassObject AllocationClass::saveState() const {
@@ -722,3 +736,12 @@ std::vector<bool>& AllocationClass::getSlabReleaseAllocMapLocked(
722736
const auto slabPtrVal = getSlabPtrValue(slab);
723737
return slabReleaseAllocMap_.at(slabPtrVal);
724738
}
739+
740+
double AllocationClass::approxFreePercentage() const {
741+
if (getNumSlabs() == 0) {
742+
return 100.0;
743+
}
744+
745+
return 100.0 - 100.0 * static_cast<double>(curAllocatedSize_.load(std::memory_order_relaxed)) /
746+
static_cast<double>(getNumSlabs() * Slab::kSize);
747+
}

cachelib/allocator/memory/AllocationClass.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,7 @@ class AllocationClass {
9696

9797
// total number of slabs under this AllocationClass.
9898
unsigned int getNumSlabs() const {
99-
return lock_->lock_combine([this]() {
100-
return static_cast<unsigned int>(freeSlabs_.size() +
101-
allocatedSlabs_.size());
102-
});
99+
return curAllocatedSlabs_.load(std::memory_order_relaxed);
103100
}
104101

105102
// fetch stats about this allocation class.
@@ -316,6 +313,9 @@ class AllocationClass {
316313
// @throw std::logic_error if the object state can not be serialized
317314
serialization::AllocationClassObject saveState() const;
318315

316+
// approximate percent of free memory inside this allocation class
317+
double approxFreePercentage() const;
318+
319319
private:
320320
// check if the state of the AllocationClass is valid and if not, throws an
321321
// std::invalid_argument exception. This is intended for use in
@@ -475,6 +475,12 @@ class AllocationClass {
475475

476476
std::atomic<int64_t> activeReleases_{0};
477477

478+
// amount of memory currently allocated by this AC
479+
std::atomic<size_t> curAllocatedSize_{0};
480+
481+
// total number of slabs under this AllocationClass.
482+
std::atomic<size_t> curAllocatedSlabs_{0};
483+
478484
// stores the list of outstanding allocations for a given slab. This is
479485
// created when we start a slab release process and if there are any active
480486
// allocaitons need to be marked as free.

cachelib/allocator/memory/MemoryAllocator.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,14 @@ class MemoryAllocator {
416416
return memoryPoolManager_.getPoolIds();
417417
}
418418

419+
double approxFreeSlabsPercentage() const {
420+
if (slabAllocator_.getNumUsableAndAdvisedSlabs() == 0)
421+
return 100.0;
422+
423+
return 100.0 - 100.0 * static_cast<double>(slabAllocator_.approxNumSlabsAllocated()) /
424+
slabAllocator_.getNumUsableAndAdvisedSlabs();
425+
}
426+
419427
// fetches the memory pool for the id if one exists. This is purely to get
420428
// information out of the pool.
421429
//

cachelib/allocator/memory/SlabAllocator.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,8 @@ Slab* SlabAllocator::makeNewSlab(PoolId id) {
359359
return nullptr;
360360
}
361361

362+
numSlabsAllocated_.fetch_add(1, std::memory_order_relaxed);
363+
362364
memoryPoolSize_[id] += sizeof(Slab);
363365
// initialize the header for the slab.
364366
initializeHeader(slab, id);
@@ -374,6 +376,8 @@ void SlabAllocator::freeSlab(Slab* slab) {
374376
}
375377

376378
memoryPoolSize_[header->poolId] -= sizeof(Slab);
379+
numSlabsAllocated_.fetch_sub(1, std::memory_order_relaxed);
380+
377381
// grab the lock
378382
LockHolder l(lock_);
379383
freeSlabs_.push_back(slab);

cachelib/allocator/memory/SlabAllocator.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,13 @@ class SlabAllocator {
327327
memorySize_);
328328
}
329329

330-
private:
330+
size_t approxNumSlabsAllocated() const {
331+
return numSlabsAllocated_.load(std::memory_order_relaxed);
332+
}
333+
334+
private:
335+
std::atomic<size_t> numSlabsAllocated_{0};
336+
331337
// null Slab* presenttation. With 4M Slab size, a valid slab index would never
332338
// reach 2^16 - 1;
333339
static constexpr SlabIdx kNullSlabIdx = std::numeric_limits<SlabIdx>::max();

cachelib/allocator/tests/CacheBaseTest.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ class CacheBaseTest : public CacheBase, public SlabAllocatorTestBase {
3434
bool isObjectCache() const override { return false; }
3535
const MemoryPool& getPool(PoolId) const override { return memoryPool_; }
3636
PoolStats getPoolStats(PoolId) const override { return PoolStats(); }
37+
AllocationClassBaseStat getAllocationClassStats(TierId tid,
38+
PoolId,
39+
ClassId) const {
40+
return AllocationClassBaseStat();
41+
};
3742
AllSlabReleaseEvents getAllSlabReleaseEvents(PoolId) const override {
3843
return AllSlabReleaseEvents{};
3944
}

0 commit comments

Comments
 (0)