Skip to content

Commit 57a3d1c

Browse files
authored
Merge pull request #91 from igchor/more_stats
Add memory usage statistics for slabs and allocation classes
2 parents 3c34254 + 407806a commit 57a3d1c

File tree

14 files changed

+196
-7
lines changed

14 files changed

+196
-7
lines changed

cachelib/allocator/Cache.h

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

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

cachelib/allocator/CacheAllocator-inl.h

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

2509+
template <typename CacheTrait>
2510+
double CacheAllocator<CacheTrait>::slabsApproxFreePercentage(TierId tid) const
2511+
{
2512+
return allocator_[tid]->approxFreeSlabsPercentage();
2513+
}
2514+
2515+
template <typename CacheTrait>
2516+
AllocationClassBaseStat CacheAllocator<CacheTrait>::getAllocationClassStats(
2517+
TierId tid, PoolId pid, ClassId cid) const {
2518+
const auto &ac = allocator_[tid]->getPool(pid).getAllocationClass(cid);
2519+
2520+
AllocationClassBaseStat stats{};
2521+
stats.allocSize = ac.getAllocSize();
2522+
stats.memorySize = ac.getNumSlabs() * Slab::kSize;
2523+
2524+
if (slabsApproxFreePercentage(tid) > 0.0) {
2525+
auto totalMemory = MemoryAllocator::getMemorySize(memoryTierSize(tid));
2526+
auto freeMemory = static_cast<double>(totalMemory) * slabsApproxFreePercentage(tid) / 100.0;
2527+
2528+
// amount of free memory which has the same ratio to entire free memory as
2529+
// this allocation class memory size has to used memory
2530+
auto scaledFreeMemory = static_cast<size_t>(freeMemory * stats.memorySize / totalMemory);
2531+
2532+
auto acAllocatedMemory = (100.0 - ac.approxFreePercentage()) / 100.0 * ac.getNumSlabs() * Slab::kSize;
2533+
auto acMaxAvailableMemory = ac.getNumSlabs() * Slab::kSize + scaledFreeMemory;
2534+
2535+
if (acMaxAvailableMemory == 0) {
2536+
stats.approxFreePercent = 100.0;
2537+
} else {
2538+
stats.approxFreePercent = 100.0 - 100.0 * acAllocatedMemory / acMaxAvailableMemory;
2539+
}
2540+
} else {
2541+
stats.approxFreePercent = ac.approxFreePercentage();
2542+
}
2543+
2544+
return stats;
2545+
}
2546+
25092547
template <typename CacheTrait>
25102548
PoolEvictionAgeStats CacheAllocator<CacheTrait>::getPoolEvictionAgeStats(
25112549
PoolId pid, unsigned int slabProjectionLength) const {
@@ -3613,6 +3651,10 @@ CacheMemoryStats CacheAllocator<CacheTrait>::getCacheMemoryStats() const {
36133651
size_t compactCacheSize = std::accumulate(
36143652
ccCachePoolIds.begin(), ccCachePoolIds.end(), 0ULL, addSize);
36153653

3654+
std::vector<double> slabsApproxFreePercentages;
3655+
for (TierId tid = 0; tid < numTiers_; tid++)
3656+
slabsApproxFreePercentages.push_back(slabsApproxFreePercentage(tid));
3657+
36163658
return CacheMemoryStats{totalCacheSize,
36173659
regularCacheSize,
36183660
compactCacheSize,
@@ -3621,7 +3663,8 @@ CacheMemoryStats CacheAllocator<CacheTrait>::getCacheMemoryStats() const {
36213663
allocator_[currentTier()]->getUnreservedMemorySize(),
36223664
nvmCache_ ? nvmCache_->getSize() : 0,
36233665
util::getMemAvailable(),
3624-
util::getRSSBytes()};
3666+
util::getRSSBytes(),
3667+
slabsApproxFreePercentages};
36253668
}
36263669

36273670
template <typename CacheTrait>

cachelib/allocator/CacheAllocator.h

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

1067+
// return basic stats for Allocation Class
1068+
AllocationClassBaseStat getAllocationClassStats(TierId tid, PoolId pid, ClassId cid)
1069+
const override final;
1070+
10671071
// return the nvm cache stats map
10681072
std::unordered_map<std::string, double> getNvmCacheStatsMap()
10691073
const override final;
@@ -1208,6 +1212,8 @@ class CacheAllocator : public CacheBase {
12081212
#pragma GCC diagnostic pop
12091213

12101214
private:
1215+
double slabsApproxFreePercentage(TierId tid) const;
1216+
12111217
// wrapper around Item's refcount and active handle tracking
12121218
FOLLY_ALWAYS_INLINE void incRef(Item& it);
12131219
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
@@ -98,6 +98,17 @@ struct MMContainerStat {
9898
uint64_t numTailAccesses;
9999
};
100100

101+
struct AllocationClassBaseStat {
102+
// size of allocation class
103+
size_t allocSize{0};
104+
105+
// size of memory assigned to this allocation class
106+
size_t memorySize{0};
107+
108+
// percent of free memory in this class
109+
double approxFreePercent{0.0};
110+
};
111+
101112
// cache related stats for a given allocation class.
102113
struct CacheStat {
103114
// allocation size for this container.
@@ -521,6 +532,9 @@ struct CacheMemoryStats {
521532

522533
// rss size of the process
523534
size_t memRssSize{0};
535+
536+
// percentage of free slabs
537+
std::vector<double> slabsApproxFreePercentages{0.0};
524538
};
525539

526540
// 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
@@ -90,10 +90,7 @@ class AllocationClass {
9090

9191
// total number of slabs under this AllocationClass.
9292
unsigned int getNumSlabs() const {
93-
return lock_->lock_combine([this]() {
94-
return static_cast<unsigned int>(freeSlabs_.size() +
95-
allocatedSlabs_.size());
96-
});
93+
return curAllocatedSlabs_.load(std::memory_order_relaxed);
9794
}
9895

9996
// fetch stats about this allocation class.
@@ -309,6 +306,9 @@ class AllocationClass {
309306
// @throw std::logic_error if the object state can not be serialized
310307
serialization::AllocationClassObject saveState() const;
311308

309+
// approximate percent of free memory inside this allocation class
310+
double approxFreePercentage() const;
311+
312312
private:
313313
// check if the state of the AllocationClass is valid and if not, throws an
314314
// std::invalid_argument exception. This is intended for use in
@@ -468,6 +468,12 @@ class AllocationClass {
468468

469469
std::atomic<int64_t> activeReleases_{0};
470470

471+
// amount of memory currently allocated by this AC
472+
std::atomic<size_t> curAllocatedSize_{0};
473+
474+
// total number of slabs under this AllocationClass.
475+
std::atomic<size_t> curAllocatedSlabs_{0};
476+
471477
// stores the list of outstanding allocations for a given slab. This is
472478
// created when we start a slab release process and if there are any active
473479
// 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
@@ -323,7 +323,13 @@ class SlabAllocator {
323323
memorySize_);
324324
}
325325

326-
private:
326+
size_t approxNumSlabsAllocated() const {
327+
return numSlabsAllocated_.load(std::memory_order_relaxed);
328+
}
329+
330+
private:
331+
std::atomic<size_t> numSlabsAllocated_{0};
332+
327333
// null Slab* presenttation. With 4M Slab size, a valid slab index would never
328334
// reach 2^16 - 1;
329335
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
@@ -33,6 +33,11 @@ class CacheBaseTest : public CacheBase, public SlabAllocatorTestBase {
3333
const std::string getCacheName() const override { return cacheName; }
3434
const MemoryPool& getPool(PoolId) const override { return memoryPool_; }
3535
PoolStats getPoolStats(PoolId) const override { return PoolStats(); }
36+
AllocationClassBaseStat getAllocationClassStats(TierId tid,
37+
PoolId,
38+
ClassId) const {
39+
return AllocationClassBaseStat();
40+
};
3641
AllSlabReleaseEvents getAllSlabReleaseEvents(PoolId) const override {
3742
return AllSlabReleaseEvents{};
3843
}

0 commit comments

Comments
 (0)