Skip to content

Commit a5fbe7a

Browse files
igchorguptask
authored andcommitted
Add memory usage statistics for slabs and allocation classes
1 parent ef4799b commit a5fbe7a

File tree

14 files changed

+195
-16
lines changed

14 files changed

+195
-16
lines changed

cachelib/allocator/Cache.h

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

106+
virtual AllocationClassBaseStat getAllocationClassStats(TierId, PoolId pid, ClassId cid)
107+
const = 0;
108+
106109
// @param poolId the pool id
107110
virtual AllSlabReleaseEvents getAllSlabReleaseEvents(PoolId poolId) const = 0;
108111

cachelib/allocator/CacheAllocator-inl.h

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

2646+
template <typename CacheTrait>
2647+
double CacheAllocator<CacheTrait>::slabsApproxFreePercentage(TierId tid) const
2648+
{
2649+
return allocator_[tid]->approxFreeSlabsPercentage();
2650+
}
2651+
2652+
template <typename CacheTrait>
2653+
AllocationClassBaseStat CacheAllocator<CacheTrait>::getAllocationClassStats(
2654+
TierId tid, PoolId pid, ClassId cid) const {
2655+
const auto &ac = allocator_[tid]->getPool(pid).getAllocationClass(cid);
2656+
2657+
AllocationClassBaseStat stats{};
2658+
stats.allocSize = ac.getAllocSize();
2659+
stats.memorySize = ac.getNumSlabs() * Slab::kSize;
2660+
2661+
if (slabsApproxFreePercentage(tid) > 0.0) {
2662+
auto totalMemory = MemoryAllocator::getMemorySize(memoryTierSize(tid));
2663+
auto freeMemory = static_cast<double>(totalMemory) * slabsApproxFreePercentage(tid) / 100.0;
2664+
2665+
// amount of free memory which has the same ratio to entire free memory as
2666+
// this allocation class memory size has to used memory
2667+
auto scaledFreeMemory = static_cast<size_t>(freeMemory * stats.memorySize / totalMemory);
2668+
2669+
auto acAllocatedMemory = (100.0 - ac.approxFreePercentage()) / 100.0 * ac.getNumSlabs() * Slab::kSize;
2670+
auto acMaxAvailableMemory = ac.getNumSlabs() * Slab::kSize + scaledFreeMemory;
2671+
2672+
if (acMaxAvailableMemory == 0) {
2673+
stats.approxFreePercent = 100.0;
2674+
} else {
2675+
stats.approxFreePercent = 100.0 - 100.0 * acAllocatedMemory / acMaxAvailableMemory;
2676+
}
2677+
} else {
2678+
stats.approxFreePercent = ac.approxFreePercentage();
2679+
}
2680+
2681+
return stats;
2682+
}
2683+
26462684
template <typename CacheTrait>
26472685
PoolEvictionAgeStats CacheAllocator<CacheTrait>::getPoolEvictionAgeStats(
26482686
PoolId pid, unsigned int slabProjectionLength) const {
@@ -3773,6 +3811,10 @@ CacheMemoryStats CacheAllocator<CacheTrait>::getCacheMemoryStats() const {
37733811
size_t compactCacheSize = std::accumulate(
37743812
ccCachePoolIds.begin(), ccCachePoolIds.end(), 0ULL, addSize);
37753813

3814+
std::vector<double> slabsApproxFreePercentages;
3815+
for (TierId tid = 0; tid < numTiers_; tid++)
3816+
slabsApproxFreePercentages.push_back(slabsApproxFreePercentage(tid));
3817+
37763818
return CacheMemoryStats{totalCacheSize,
37773819
regularCacheSize,
37783820
compactCacheSize,
@@ -3781,7 +3823,8 @@ CacheMemoryStats CacheAllocator<CacheTrait>::getCacheMemoryStats() const {
37813823
allocator_[currentTier()]->getUnreservedMemorySize(),
37823824
nvmCache_ ? nvmCache_->getSize() : 0,
37833825
util::getMemAvailable(),
3784-
util::getRSSBytes()};
3826+
util::getRSSBytes(),
3827+
slabsApproxFreePercentages};
37853828
}
37863829

37873830
template <typename CacheTrait>

cachelib/allocator/CacheAllocator.h

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

1128+
// return basic stats for Allocation Class
1129+
AllocationClassBaseStat getAllocationClassStats(TierId tid, PoolId pid, ClassId cid)
1130+
const override final;
1131+
11281132
// return the nvm cache stats map
11291133
std::unordered_map<std::string, double> getNvmCacheStatsMap()
11301134
const override final;
@@ -1269,6 +1273,8 @@ class CacheAllocator : public CacheBase {
12691273
#pragma GCC diagnostic pop
12701274

12711275
private:
1276+
double slabsApproxFreePercentage(TierId tid) const;
1277+
12721278
// wrapper around Item's refcount and active handle tracking
12731279
FOLLY_ALWAYS_INLINE void incRef(Item& it);
12741280
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.
@@ -536,6 +547,9 @@ struct CacheMemoryStats {
536547

537548
// rss size of the process
538549
size_t memRssSize{0};
550+
551+
// percentage of free slabs
552+
std::vector<double> slabsApproxFreePercentages{0.0};
539553
};
540554

541555
// 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)