cachelib/benchmarks/ItemsReaperBench.cpp (118 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 <folly/Benchmark.h>
#include <folly/Random.h>
#include <folly/init/Init.h>
#include <folly/lang/Bits.h>
#include <folly/logging/xlog.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <iomanip>
#include "cachelib/allocator/CacheAllocator.h"
#include "cachelib/common/TestUtils.h"
using namespace facebook::cachelib;
DEFINE_uint64(num_keys, 1 << 20, "Number of keys to populate the cache with");
DEFINE_uint64(size_gb, 10, "Size of the cache in gb");
DEFINE_uint32(sleep_ms, 10, "reaper throttler sleep time");
DEFINE_uint32(work_ms, 5, "reaper throttler work interval");
DEFINE_uint32(print_interval_s,
100,
"interval at which to print reaper speed. 0 means disabled");
DEFINE_uint32(benchmark_duration_s, 300, "how long to run the benchmark");
namespace {
constexpr unsigned int kMaxKeySize = 255;
constexpr unsigned int kMinKeySize = 50;
constexpr unsigned int kMaxAllocSize = Slab::kSize - 500;
constexpr unsigned int kMinAllocSize = 100;
constexpr unsigned int kAllocSizeMean = 4096;
constexpr unsigned int kAllocSizeDev = 10 * 4096;
static uint32_t getRandomAllocSize(double val) {
if (val < kMinAllocSize) {
return kMinAllocSize;
} else if (val > kMaxAllocSize) {
return kMaxAllocSize;
} else {
return static_cast<uint32_t>(std::round(val));
}
}
} // namespace
int main(int argc, char** argv) {
folly::init(&argc, &argv);
XLOG(INFO) << "initializing cache";
LruAllocator::AccessConfig accessConfig(
folly::findLastSet(FLAGS_num_keys) + 1, 10);
LruAllocator::Config lruConfig;
lruConfig.setCacheSize(FLAGS_size_gb * 1024 * 1024 * 1024);
lruConfig.setAccessConfig(accessConfig);
lruConfig.enableItemReaperInBackground(
std::chrono::milliseconds{FLAGS_sleep_ms},
util::Throttler::Config{FLAGS_sleep_ms, FLAGS_work_ms});
assert(lruConfig.itemsReaperEnabled());
LruAllocator cache(lruConfig);
const auto poolId =
cache.addPool("default", cache.getCacheMemoryStats().cacheSize);
XLOG(INFO) << "allocating items";
std::mt19937 gen(folly::Random::rand32());
std::normal_distribution<> sizeDis(kAllocSizeMean, kAllocSizeDev);
std::uniform_int_distribution<uint32_t> keySize(kMinKeySize, kMaxKeySize);
std::uniform_int_distribution<uint32_t> ttlDist(0,
FLAGS_benchmark_duration_s);
std::uniform_int_distribution<uint8_t> charDis(0, 25);
for (uint64_t i = 0; i < FLAGS_num_keys; ++i) {
util::allocateAccessible(
cache, poolId,
facebook::cachelib::test_util::getRandomAsciiStr(keySize(gen)),
getRandomAllocSize(sizeDis(gen)), ttlDist(gen));
}
auto reaperStatStr = [](const facebook::cachelib::ReaperStats& stats) {
auto str = folly::sformat(
"numTraversals: {:8d}, numVisits: {:12d}, lastTraversalMs: {:6d}ms, "
"avgTraversalMs: {:6d}ms, maxTraversalMs: {:6d}",
stats.numTraversals, stats.numVisitedItems, stats.lastTraversalTimeMs,
stats.avgTraversalTimeMs, stats.maxTraversalTimeMs);
return str;
};
struct rusage rUsageBefore = {};
struct rusage rUsageAfter = {};
XLOG(INFO) << "Gathering reaper metrics";
if (::getrusage(RUSAGE_SELF, &rUsageBefore)) {
XLOG(INFO) << "Error getting rusage" << errno;
return 0;
}
const auto start = std::chrono::steady_clock::now();
const auto startStats = cache.getReaperStats();
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
if (FLAGS_print_interval_s) {
XLOG_EVERY_MS(INFO, FLAGS_print_interval_s * 1000)
<< reaperStatStr(cache.getReaperStats());
}
if (std::chrono::steady_clock::now() - start >
std::chrono::seconds(FLAGS_benchmark_duration_s)) {
break;
}
}
if (::getrusage(RUSAGE_SELF, &rUsageAfter)) {
XLOG(INFO) << "Error getting rusage" << errno;
return 0;
}
auto finalStats = cache.getReaperStats();
double memoryScanned =
FLAGS_size_gb * (finalStats.numTraversals - startStats.numTraversals);
auto timeTaken = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::steady_clock::now() - start);
XLOG(INFO) << reaperStatStr(finalStats);
XLOG(INFO) << folly::sformat("bytes scanned : {:12.2f}gb/sec",
memoryScanned / timeTaken.count());
auto getTimeRUsage = [](const struct rusage& r) {
double userSeconds = r.ru_utime.tv_sec + r.ru_utime.tv_usec / 1e6;
double sysSeconds = r.ru_stime.tv_sec + r.ru_stime.tv_usec / 1e6;
return std::make_tuple(userSeconds, sysSeconds, userSeconds + sysSeconds);
};
auto [beforeUser, beforeSys, beforeTot] = getTimeRUsage(rUsageBefore);
auto [afterUser, afterSys, afterTot] = getTimeRUsage(rUsageAfter);
// compute core cpu util by divinding the time spent on a core with overall
// time spent to complete the operation.
XLOG(INFO) << folly::sformat(
"cpu util: user: {:3.6}s sys: {:3.6f}s total: {:3.6f}s util-pct: "
"{:2.2f}%",
afterUser - beforeUser, afterSys - beforeSys, afterTot - beforeTot,
100.0 * (afterTot - beforeTot) / timeTaken.count());
return 0;
}