diff --git a/cachelib/allocator/CacheAllocator-inl.h b/cachelib/allocator/CacheAllocator-inl.h index ecd3ea9ffc..00d8dbcc9c 100644 --- a/cachelib/allocator/CacheAllocator-inl.h +++ b/cachelib/allocator/CacheAllocator-inl.h @@ -303,6 +303,20 @@ CacheAllocator::allocate(PoolId poolId, ttlSecs == 0 ? 0 : creationTime + ttlSecs); } +template +ClassId CacheAllocator::getAllocClassId(PoolId pid, + typename Item::Key key, + uint32_t size) const { + const auto requiredSize = Item::getRequiredSize(key, size); + return allocator_->getAllocationClassId(pid, requiredSize); +} + +template +uint64_t CacheAllocator::getAllocationSize(ClassId cid, + PoolId pid) const { + return getPool(pid).getAllocationClass(cid).getAllocSize(); +} + template typename CacheAllocator::WriteHandle CacheAllocator::allocateInternal(PoolId pid, @@ -340,6 +354,7 @@ CacheAllocator::allocateInternal(PoolId pid, handle = acquire(new (memory) Item(key, size, creationTime, expiryTime)); if (handle) { + stats_.usedSize_[pid].set(allocator_->getPoolUsedSize(pid)); handle.markNascent(); (*stats_.fragmentationSize)[pid][cid].add( util::getFragmentation(*this, *handle)); diff --git a/cachelib/allocator/CacheAllocator.h b/cachelib/allocator/CacheAllocator.h index 5d39214ce0..88ac1d352b 100644 --- a/cachelib/allocator/CacheAllocator.h +++ b/cachelib/allocator/CacheAllocator.h @@ -1116,6 +1116,12 @@ class CacheAllocator : public CacheBase { // Whether this cache allocator was created on shared memory. bool isOnShm() const noexcept { return isOnShm_; } + ClassId getAllocClassId(PoolId pid, + typename Item::Key key, + uint32_t size) const; + + uint64_t getAllocationSize(ClassId cid, PoolId pid) const; + // Whether NvmCache is currently enabled bool isNvmCacheEnabled() const noexcept { return nvmCache_ && nvmCache_->isEnabled(); diff --git a/cachelib/allocator/CacheStats.cpp b/cachelib/allocator/CacheStats.cpp index 5b4c855f02..70601af5ee 100644 --- a/cachelib/allocator/CacheStats.cpp +++ b/cachelib/allocator/CacheStats.cpp @@ -49,7 +49,7 @@ struct SizeVerify {}; void Stats::populateGlobalCacheStats(GlobalCacheStats& ret) const { #ifndef SKIP_SIZE_VERIFY - SizeVerify a = SizeVerify<16160>{}; + SizeVerify a = SizeVerify<16672>{}; std::ignore = a; #endif ret.numCacheGets = numCacheGets.get(); @@ -125,6 +125,10 @@ void Stats::populateGlobalCacheStats(GlobalCacheStats& ret) const { ret.numEvictions = accum(*chainedItemEvictions); ret.numEvictions += accum(*regularItemEvictions); + for (const auto& x : usedSize_) { + ret.poolUsedSize.emplace_back(x.get()); + } + ret.invalidAllocs = invalidAllocs.get(); ret.numRefcountOverflow = numRefcountOverflow.get(); diff --git a/cachelib/allocator/CacheStats.h b/cachelib/allocator/CacheStats.h index 7d458d7a6a..72e25d3dd0 100644 --- a/cachelib/allocator/CacheStats.h +++ b/cachelib/allocator/CacheStats.h @@ -416,6 +416,9 @@ struct GlobalCacheStats { // number of evictions across all the pools in the cache. uint64_t numEvictions{0}; + // Used size of all the pools + std::vector poolUsedSize; + // number of allocation attempts with invalid input params. uint64_t invalidAllocs{0}; diff --git a/cachelib/allocator/CacheStatsInternal.h b/cachelib/allocator/CacheStatsInternal.h index 0d910c4918..f3f5fe9b5a 100644 --- a/cachelib/allocator/CacheStatsInternal.h +++ b/cachelib/allocator/CacheStatsInternal.h @@ -222,6 +222,8 @@ struct Stats { std::unique_ptr chainedItemEvictions{}; std::unique_ptr regularItemEvictions{}; + std::array usedSize_; + // Eviction failures due to parent cannot be removed from access container AtomicCounter evictFailParentAC{0}; diff --git a/cachelib/allocator/memory/MemoryAllocator.h b/cachelib/allocator/memory/MemoryAllocator.h index de1a2a926f..f08d93aaf3 100644 --- a/cachelib/allocator/memory/MemoryAllocator.h +++ b/cachelib/allocator/memory/MemoryAllocator.h @@ -644,6 +644,12 @@ class MemoryAllocator { memoryPoolManager_.updateNumSlabsToAdvise(numSlabs); } + // fetch used size of a particular pool + // @return used size of the pool + size_t getPoolUsedSize(PoolId id) { + return slabAllocator_.getPoolUsedSize(id); + } + private: // @param memory pointer to the memory. // @return the MemoryPool corresponding to the memory. diff --git a/cachelib/allocator/memory/SlabAllocator.cpp b/cachelib/allocator/memory/SlabAllocator.cpp index a5cc8b12bf..e8d0534c1a 100644 --- a/cachelib/allocator/memory/SlabAllocator.cpp +++ b/cachelib/allocator/memory/SlabAllocator.cpp @@ -91,6 +91,13 @@ SlabAllocator::~SlabAllocator() { } } +size_t SlabAllocator::getPoolUsedSize(PoolId id) { + if (id >= memoryPoolSize_.size()) { + throw std::invalid_argument(folly::sformat("Invalid pool id {}.", id)); + } + return memoryPoolSize_[id]; +} + void SlabAllocator::stopMemoryLocker() { if (memoryLocker_.joinable()) { stopLocking_ = true; diff --git a/cachelib/allocator/memory/SlabAllocator.h b/cachelib/allocator/memory/SlabAllocator.h index 7d11bf6bc9..87321fa420 100644 --- a/cachelib/allocator/memory/SlabAllocator.h +++ b/cachelib/allocator/memory/SlabAllocator.h @@ -316,6 +316,10 @@ class SlabAllocator { return PtrCompressor(*this); } + // Retrive used size of a pool + // @return used size of Pool + size_t getPoolUsedSize(PoolId id); + private: // null Slab* presenttation. With 4M Slab size, a valid slab index would never // reach 2^16 - 1; diff --git a/cachelib/allocator/tests/AllocatorHitStatsTest.h b/cachelib/allocator/tests/AllocatorHitStatsTest.h index d2f3ba3c34..1857aaea7f 100644 --- a/cachelib/allocator/tests/AllocatorHitStatsTest.h +++ b/cachelib/allocator/tests/AllocatorHitStatsTest.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "cachelib/allocator/CacheAllocator.h" @@ -246,15 +247,35 @@ class AllocatorHitStatsTest : public SlabAllocatorTestBase { // allocations across a set of sizes. std::vector keys; const unsigned int nKeys = 1000; - unsigned int initialAllocs = 0; + const unsigned int nChkPoint = 20; + unsigned int initialAllocs = 0, successAllocs = 0; + std::unordered_map pool1Alloc; while (keys.size() != nKeys) { const auto keyLen = folly::Random::rand32(10, 100); const auto allocSize = folly::Random::rand32(100, 1024 * 1024 - 1000); auto str = cachelib::test_util::getRandomAsciiStr(keyLen); ++initialAllocs; + auto classId = alloc.getAllocClassId(poolId, str, allocSize); auto handle = util::allocateAccessible(alloc, poolId, str, allocSize); if (handle) { keys.push_back(str); + pool1Alloc[classId] += alloc.getAllocationSize(classId, poolId); + if (++successAllocs == nChkPoint) { + // This check helps to verify accuracy of pool used size before + // evictions start to occur. Adjust pool alloc size for each class + // to its ceiling slab size and calculate the total. This should + // match the pool used size reported by global stats counter. + uint64_t pool1AllocSize = 0; + for (const auto& cAlloc : pool1Alloc) { + pool1AllocSize += (cAlloc.second + Slab::kSize - 1) / Slab::kSize; + } + pool1AllocSize *= Slab::kSize; + const auto tempCacheStats = alloc.getGlobalCacheStats(); + ASSERT_EQ(tempCacheStats.numEvictions, 0); + ASSERT_EQ(tempCacheStats.poolUsedSize.size(), + MemoryPoolManager::kMaxPools); + ASSERT_EQ(tempCacheStats.poolUsedSize[0], pool1AllocSize); + } } } diff --git a/cachelib/cachebench/cache/Cache-inl.h b/cachelib/cachebench/cache/Cache-inl.h index ec0b42d6ba..32cf07d9ff 100644 --- a/cachelib/cachebench/cache/Cache-inl.h +++ b/cachelib/cachebench/cache/Cache-inl.h @@ -460,6 +460,7 @@ Stats Cache::getStats() const { ret.allocAttempts = cacheStats.allocAttempts; ret.allocFailures = cacheStats.allocFailures; + ret.poolUsedSize = cacheStats.poolUsedSize; ret.numCacheGets = cacheStats.numCacheGets; ret.numCacheGetMiss = cacheStats.numCacheGetMiss; ret.numRamDestructorCalls = cacheStats.numRamDestructorCalls; diff --git a/cachelib/cachebench/cache/CacheStats.h b/cachelib/cachebench/cache/CacheStats.h index e67edae69c..29e69b7703 100644 --- a/cachelib/cachebench/cache/CacheStats.h +++ b/cachelib/cachebench/cache/CacheStats.h @@ -32,6 +32,7 @@ struct Stats { uint64_t allocAttempts{0}; uint64_t allocFailures{0}; + std::vector poolUsedSize; uint64_t numCacheGets{0}; uint64_t numCacheGetMiss{0}; uint64_t numRamDestructorCalls{0}; @@ -116,6 +117,12 @@ struct Stats { << std::endl; out << folly::sformat("RAM Evictions : {:,}", numEvictions) << std::endl; + for (auto pid = 0U; pid < poolUsedSize.size(); pid++) { + out << folly::sformat("Pool {:,} Used size : {:,} Bytes", pid, + poolUsedSize[pid]) + << std::endl; + } + if (numCacheGets > 0) { out << folly::sformat("Cache Gets : {:,}", numCacheGets) << std::endl; out << folly::sformat("Hit Ratio : {:6.2f}%", overallHitRatio)