Skip to content

Commit a293745

Browse files
rlyerlymeta-codesync[bot]
authored andcommitted
Allow Stressors to implement their own Stats
Summary: The current stats implementation in cachebench assumes the old model where you have a CacheAllocator that has a RAM and flash cache. This doesn't work for CacheComponent (which explicitly breaks these apart) and trying to force CacheComponents into the existing stats reporting is too cumbersome. Instead, make this interface generic instead to allow stressors to implement their own stats collection & reporting. Follow-on diffs will implement CacheComponent stats reporting. Reviewed By: AlnisM Differential Revision: D92316026 fbshipit-source-id: 55fe502f07e05ef343b0cef86b2b8365806e3568
1 parent c329732 commit a293745

18 files changed

Lines changed: 253 additions & 90 deletions

cachelib/cachebench/cache/Cache.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ class Cache {
322322
}
323323

324324
// Get overall stats on the whole cache allocator
325-
Stats getStats() const;
325+
std::unique_ptr<StatsBase> getStats() const;
326326

327327
// Get number of bytes written to NVM.
328328
double getNvmBytesWritten() const;
@@ -1114,12 +1114,14 @@ double Cache<Allocator>::getNvmBytesWritten() const {
11141114
}
11151115

11161116
template <typename Allocator>
1117-
Stats Cache<Allocator>::getStats() const {
1117+
std::unique_ptr<StatsBase> Cache<Allocator>::getStats() const {
1118+
auto retPtr = std::make_unique<Stats>();
1119+
auto& ret = *retPtr;
1120+
11181121
PoolStats aggregate = cache_->getPoolStats(pools_[0]);
11191122
auto usageFraction =
11201123
1.0 - (static_cast<double>(aggregate.freeMemoryBytes())) /
11211124
aggregate.poolUsableSize;
1122-
Stats ret;
11231125
ret.poolUsageFraction.push_back(usageFraction);
11241126
for (size_t pid = 1; pid < pools_.size(); pid++) {
11251127
auto poolStats = cache_->getPoolStats(static_cast<PoolId>(pid));
@@ -1292,7 +1294,7 @@ Stats Cache<Allocator>::getStats() const {
12921294
}
12931295
}
12941296

1295-
return ret;
1297+
return retPtr;
12961298
}
12971299

12981300
template <typename Allocator>

cachelib/cachebench/cache/CacheStats.h

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "cachelib/allocator/memory/MemoryAllocatorStats.h"
2222
#include "cachelib/allocator/memory/Slab.h"
23+
#include "cachelib/cachebench/cache/StatsBase.h"
2324
#include "cachelib/common/PercentileStats.h"
2425

2526
DECLARE_bool(report_api_latency);
@@ -51,7 +52,8 @@ struct BackgroundPromotionStats {
5152
uint64_t nTraversals{0};
5253
};
5354

54-
struct Stats {
55+
class Stats : public StatsBase {
56+
public:
5557
BackgroundEvictionStats backgndEvicStats;
5658
BackgroundPromotionStats backgndPromoStats;
5759

@@ -169,7 +171,10 @@ struct Stats {
169171

170172
// Aggregate throughput stats from another instance. DOES NOT HANDLE
171173
// LATENCY STATS!
172-
Stats& operator+=(const Stats& other) {
174+
Stats& operator+=(const StatsBase& otherBase) override {
175+
aggregated_ = true;
176+
auto& other = otherBase.as<Stats>();
177+
173178
backgndEvicStats.nEvictedItems += other.backgndEvicStats.nEvictedItems;
174179
backgndEvicStats.nTraversals += other.backgndEvicStats.nTraversals;
175180
backgndEvicStats.nClasses += other.backgndEvicStats.nClasses;
@@ -241,7 +246,21 @@ struct Stats {
241246
return *this;
242247
}
243248

244-
void render(std::ostream& out, bool isAggregate = false) const {
249+
std::string progress(const StatsBase& prevStatsBase) const override {
250+
const auto& prevStats = prevStatsBase.as<Stats>();
251+
auto hitRates = getHitRatios(prevStats);
252+
return folly::sformat(
253+
"{} items in cache. {} items in nvm cache. {} items evicted from nvm "
254+
"cache. Hit Ratio {:6.2f}% (RAM {:6.2f}%, NVM {:6.2f}%).",
255+
numItems,
256+
numNvmItems,
257+
numNvmEvictions,
258+
hitRates["overall"],
259+
hitRates["ram"],
260+
hitRates["nvm"]);
261+
}
262+
263+
void render(std::ostream& out) const override {
245264
auto totalMisses = getTotalMisses();
246265
const double overallHitRatio = invertPctFn(totalMisses, numCacheGets);
247266
out << folly::sformat("Items in RAM : {:,}", numItems) << std::endl;
@@ -265,7 +284,7 @@ struct Stats {
265284
}
266285
};
267286

268-
if (!isAggregate) {
287+
if (!aggregated_) {
269288
for (auto pid = 0U; pid < poolUsageFraction.size(); pid++) {
270289
out << folly::sformat("Fraction of pool {:,} used : {:.2f}", pid,
271290
poolUsageFraction[pid])
@@ -334,7 +353,7 @@ struct Stats {
334353
out << folly::sformat("Hit Ratio : {:6.2f}%", overallHitRatio)
335354
<< std::endl;
336355

337-
if (FLAGS_report_api_latency && !isAggregate) {
356+
if (FLAGS_report_api_latency && !aggregated_) {
338357
auto printLatencies =
339358
[&out](folly::StringPiece cat,
340359
const util::PercentileStats::Estimates& latency) {
@@ -362,7 +381,7 @@ struct Stats {
362381
if (!backgroundEvictionClasses.empty() &&
363382
backgndEvicStats.nEvictedItems > 0) {
364383
out << "== Class Background Eviction Counters Map ==" << std::endl;
365-
if (!isAggregate) {
384+
if (!aggregated_) {
366385
foreachAC(backgroundEvictionClasses,
367386
[&](auto pid, auto cid, auto evicted) {
368387
out << folly::sformat("pid{:2} cid{:4} evicted: {:4}", pid,
@@ -382,7 +401,7 @@ struct Stats {
382401
if (!backgroundPromotionClasses.empty() &&
383402
backgndPromoStats.nPromotedItems > 0) {
384403
out << "== Class Background Promotion Counters Map ==" << std::endl;
385-
if (!isAggregate) {
404+
if (!aggregated_) {
386405
foreachAC(backgroundPromotionClasses,
387406
[&](auto pid, auto cid, auto promoted) {
388407
out << folly::sformat("pid{:2} cid{:4} promoted: {:4}", pid,
@@ -413,7 +432,7 @@ struct Stats {
413432
"{:,}\n",
414433
numNvmRejectsByExpiry, numNvmRejectsByClean);
415434

416-
if (!isAggregate) {
435+
if (!aggregated_) {
417436
folly::StringPiece readCat = "NVM Read Latency";
418437
folly::StringPiece writeCat = "NVM Write Latency";
419438
auto fmtLatency = [&](folly::StringPiece cat, folly::StringPiece pct,
@@ -575,51 +594,55 @@ struct Stats {
575594
return numNvmGets > 0 ? numNvmGetMiss : numCacheGetMiss;
576595
}
577596

578-
std::tuple<double, double, double> getHitRatios(
579-
const Stats& prevStats) const {
580-
double overallHitRatio = 0.0;
581-
double ramHitRatio = 0.0;
582-
double nvmHitRatio = 0.0;
597+
std::map<std::string, double> getHitRatios(
598+
const StatsBase& prevStatsBase) const override {
599+
auto& prevStats = prevStatsBase.as<Stats>();
600+
std::map<std::string, double> rates;
601+
rates["overall"] = 0.0;
602+
rates["ram"] = 0.0;
603+
rates["nvm"] = 0.0;
583604

584605
if (numCacheGets > prevStats.numCacheGets) {
585606
auto totalMisses = getTotalMisses();
586607
auto prevTotalMisses = prevStats.getTotalMisses();
587608

588-
overallHitRatio = invertPctFn(totalMisses - prevTotalMisses,
589-
numCacheGets - prevStats.numCacheGets);
609+
rates["overall"] = invertPctFn(totalMisses - prevTotalMisses,
610+
numCacheGets - prevStats.numCacheGets);
590611

591-
ramHitRatio = invertPctFn(numCacheGetMiss - prevStats.numCacheGetMiss,
592-
numCacheGets - prevStats.numCacheGets);
612+
rates["ram"] = invertPctFn(numCacheGetMiss - prevStats.numCacheGetMiss,
613+
numCacheGets - prevStats.numCacheGets);
593614
}
594615

595616
if (numNvmGets > prevStats.numNvmGets) {
596-
nvmHitRatio = invertPctFn(numNvmGetMiss - prevStats.numNvmGetMiss,
597-
numNvmGets - prevStats.numNvmGets);
617+
rates["nvm"] = invertPctFn(numNvmGetMiss - prevStats.numNvmGetMiss,
618+
numNvmGets - prevStats.numNvmGets);
598619
}
599620

600-
return std::make_tuple(overallHitRatio, ramHitRatio, nvmHitRatio);
621+
return rates;
601622
}
602623

603624
// Render the stats based on the delta between overall stats and previous
604625
// stats. It can be used to render the stats in the last time period.
605-
void render(const Stats& prevStats, std::ostream& out) const {
626+
void render(const StatsBase& prevStatsBase,
627+
std::ostream& out) const override {
628+
auto& prevStats = prevStatsBase.as<Stats>();
629+
606630
if (numCacheGets > prevStats.numCacheGets) {
607-
auto [overallHitRatio, ramHitRatio, nvmHitRatio] =
608-
getHitRatios(prevStats);
631+
auto rates = getHitRatios(prevStatsBase);
609632
out << folly::sformat("Cache Gets : {:,}",
610633
numCacheGets - prevStats.numCacheGets)
611634
<< std::endl;
612-
out << folly::sformat("Hit Ratio : {:6.2f}%", overallHitRatio)
635+
out << folly::sformat("Hit Ratio : {:6.2f}%", rates["overall"])
613636
<< std::endl;
614637

615638
out << folly::sformat(
616639
"RAM Hit Ratio : {:6.2f}%\n"
617640
"NVM Hit Ratio : {:6.2f}%\n",
618-
ramHitRatio, nvmHitRatio);
641+
rates["ram"], rates["nvm"]);
619642
}
620643
}
621644

622-
void render(folly::UserCounters& counters) {
645+
void render(folly::UserCounters& counters) const override {
623646
auto calcInvertPctFn = [](uint64_t ops, uint64_t total) {
624647
return static_cast<int64_t>(invertPctFn(ops, total) * 100);
625648
};
@@ -657,7 +680,7 @@ struct Stats {
657680
counters["nvm_dev_write_amp"] = static_cast<int64_t>(devWriteAmp);
658681
}
659682

660-
bool renderIsTestPassed(std::ostream& out) {
683+
bool renderIsTestPassed(std::ostream& out) const override {
661684
bool pass = true;
662685

663686
if (isNvmCacheDisabled) {
@@ -697,6 +720,8 @@ struct Stats {
697720
}
698721

699722
private:
723+
bool aggregated_{false};
724+
700725
static double pctFn(uint64_t ops, uint64_t total) {
701726
return total == 0
702727
? 0
@@ -706,8 +731,7 @@ struct Stats {
706731
static double invertPctFn(uint64_t ops, uint64_t total) {
707732
return 100 - pctFn(ops, total);
708733
}
709-
710-
}; // namespace cachebench
734+
};
711735

712736
} // namespace cachebench
713737
} // namespace cachelib
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <folly/Benchmark.h>
20+
#include <folly/logging/xlog.h>
21+
22+
#include <map>
23+
#include <ostream>
24+
#include <string>
25+
26+
namespace facebook {
27+
namespace cachelib {
28+
namespace cachebench {
29+
30+
/**
31+
* Interface for cache statistics. Different stressors can use different stats
32+
* implementations while providing a common interface for rendering and
33+
* calculating hit rates.
34+
*/
35+
class StatsBase {
36+
public:
37+
virtual ~StatsBase() = default;
38+
39+
/**
40+
* Add the stats from another stats object to this one. It should only ever be
41+
* called with two of the same sub-types, i.e., you don't have to deal with
42+
* trying to aggregate child class A and child class B.
43+
*
44+
* @param otherBase other stats object
45+
* @return reference to this
46+
*/
47+
virtual StatsBase& operator+=(const StatsBase& otherBase) = 0;
48+
49+
/**
50+
* Get a string with some brief summary statistics for printing progress.
51+
* @return progress string
52+
*/
53+
virtual std::string progress(const StatsBase& prevStats) const = 0;
54+
55+
/**
56+
* Render the current stats to the output stream.
57+
* @param out The output stream to write to.
58+
*/
59+
virtual void render(std::ostream& out) const = 0;
60+
61+
/**
62+
* Render stats as a delta from previous stats to show changes over time.
63+
* @param prevStats The previous stats snapshot to calculate deltas from.
64+
* @param out The output stream to write to.
65+
*/
66+
virtual void render(const StatsBase& prevStats, std::ostream& out) const = 0;
67+
68+
/**
69+
* Render stats to folly::UserCounters for benchmark integration.
70+
* @param counters The counter map to populate.
71+
*/
72+
virtual void render(folly::UserCounters& counters) const = 0;
73+
74+
/**
75+
* Calculate hit rates as deltas from previous stats.
76+
* @param prevStats The previous stats snapshot to calculate deltas from.
77+
* @return Map of hit rate metric names to their delta values (0.0 - 100.0).
78+
*/
79+
virtual std::map<std::string, double> getHitRatios(
80+
const StatsBase& prevStats) const = 0;
81+
82+
/**
83+
* Render test pass/fail status and return whether the test passed.
84+
* @param out The output stream to write status messages to.
85+
* @return true if the test passed, false otherwise.
86+
*/
87+
virtual bool renderIsTestPassed(std::ostream& out) const = 0;
88+
89+
/**
90+
* Helper to cast a StatsBase to a child. Returns a nullptr if not casting to
91+
* the correct type.
92+
*
93+
* @return this cast to a child type pointer
94+
*/
95+
template <typename ChildT>
96+
ChildT* asPtr() const {
97+
return dynamic_cast<ChildT*>(this);
98+
}
99+
100+
/**
101+
* Helper to cast a StatsBase to a child. Will crash if not casting to the
102+
* correct type.
103+
*
104+
* @return this cast to a child type reference
105+
*/
106+
template <typename ChildT>
107+
const ChildT& as() const {
108+
auto* ret = asPtr<const ChildT>();
109+
XCHECK_NE(ret, nullptr);
110+
return *ret;
111+
}
112+
};
113+
114+
} // namespace cachebench
115+
} // namespace cachelib
116+
} // namespace facebook

cachelib/cachebench/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <thread>
2424
#include <vector>
2525

26+
#include "cachelib/cachebench/cache/Cache.h"
2627
#include "cachelib/cachebench/runner/Runner.h"
2728
#include "cachelib/cachebench/util/AggregateStats.h"
2829
#include "cachelib/cachebench/util/Sleep.h"

cachelib/cachebench/runner/AsyncCacheStressor.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,9 @@ class AsyncCacheStressor : public Stressor {
171171
}
172172

173173
// obtain stats from the cache instance.
174-
Stats getCacheStats() const override { return cache_->getStats(); }
174+
std::unique_ptr<StatsBase> getCacheStats() const override {
175+
return cache_->getStats();
176+
}
175177

176178
// obtain aggregated throughput stats for the stress run so far.
177179
ThroughputStats aggregateThroughputStats() const override {

0 commit comments

Comments
 (0)