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