cachelib/allocator/CacheStats.cpp (254 lines of code) (raw):

/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "cachelib/allocator/CacheStats.h" #include "cachelib/allocator/CacheStatsInternal.h" namespace facebook { namespace cachelib { namespace detail { void Stats::init() { cacheHits = std::make_unique<PerPoolClassTLCounters>(); allocAttempts = std::make_unique<PerPoolClassAtomicCounters>(); fragmentationSize = std::make_unique<PerPoolClassAtomicCounters>(); allocFailures = std::make_unique<PerPoolClassAtomicCounters>(); chainedItemEvictions = std::make_unique<PerPoolClassAtomicCounters>(); regularItemEvictions = std::make_unique<PerPoolClassAtomicCounters>(); auto initToZero = [](auto& a) { for (auto& s : a) { for (auto& c : s) { c.set(0); } } }; initToZero(*allocAttempts); initToZero(*allocFailures); initToZero(*fragmentationSize); initToZero(*chainedItemEvictions); initToZero(*regularItemEvictions); } template <int> struct SizeVerify {}; void Stats::populateGlobalCacheStats(GlobalCacheStats& ret) const { #ifndef SKIP_SIZE_VERIFY SizeVerify<sizeof(Stats)> a = SizeVerify<16144>{}; std::ignore = a; #endif ret.numCacheGets = numCacheGets.get(); ret.numCacheGetMiss = numCacheGetMiss.get(); ret.numCacheGetExpiries = numCacheGetExpiries.get(); ret.numCacheRemoves = numCacheRemoves.get(); ret.numCacheRemoveRamHits = numCacheRemoveRamHits.get(); ret.numRamDestructorCalls = numRamDestructorCalls.get(); ret.numDestructorExceptions = numDestructorExceptions.get(); ret.numNvmGets = numNvmGets.get(); ret.numNvmGetMiss = numNvmGetMiss.get(); ret.numNvmGetMissFast = numNvmGetMissFast.get(); ret.numNvmGetMissExpired = numNvmGetMissExpired.get(); ret.numNvmGetMissDueToInflightRemove = numNvmGetMissDueToInflightRemove.get(); ret.numNvmGetMissErrs = numNvmGetMissErrs.get(); ret.numNvmGetCoalesced = numNvmGetCoalesced.get(); ret.numNvmPuts = numNvmPuts.get(); ret.numNvmDeletes = numNvmDeletes.get(); ret.numNvmSkippedDeletes = numNvmSkippedDeletes.get(); ret.numNvmPutErrs = numNvmPutErrs.get(); ret.numNvmPutEncodeFailure = numNvmPutEncodeFailure.get(); ret.numNvmAbortedPutOnTombstone += numNvmAbortedPutOnTombstone.get(); ret.numNvmCompactionFiltered += numNvmCompactionFiltered.get(); ret.numNvmAbortedPutOnInflightGet = numNvmAbortedPutOnInflightGet.get(); ret.numNvmCleanEvict = numNvmCleanEvict.get(); ret.numNvmCleanDoubleEvict = numNvmCleanDoubleEvict.get(); ret.numNvmDestructorCalls = numNvmDestructorCalls.get(); ret.numNvmDestructorRefcountOverflow = numNvmDestructorRefcountOverflow.get(); ret.numNvmExpiredEvict = numNvmExpiredEvict.get(); ret.numNvmPutFromClean = numNvmPutFromClean.get(); ret.numNvmEvictions = numNvmEvictions.get(); ret.numNvmEncryptionErrors = numNvmEncryptionErrors.get(); ret.numNvmDecryptionErrors = numNvmDecryptionErrors.get(); ret.numNvmRejectsByExpiry = numNvmRejectsByExpiry.get(); ret.numNvmRejectsByClean = numNvmRejectsByClean.get(); ret.numNvmRejectsByAP = numNvmRejectsByAP.get(); ret.numChainedParentItems = numChainedParentItems.get(); ret.numChainedChildItems = numChainedChildItems.get(); ret.numNvmAllocAttempts = numNvmAllocAttempts.get(); ret.numNvmAllocForItemDestructor = numNvmAllocForItemDestructor.get(); ret.numNvmItemDestructorAllocErrors = numNvmItemDestructorAllocErrors.get(); ret.allocateLatencyNs = this->allocateLatency_.estimate(); ret.moveChainedLatencyNs = this->moveChainedLatency_.estimate(); ret.moveRegularLatencyNs = this->moveRegularLatency_.estimate(); ret.nvmLookupLatencyNs = this->nvmLookupLatency_.estimate(); ret.nvmInsertLatencyNs = this->nvmInsertLatency_.estimate(); ret.nvmRemoveLatencyNs = this->nvmRemoveLatency_.estimate(); ret.ramEvictionAgeSecs = this->ramEvictionAgeSecs_.estimate(); ret.ramItemLifeTimeSecs = this->ramItemLifeTimeSecs_.estimate(); ret.nvmSmallLifetimeSecs = this->nvmSmallLifetimeSecs_.estimate(); ret.nvmLargeLifetimeSecs = this->nvmLargeLifetimeSecs_.estimate(); ret.nvmEvictionSecondsPastExpiry = this->nvmEvictionSecondsPastExpiry_.estimate(); ret.nvmEvictionSecondsToExpiry = this->nvmEvictionSecondsToExpiry_.estimate(); ret.nvmPutSize = this->nvmPutSize_.estimate(); auto accum = [](const PerPoolClassAtomicCounters& c) { uint64_t sum = 0; for (const auto& x : c) { for (const auto& v : x) { sum += v.get(); } } return sum; }; ret.allocAttempts = accum(*allocAttempts); ret.allocFailures = accum(*allocFailures); ret.numEvictions = accum(*chainedItemEvictions); ret.numEvictions += accum(*regularItemEvictions); ret.invalidAllocs = invalidAllocs.get(); ret.numRefcountOverflow = numRefcountOverflow.get(); ret.numEvictionFailureFromAccessContainer = evictFailAC.get(); ret.numEvictionFailureFromConcurrentFill = evictFailConcurrentFill.get(); ret.numEvictionFailureFromParentAccessContainer = evictFailParentAC.get(); ret.numEvictionFailureFromMoving = evictFailMove.get(); ret.numEvictionFailureFromParentMoving = evictFailParentMove.get(); ret.numAbortedSlabReleases = numAbortedSlabReleases.get(); } } // namespace detail PoolStats& PoolStats::operator+=(const PoolStats& other) { auto verify = [](bool isCompatible) { if (!isCompatible) { throw std::invalid_argument( "attempting to aggregate incompatible pool stats"); } }; XDCHECK_EQ(cacheStats.size(), mpStats.acStats.size()); verify(cacheStats.size() == other.cacheStats.size()); verify(mpStats.acStats.size() == other.mpStats.acStats.size()); verify(getClassIds() == other.getClassIds()); // aggregate mp stats { auto& d = mpStats; const auto& s = other.mpStats; d.freeSlabs += s.freeSlabs; d.slabsUnAllocated += s.slabsUnAllocated; d.numSlabResize += s.numSlabResize; d.numSlabRebalance += s.numSlabRebalance; d.numSlabAdvise += s.numSlabAdvise; } for (const ClassId i : other.getClassIds()) { verify(cacheStats.at(i).allocSize == other.cacheStats.at(i).allocSize); // aggregate CacheStat stats { auto& d = cacheStats.at(i); const auto& s = other.cacheStats.at(i); d.allocAttempts += s.allocAttempts; d.allocFailures += s.allocFailures; d.fragmentationSize += s.fragmentationSize; d.numHits += s.numHits; d.chainedItemEvictions += s.chainedItemEvictions; d.regularItemEvictions += s.regularItemEvictions; } // aggregate container stats within CacheStat { auto& d = cacheStats.at(i).containerStat; const auto& s = other.cacheStats.at(i).containerStat; d.size += s.size; if (d.oldestTimeSec < s.oldestTimeSec) { d.oldestTimeSec = s.oldestTimeSec; } d.numHotAccesses += s.numHotAccesses; d.numColdAccesses += s.numColdAccesses; d.numWarmAccesses += s.numWarmAccesses; } // aggregate ac stats { auto& d = mpStats.acStats.at(i); const auto& s = other.mpStats.acStats.at(i); // allocsPerSlab is fixed for each allocation class, and thus // there is no need to aggregate it /* d.allocsPerSlab */ d.usedSlabs += s.usedSlabs; d.freeSlabs += s.freeSlabs; d.freeAllocs += s.freeAllocs; d.activeAllocs += s.activeAllocs; d.full = d.full && s.full ? true : false; } } // aggregate rest of PoolStats numPoolGetHits += other.numPoolGetHits; return *this; } uint64_t PoolStats::numFreeAllocs() const noexcept { return mpStats.numFreeAllocs(); } size_t PoolStats::freeMemoryBytes() const noexcept { return mpStats.freeMemory(); } uint64_t PoolStats::numEvictions() const noexcept { uint64_t n = 0; for (const auto& s : cacheStats) { n += s.second.numEvictions(); } return n; } uint64_t PoolStats::numItems() const noexcept { uint64_t n = 0; for (const auto& s : cacheStats) { n += s.second.numItems(); } return n; } uint64_t PoolStats::numAllocFailures() const { uint64_t n = 0; for (const auto& s : cacheStats) { n += s.second.allocFailures; } return n; } uint64_t PoolStats::numAllocAttempts() const { uint64_t n = 0; for (const auto& s : cacheStats) { n += s.second.allocAttempts; } return n; } uint64_t PoolStats::totalFragmentation() const { uint64_t n = 0; for (const auto& s : cacheStats) { n += s.second.fragmentationSize; } return n; } uint64_t PoolStats::numActiveAllocs() const noexcept { uint64_t n = 0; for (const auto& ac : mpStats.acStats) { n += ac.second.activeAllocs; } return n; } uint64_t PoolStats::minEvictionAge() const { if (isCompactCache) { return 0; } XDCHECK_GT(cacheStats.size(), 0u); // treat 0 eviction age as higher values so that we filter that out for min. // 0 eviction age usually means we dont have anything in that classId. a is // min than b only when a is not 0 and is less than b or if b is 0. all // other cases, a < b is false. return std::min_element(cacheStats.begin(), cacheStats.end(), [](auto& a, auto& b) { uint64_t aVal = a.second.getEvictionAge(); uint64_t bVal = b.second.getEvictionAge(); return (aVal != 0 && aVal < bVal) || bVal == 0; }) ->second.getEvictionAge(); } uint64_t PoolStats::maxEvictionAge() const { if (isCompactCache) { return 0; } XDCHECK_GT(cacheStats.size(), 0u); return std::max_element(cacheStats.begin(), cacheStats.end(), [](auto& a, auto& b) { return a.second.getEvictionAge() < b.second.getEvictionAge(); }) ->second.getEvictionAge(); } namespace { double hitRatioCalc(uint64_t ops, uint64_t miss) { return miss == 0 || ops == 0 ? 100.0 : 100.0 - 100.0 * static_cast<double>(miss) / static_cast<double>(ops); } } // namespace uint64_t PoolStats::numEvictableItems() const noexcept { uint64_t n = 0; for (const auto& s : cacheStats) { n += s.second.numEvictableItems(); } return n; } double CCacheStats::hitRatio() const { return hitRatioCalc(get, getMiss); } } // namespace cachelib } // namespace facebook