Skip to content

[Upstream] Rolling Stats rebased against the upstream AC Stats branch #47

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cachelib/allocator/Cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ class CacheBase {
// @param poolId the pool id
virtual PoolStats getPoolStats(PoolId poolId) const = 0;

// Get Allocation Class specific stats.
//
// @param poolId the pool id
// @param classId the class id
virtual ACStats getACStats(PoolId poolId, ClassId classId) const = 0;

// @param poolId the pool id
virtual AllSlabReleaseEvents getAllSlabReleaseEvents(PoolId poolId) const = 0;

Expand Down
16 changes: 16 additions & 0 deletions cachelib/allocator/CacheAllocator-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ CacheAllocator<CacheTrait>::allocateInternal(PoolId pid,

// the allocation class in our memory allocator.
const auto cid = allocator_->getAllocationClassId(pid, requiredSize);
util::RollingLatencyTracker rollTracker{
(*stats_.classAllocLatency)[pid][cid]};

(*stats_.allocAttempts)[pid][cid].inc();

Expand Down Expand Up @@ -402,6 +404,9 @@ CacheAllocator<CacheTrait>::allocateChainedItemInternal(
const auto pid = allocator_->getAllocInfo(parent->getMemory()).poolId;
const auto cid = allocator_->getAllocationClassId(pid, requiredSize);

util::RollingLatencyTracker rollTracker{
(*stats_.classAllocLatency)[pid][cid]};

(*stats_.allocAttempts)[pid][cid].inc();

void* memory = allocator_->allocate(pid, requiredSize);
Expand Down Expand Up @@ -2220,6 +2225,17 @@ PoolStats CacheAllocator<CacheTrait>::getPoolStats(PoolId poolId) const {
return ret;
}

template <typename CacheTrait>
ACStats CacheAllocator<CacheTrait>::getACStats(PoolId poolId,
ClassId classId) const {
const auto& pool = allocator_->getPool(poolId);
const auto& ac = pool.getAllocationClass(classId);

auto stats = ac.getStats();
stats.allocLatencyNs = (*stats_.classAllocLatency)[poolId][classId];
return stats;
}

template <typename CacheTrait>
PoolEvictionAgeStats CacheAllocator<CacheTrait>::getPoolEvictionAgeStats(
PoolId pid, unsigned int slabProjectionLength) const {
Expand Down
3 changes: 3 additions & 0 deletions cachelib/allocator/CacheAllocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -1175,6 +1175,9 @@ class CacheAllocator : public CacheBase {
// return cache's memory usage stats
CacheMemoryStats getCacheMemoryStats() const override final;

// return stats for Allocation Class
ACStats getACStats(PoolId pid, ClassId cid) const override final;

// return the nvm cache stats map
util::StatsMap getNvmCacheStatsMap() const override final;

Expand Down
4 changes: 3 additions & 1 deletion cachelib/allocator/CacheStats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,16 @@ void Stats::init() {
initToZero(*fragmentationSize);
initToZero(*chainedItemEvictions);
initToZero(*regularItemEvictions);

classAllocLatency = std::make_unique<PerPoolClassRollingStats>();
}

template <int>
struct SizeVerify {};

void Stats::populateGlobalCacheStats(GlobalCacheStats& ret) const {
#ifndef SKIP_SIZE_VERIFY
SizeVerify<sizeof(Stats)> a = SizeVerify<16176>{};
SizeVerify<sizeof(Stats)> a = SizeVerify<16192>{};
std::ignore = a;
#endif
ret.numCacheGets = numCacheGets.get();
Expand Down
8 changes: 8 additions & 0 deletions cachelib/allocator/CacheStatsInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "cachelib/allocator/Cache.h"
#include "cachelib/allocator/memory/MemoryAllocator.h"
#include "cachelib/common/AtomicCounter.h"
#include "cachelib/common/RollingStats.h"

namespace facebook {
namespace cachelib {
Expand Down Expand Up @@ -229,6 +230,13 @@ struct Stats {
std::unique_ptr<PerPoolClassAtomicCounters> chainedItemEvictions{};
std::unique_ptr<PerPoolClassAtomicCounters> regularItemEvictions{};

using PerPoolClassRollingStats =
std::array<std::array<util::RollingStats, MemoryAllocator::kMaxClasses>,
MemoryPoolManager::kMaxPools>;

// rolling latency tracking for every alloc class in every pool
std::unique_ptr<PerPoolClassRollingStats> classAllocLatency{};

// Eviction failures due to parent cannot be removed from access container
AtomicCounter evictFailParentAC{0};

Expand Down
8 changes: 0 additions & 8 deletions cachelib/allocator/memory/AllocationClass.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,6 @@ class AllocationClass {
return static_cast<unsigned int>(Slab::kSize / allocationSize_);
}

// total number of slabs under this AllocationClass.
unsigned int getNumSlabs() const {
return lock_->lock_combine([this]() {
return static_cast<unsigned int>(freeSlabs_.size() +
allocatedSlabs_.size());
});
}

// fetch stats about this allocation class.
ACStats getStats() const;

Expand Down
15 changes: 15 additions & 0 deletions cachelib/allocator/memory/MemoryAllocatorStats.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <unordered_map>

#include "cachelib/allocator/memory/Slab.h"
#include "cachelib/common/RollingStats.h"

namespace facebook {
namespace cachelib {
Expand Down Expand Up @@ -47,13 +48,27 @@ struct ACStats {
// true if the allocation class is full.
bool full;

// Rolling allocation latency (in ns)
util::RollingStats allocLatencyNs;

constexpr unsigned long long totalSlabs() const noexcept {
return freeSlabs + usedSlabs;
}

constexpr size_t getTotalFreeMemory() const noexcept {
return Slab::kSize * freeSlabs + freeAllocs * allocSize;
}

constexpr double usageFraction() const noexcept {
if (usedSlabs == 0)
return 0.0;

return activeAllocs / (usedSlabs * allocsPerSlab);
}

constexpr size_t totalAllocatedSize() const noexcept {
return activeAllocs * allocSize;
}
};

// structure to query stats corresponding to a MemoryPool
Expand Down
1 change: 1 addition & 0 deletions cachelib/allocator/tests/CacheBaseTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class CacheBaseTest : public CacheBase, public SlabAllocatorTestBase {
bool isObjectCache() const override { return false; }
const MemoryPool& getPool(PoolId) const override { return memoryPool_; }
PoolStats getPoolStats(PoolId) const override { return PoolStats(); }
ACStats getACStats(PoolId, ClassId) const { return ACStats(); };
AllSlabReleaseEvents getAllSlabReleaseEvents(PoolId) const override {
return AllSlabReleaseEvents{};
}
Expand Down
9 changes: 9 additions & 0 deletions cachelib/cachebench/cache/Cache-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -609,10 +609,19 @@ Stats Cache<Allocator>::getStats() const {
aggregate += poolStats;
}

std::map<PoolId, std::map<ClassId, ACStats>> allocationClassStats{};

for (size_t pid = 0; pid < pools_.size(); pid++) {
auto cids = cache_->getPoolStats(static_cast<PoolId>(pid)).getClassIds();
for (auto cid : cids)
allocationClassStats[pid][cid] = cache_->getACStats(pid, cid);
}

const auto cacheStats = cache_->getGlobalCacheStats();
const auto rebalanceStats = cache_->getSlabReleaseStats();
const auto navyStats = cache_->getNvmCacheStatsMap().toMap();

ret.allocationClassStats = allocationClassStats;
ret.numEvictions = aggregate.numEvictions();
ret.numItems = aggregate.numItems();
ret.evictAttempts = cacheStats.evictionAttempts;
Expand Down
6 changes: 6 additions & 0 deletions cachelib/cachebench/cache/Cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ DEFINE_bool(report_api_latency,
false,
"Enable reporting cache API latency tracking");

DEFINE_string(
report_ac_memory_usage_stats,
"",
"Enable reporting statistics for each allocation class. Set to"
"'human_readable' to print KB/MB/GB or to 'raw' to print in bytes.");

namespace facebook {
namespace cachelib {
namespace cachebench {} // namespace cachebench
Expand Down
5 changes: 5 additions & 0 deletions cachelib/cachebench/cache/Cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "cachelib/cachebench/util/NandWrites.h"

DECLARE_bool(report_api_latency);
DECLARE_string(report_ac_memory_usage_stats);

namespace facebook {
namespace cachelib {
Expand Down Expand Up @@ -324,6 +325,10 @@ class Cache {
// return the stats for the pool.
PoolStats getPoolStats(PoolId pid) const { return cache_->getPoolStats(pid); }

ACStats getACStats(PoolId pid, ClassId cid) const {
return cache_->getACStats(pid, cid);
}

// return the total number of inconsistent operations detected since start.
unsigned int getInconsistencyCount() const {
return inconsistencyCount_.load(std::memory_order_relaxed);
Expand Down
60 changes: 60 additions & 0 deletions cachelib/cachebench/cache/CacheStats.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "cachelib/common/PercentileStats.h"

DECLARE_bool(report_api_latency);
DECLARE_string(report_ac_memory_usage_stats);

namespace facebook {
namespace cachelib {
Expand Down Expand Up @@ -100,6 +101,8 @@ struct Stats {
uint64_t invalidDestructorCount{0};
int64_t unDestructedItemCount{0};

std::map<PoolId, std::map<ClassId, ACStats>> allocationClassStats;

// populate the counters related to nvm usage. Cache implementation can decide
// what to populate since not all of those are interesting when running
// cachebench.
Expand Down Expand Up @@ -131,6 +134,63 @@ struct Stats {
<< std::endl;
}

if (FLAGS_report_ac_memory_usage_stats != "") {
auto formatMemory = [&](size_t bytes) -> std::tuple<std::string, double> {
if (FLAGS_report_ac_memory_usage_stats == "raw") {
return {"B", bytes};
}

constexpr double KB = 1024.0;
constexpr double MB = 1024.0 * 1024;
constexpr double GB = 1024.0 * 1024 * 1024;

if (bytes >= GB) {
return {"GB", static_cast<double>(bytes) / GB};
} else if (bytes >= MB) {
return {"MB", static_cast<double>(bytes) / MB};
} else if (bytes >= KB) {
return {"KB", static_cast<double>(bytes) / KB};
} else {
return {"B", bytes};
}
};

auto foreachAC = [&](auto cb) {
for (auto& pidStat : allocationClassStats) {
for (auto& cidStat : pidStat.second) {
cb(pidStat.first, cidStat.first, cidStat.second);
}
}
};

foreachAC([&](auto pid, auto cid, auto stats) {
auto [allocSizeSuffix, allocSize] = formatMemory(stats.allocSize);
auto [memorySizeSuffix, memorySize] =
formatMemory(stats.totalAllocatedSize());
out << folly::sformat("pid{:2} cid{:4} {:8.2f}{} memorySize: {:8.2f}{}",
pid, cid, allocSize, allocSizeSuffix, memorySize,
memorySizeSuffix)
<< std::endl;
});

foreachAC([&](auto pid, auto cid, auto stats) {
auto [allocSizeSuffix, allocSize] = formatMemory(stats.allocSize);

// If the pool is not full, extrapolate usageFraction for AC assuming it
// will grow at the same rate. This value will be the same for all ACs.
auto acUsageFraction = (poolUsageFraction[pid] < 1.0)
? poolUsageFraction[pid]
: stats.usageFraction();

out << folly::sformat(
"pid{:2} cid{:4} {:8.2f}{} usageFraction: {:4.2f} "
"rollingAvgAllocLatency: {:8.2f}ns",
pid, cid, allocSize, allocSizeSuffix, acUsageFraction,
stats.allocLatencyNs.estimate())
<< std::endl;
});
}

if (numCacheGets > 0) {
out << folly::sformat("Cache Gets : {:,}", numCacheGets) << std::endl;
out << folly::sformat("Hit Ratio : {:6.2f}%", overallHitRatio)
Expand Down
90 changes: 90 additions & 0 deletions cachelib/common/RollingStats.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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.
*/

#pragma once

#include <folly/Range.h>
#include <folly/logging/xlog.h>

#include "cachelib/common/Utils.h"

namespace facebook {
namespace cachelib {
namespace util {

class RollingStats {
public:
// track latency by taking the value of duration directly.
void trackValue(double value) {
// This is a highly unlikely scenario where
// cnt_ reaches numerical limits. Skip update
// of the rolling average anymore.
if (cnt_ == std::numeric_limits<uint64_t>::max()) {
cnt_ = 0;
return;
}
auto ratio = static_cast<double>(cnt_) / (cnt_ + 1);
avg_ *= ratio;
++cnt_;
avg_ += value / cnt_;
}

// Return the rolling average.
double estimate() { return avg_; }

private:
double avg_{0};
uint64_t cnt_{0};
};

class RollingLatencyTracker {
public:
explicit RollingLatencyTracker(RollingStats& stats)
: stats_(&stats), begin_(std::chrono::steady_clock::now()) {}
RollingLatencyTracker() {}
~RollingLatencyTracker() {
if (stats_) {
auto tp = std::chrono::steady_clock::now();
auto diffNanos =
std::chrono::duration_cast<std::chrono::nanoseconds>(tp - begin_)
.count();
stats_->trackValue(static_cast<double>(diffNanos));
}
}

RollingLatencyTracker(const RollingLatencyTracker&) = delete;
RollingLatencyTracker& operator=(const RollingLatencyTracker&) = delete;

RollingLatencyTracker(RollingLatencyTracker&& rhs) noexcept
: stats_(rhs.stats_), begin_(rhs.begin_) {
rhs.stats_ = nullptr;
}

RollingLatencyTracker& operator=(RollingLatencyTracker&& rhs) noexcept {
if (this != &rhs) {
this->~RollingLatencyTracker();
new (this) RollingLatencyTracker(std::move(rhs));
}
return *this;
}

private:
RollingStats* stats_{nullptr};
std::chrono::time_point<std::chrono::steady_clock> begin_;
};
} // namespace util
} // namespace cachelib
} // namespace facebook