Skip to content

Commit cd2b3ad

Browse files
igchorvinser52
authored andcommitted
Add memory usage statistics for slabs and allocation classes
1 parent 0a7ed05 commit cd2b3ad

14 files changed

+195
-6
lines changed

cachelib/allocator/Cache.h

+3
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

+44-1
Original file line numberDiff line numberDiff line change
@@ -2569,6 +2569,44 @@ PoolStats CacheAllocator<CacheTrait>::getPoolStats(PoolId poolId) const {
25692569
return ret;
25702570
}
25712571

2572+
template <typename CacheTrait>
2573+
double CacheAllocator<CacheTrait>::slabsApproxFreePercentage(TierId tid) const
2574+
{
2575+
return allocator_[tid]->approxFreeSlabsPercentage();
2576+
}
2577+
2578+
template <typename CacheTrait>
2579+
AllocationClassBaseStat CacheAllocator<CacheTrait>::getAllocationClassStats(
2580+
TierId tid, PoolId pid, ClassId cid) const {
2581+
const auto &ac = allocator_[tid]->getPool(pid).getAllocationClass(cid);
2582+
2583+
AllocationClassBaseStat stats{};
2584+
stats.allocSize = ac.getAllocSize();
2585+
stats.memorySize = ac.getNumSlabs() * Slab::kSize;
2586+
2587+
if (slabsApproxFreePercentage(tid) > 0.0) {
2588+
auto totalMemory = MemoryAllocator::getMemorySize(memoryTierSize(tid));
2589+
auto freeMemory = static_cast<double>(totalMemory) * slabsApproxFreePercentage(tid) / 100.0;
2590+
2591+
// amount of free memory which has the same ratio to entire free memory as
2592+
// this allocation class memory size has to used memory
2593+
auto scaledFreeMemory = static_cast<size_t>(freeMemory * stats.memorySize / totalMemory);
2594+
2595+
auto acAllocatedMemory = (100.0 - ac.approxFreePercentage()) / 100.0 * ac.getNumSlabs() * Slab::kSize;
2596+
auto acMaxAvailableMemory = ac.getNumSlabs() * Slab::kSize + scaledFreeMemory;
2597+
2598+
if (acMaxAvailableMemory == 0) {
2599+
stats.approxFreePercent = 100.0;
2600+
} else {
2601+
stats.approxFreePercent = 100.0 - 100.0 * acAllocatedMemory / acMaxAvailableMemory;
2602+
}
2603+
} else {
2604+
stats.approxFreePercent = ac.approxFreePercentage();
2605+
}
2606+
2607+
return stats;
2608+
}
2609+
25722610
template <typename CacheTrait>
25732611
PoolEvictionAgeStats CacheAllocator<CacheTrait>::getPoolEvictionAgeStats(
25742612
PoolId pid, unsigned int slabProjectionLength) const {
@@ -3681,6 +3719,10 @@ CacheMemoryStats CacheAllocator<CacheTrait>::getCacheMemoryStats() const {
36813719
size_t compactCacheSize = std::accumulate(
36823720
ccCachePoolIds.begin(), ccCachePoolIds.end(), 0ULL, addSize);
36833721

3722+
std::vector<double> slabsApproxFreePercentages;
3723+
for (TierId tid = 0; tid < getNumTiers(); ++tid)
3724+
slabsApproxFreePercentages.push_back(slabsApproxFreePercentage(tid));
3725+
36843726
return CacheMemoryStats{totalCacheSize,
36853727
regularCacheSize,
36863728
compactCacheSize,
@@ -3689,7 +3731,8 @@ CacheMemoryStats CacheAllocator<CacheTrait>::getCacheMemoryStats() const {
36893731
allocator_[currentTier()]->getUnreservedMemorySize(),
36903732
nvmCache_ ? nvmCache_->getSize() : 0,
36913733
util::getMemAvailable(),
3692-
util::getRSSBytes()};
3734+
util::getRSSBytes(),
3735+
slabsApproxFreePercentages};
36933736
}
36943737

36953738
template <typename CacheTrait>

cachelib/allocator/CacheAllocator.h

+6
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,10 @@ class CacheAllocator : public CacheBase {
11311131
// return cache's memory usage stats
11321132
CacheMemoryStats getCacheMemoryStats() const override final;
11331133

1134+
// return basic stats for Allocation Class
1135+
AllocationClassBaseStat getAllocationClassStats(TierId tid, PoolId pid, ClassId cid)
1136+
const override final;
1137+
11341138
// return the nvm cache stats map
11351139
std::unordered_map<std::string, double> getNvmCacheStatsMap()
11361140
const override final;
@@ -1266,6 +1270,8 @@ class CacheAllocator : public CacheBase {
12661270
#pragma GCC diagnostic pop
12671271

12681272
private:
1273+
double slabsApproxFreePercentage(TierId tid) const;
1274+
12691275
// wrapper around Item's refcount and active handle tracking
12701276
FOLLY_ALWAYS_INLINE void incRef(Item& it);
12711277
FOLLY_ALWAYS_INLINE RefcountWithFlags::Value decRef(Item& it);

cachelib/allocator/CacheStats.h

+14
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.
@@ -545,6 +556,9 @@ struct CacheMemoryStats {
545556

546557
// rss size of the process
547558
size_t memRssSize{0};
559+
560+
// percentage of free slabs
561+
std::vector<double> slabsApproxFreePercentages{0.0};
548562
};
549563

550564
// Stats for compact cache

cachelib/allocator/memory/AllocationClass.cpp

+23
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

+10-4
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

+8
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

+4
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

+7-1
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

+5
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
}

cachelib/cachebench/cache/Cache-inl.h

+12
Original file line numberDiff line numberDiff line change
@@ -595,10 +595,22 @@ Stats Cache<Allocator>::getStats() const {
595595
aggregate += poolStats;
596596
}
597597

598+
std::map<TierId, std::map<PoolId, std::map<ClassId, AllocationClassBaseStat>>> allocationClassStats{};
599+
600+
for (size_t pid = 0; pid < pools_.size(); pid++) {
601+
auto cids = cache_->getPoolStats(static_cast<PoolId>(pid)).getClassIds();
602+
for (TierId tid = 0; tid < cache_->getNumTiers(); tid++) {
603+
for (auto cid : cids)
604+
allocationClassStats[tid][pid][cid] = cache_->getAllocationClassStats(tid, pid, cid);
605+
}
606+
}
607+
598608
const auto cacheStats = cache_->getGlobalCacheStats();
599609
const auto rebalanceStats = cache_->getSlabReleaseStats();
600610
const auto navyStats = cache_->getNvmCacheStatsMap();
601611

612+
ret.slabsApproxFreePercentages = cache_->getCacheMemoryStats().slabsApproxFreePercentages;
613+
ret.allocationClassStats = allocationClassStats;
602614
ret.numEvictions = aggregate.numEvictions();
603615
ret.numItems = aggregate.numItems();
604616
ret.evictAttempts = cacheStats.evictionAttempts;

cachelib/cachebench/cache/Cache.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ DEFINE_bool(report_api_latency,
2222
false,
2323
"Enable reporting cache API latency tracking");
2424

25+
DEFINE_bool(report_memory_usage_stats,
26+
false,
27+
"Enable reporting statistics for each allocation class");
28+
2529
namespace facebook {
2630
namespace cachelib {
2731
namespace cachebench {} // namespace cachebench

cachelib/cachebench/cache/Cache.h

+5
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "cachelib/cachebench/util/NandWrites.h"
4545

4646
DECLARE_bool(report_api_latency);
47+
DECLARE_bool(report_memory_usage_stats);
4748

4849
namespace facebook {
4950
namespace cachelib {
@@ -318,6 +319,10 @@ class Cache {
318319
// return the stats for the pool.
319320
PoolStats getPoolStats(PoolId pid) const { return cache_->getPoolStats(pid); }
320321

322+
AllocationClassBaseStat getAllocationClassStats(TierId tid, PoolId pid, ClassId cid) const {
323+
return cache_->getAllocationClassStats(tid, pid, cid);
324+
}
325+
321326
// return the total number of inconsistent operations detected since start.
322327
unsigned int getInconsistencyCount() const {
323328
return inconsistencyCount_.load(std::memory_order_relaxed);

0 commit comments

Comments
 (0)