From b72d9da0f52ac7bbe9131f28a2931304da5b4678 Mon Sep 17 00:00:00 2001 From: igchor Date: Wed, 4 May 2022 06:29:54 -0400 Subject: [PATCH] Extend cachbench with touch value The main purpose of this patch is to better simulate workloads in cachebench. Setting touchValue to true allows to see performance impact of using different mediums for memory cache. --- cachelib/cachebench/cache/Cache-inl.h | 26 +++++++++++++++++++++- cachelib/cachebench/cache/Cache.h | 13 ++++++++++- cachelib/cachebench/runner/CacheStressor.h | 3 ++- cachelib/cachebench/util/Config.cpp | 1 + cachelib/cachebench/util/Config.h | 4 ++++ 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/cachelib/cachebench/cache/Cache-inl.h b/cachelib/cachebench/cache/Cache-inl.h index 62e9e90853..8b726c1aee 100644 --- a/cachelib/cachebench/cache/Cache-inl.h +++ b/cachelib/cachebench/cache/Cache-inl.h @@ -50,8 +50,10 @@ uint64_t Cache::fetchNandWrites() const { template Cache::Cache(const CacheConfig& config, ChainedItemMovingSync movingSync, - std::string cacheDir) + std::string cacheDir, + bool touchValue) : config_(config), + touchValue_(touchValue), nandBytesBegin_{fetchNandWrites()}, itemRecords_(config_.enableItemDestructorCheck) { constexpr size_t MB = 1024ULL * 1024ULL; @@ -396,6 +398,18 @@ typename Cache::WriteHandle Cache::insertOrReplace( return rv; } +template +void Cache::touchValue(const ReadHandle& it) const { + XDCHECK(touchValueEnabled()); + + auto ptr = reinterpret_cast(getMemory(it)); + + /* The accumulate call is intended to access all bytes of the value + * and nothing more. */ + auto sum = std::accumulate(ptr, ptr + getSize(it), 0ULL); + folly::doNotOptimizeAway(sum); +} + template typename Cache::ReadHandle Cache::find(Key key) { auto findFn = [&]() { @@ -406,6 +420,11 @@ typename Cache::ReadHandle Cache::find(Key key) { // find from cache and wait for the result to be ready. auto it = cache_->find(key); it.wait(); + + if (touchValueEnabled()) { + touchValue(it); + } + return it; }; @@ -431,6 +450,11 @@ typename Cache::WriteHandle Cache::findToWrite(Key key) { // find from cache and wait for the result to be ready. auto it = cache_->findToWrite(key); it.wait(); + + if (touchValueEnabled()) { + touchValue(it); + } + return it; }; diff --git a/cachelib/cachebench/cache/Cache.h b/cachelib/cachebench/cache/Cache.h index b31e1c6336..5afbca5c84 100644 --- a/cachelib/cachebench/cache/Cache.h +++ b/cachelib/cachebench/cache/Cache.h @@ -65,9 +65,11 @@ class Cache { // cache. // @param cacheDir optional directory for the cache to enable // persistence across restarts. + // @param touchValue read entire value on find explicit Cache(const CacheConfig& config, ChainedItemMovingSync movingSync = {}, - std::string cacheDir = ""); + std::string cacheDir = "", + bool touchValue = false); ~Cache(); @@ -179,6 +181,9 @@ class Cache { return getSize(item.get()); } + // read entire value on find. + void touchValue(const ReadHandle& it) const; + // returns the size of the item, taking into account ItemRecords could be // enabled. uint32_t getSize(const Item* item) const noexcept; @@ -241,6 +246,9 @@ class Cache { // returns true if the consistency checking is enabled. bool consistencyCheckEnabled() const { return valueTracker_ != nullptr; } + // returns true if touching value is enabled. + bool touchValueEnabled() const { return touchValue_; } + // return true if the key was previously detected to be inconsistent. This // is useful only when consistency checking is enabled by calling // enableConsistencyCheck() @@ -363,6 +371,9 @@ class Cache { // tracker for consistency monitoring. std::unique_ptr valueTracker_; + // read entire value on find. + bool touchValue_{false}; + // reading of the nand bytes written for the benchmark if enabled. const uint64_t nandBytesBegin_{0}; diff --git a/cachelib/cachebench/runner/CacheStressor.h b/cachelib/cachebench/runner/CacheStressor.h index b94483a295..a793fb8f17 100644 --- a/cachelib/cachebench/runner/CacheStressor.h +++ b/cachelib/cachebench/runner/CacheStressor.h @@ -95,7 +95,8 @@ class CacheStressor : public Stressor { cacheConfig.ticker = ticker_; } - cache_ = std::make_unique(cacheConfig, movingSync); + cache_ = std::make_unique(cacheConfig, movingSync, "", + config_.touchValue); if (config_.opPoolDistribution.size() > cache_->numPools()) { throw std::invalid_argument(folly::sformat( "more pools specified in the test than in the cache. " diff --git a/cachelib/cachebench/util/Config.cpp b/cachelib/cachebench/util/Config.cpp index f48f4bee57..2be51b6eef 100644 --- a/cachelib/cachebench/util/Config.cpp +++ b/cachelib/cachebench/util/Config.cpp @@ -34,6 +34,7 @@ StressorConfig::StressorConfig(const folly::dynamic& configJson) { JSONSetVal(configJson, samplingIntervalMs); JSONSetVal(configJson, checkConsistency); + JSONSetVal(configJson, touchValue); JSONSetVal(configJson, numOps); JSONSetVal(configJson, numThreads); diff --git a/cachelib/cachebench/util/Config.h b/cachelib/cachebench/util/Config.h index 48cbdf8c5d..e3333399e2 100644 --- a/cachelib/cachebench/util/Config.h +++ b/cachelib/cachebench/util/Config.h @@ -194,6 +194,10 @@ struct StressorConfig : public JSONConfig { // output stats after warmup. bool checkNvmCacheWarmUp{false}; + // If enabled, each value will be read on find. This is useful for measuring + // performance of value access. + bool touchValue{false}; + uint64_t numOps{0}; // operation per thread uint64_t numThreads{0}; // number of threads that will run uint64_t numKeys{0}; // number of keys that will be used