cachelib/benchmarks/CachelibRangeMapWorkloadBench.cpp (165 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.
*/
// Benchmark for measuring RMW workload with cachelib::Map
//
// Benchmark Results on a Devvm
// ============================================================================
// cachelib/benchmarks/CachelibRangeMapWorkloadBench.cpprelative time/iter
// iters/s
// ============================================================================
// cachelib_range_map 49.55ms 20.18
// std_map_on_cachelib 10.40% 476.42ms 2.10
// frozen_map_on_cachelib 15.93% 311.07ms 3.21
// std_map_on_folly_evcting_cache_map 405.06% 12.23ms 81.74
// ============================================================================
#include <folly/Benchmark.h>
#include <folly/container/EvictingCacheMap.h>
#include <folly/init/Init.h>
#include <random>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#include <thrift/lib/cpp2/frozen/FrozenUtil.h>
#include "cachelib/benchmarks/gen-cpp2/DataTypeBench_layouts.h"
#include "cachelib/benchmarks/gen-cpp2/DataTypeBench_types.h"
#pragma GCC diagnostic pop
#include "cachelib/allocator/CacheAllocator.h"
#include "cachelib/datatype/RangeMap.h"
DEFINE_int32(num_keys, 100, "number of keys used to populate the maps");
DEFINE_int32(num_ops, 100 * 1000, "number of operations");
DEFINE_double(write_rate, 0.05, "rate of writes");
namespace facebook {
namespace cachelib {
struct Value {
static constexpr size_t kValueSize = 8;
std::array<uint8_t, kValueSize> _;
};
using CachelibRangeMap = RangeMap<uint32_t, Value, LruAllocator>;
using StdMap = datatypebench::StdMap;
constexpr folly::StringPiece kClMap = "cachelib_map";
constexpr folly::StringPiece kStdMap = "std_unordered_map";
constexpr folly::StringPiece kFrozenStdMap = "frozen_unordered_map";
const std::string kFollyCacheStdMap = "folly_cache_std_unordered_map";
std::unique_ptr<LruAllocator> cache;
PoolId poolId;
using FollyCache = folly::EvictingCacheMap<std::string, StdMap>;
std::unique_ptr<FollyCache> follyCache;
void insertFrozenMap(StdMap& m, folly::StringPiece name) {
std::string frozenContent;
apache::thrift::frozen::freezeToString(m, frozenContent);
auto item = cache->allocate(poolId, name, frozenContent.size());
XDCHECK(item);
std::memcpy(item->getMemory(), frozenContent.data(), frozenContent.size());
cache->insertOrReplace(item);
}
void setup() {
LruAllocator::Config config;
config.setCacheSize(200 * 1024 * 1024); // 200 MB
config.enableCompactCache();
// 16 million buckets, 1 million locks
LruAllocator::AccessConfig accessConfig{24 /* buckets power */,
20 /* locks power */};
config.setAccessConfig(accessConfig);
config.configureChainedItems(accessConfig);
cache = std::make_unique<LruAllocator>(config);
poolId = cache->addPool("default", cache->getCacheMemoryStats().cacheSize);
// insert CachelibRangeMap into cache
{
auto m = CachelibRangeMap::create(*cache, poolId, kClMap);
cache->insert(m.viewItemHandle());
}
// insert StdMap
{
StdMap m;
auto iobuf = Serializer::serializeToIOBuf(m);
auto res = util::insertIOBufInCache(*cache, poolId, kStdMap, *iobuf);
XDCHECK(res);
}
// insert frozen StdMap into cache
{
StdMap m;
insertFrozenMap(m, kFrozenStdMap);
}
// set up folly::EvictingCacheMap
follyCache = std::make_unique<FollyCache>(1000 /* max number of items */);
{
StdMap m;
follyCache->set(kFollyCacheStdMap, std::move(m));
}
}
void benchCachelibRangeMap() {
auto getCachelibRangeMap = [] {
auto it = cache->findImpl(kClMap, AccessMode::kRead);
XDCHECK(it);
return CachelibRangeMap::fromItemHandle(*cache, std::move(it));
};
std::mt19937 gen{1};
std::discrete_distribution<> rwDist({1 - FLAGS_write_rate, FLAGS_write_rate});
Value val;
for (int i = 0; i < FLAGS_num_ops; ++i) {
auto m = getCachelibRangeMap();
int key = i % FLAGS_num_keys;
if (rwDist(gen) == 0) {
m.lookup(key);
} else {
m.insertOrReplace(key, val);
}
}
}
void benchStdMap() {
auto getMap = [] {
auto it = cache->find(kStdMap);
XDCHECK(it);
Deserializer deserializer{
it->template getMemoryAs<uint8_t>(),
it->template getMemoryAs<uint8_t>() + it->getSize()};
return deserializer.deserialize<StdMap>();
};
std::mt19937 gen{1};
std::discrete_distribution<> rwDist({1 - FLAGS_write_rate, FLAGS_write_rate});
std::string val{Value::kValueSize, 'c'};
for (int i = 0; i < FLAGS_num_ops; ++i) {
auto s = getMap();
int key = i % FLAGS_num_keys;
if (rwDist(gen) == 0) {
s.m_ref()->find(key);
} else {
s.m_ref()[key] = val;
auto iobuf = Serializer::serializeToIOBuf(s);
auto res = util::insertIOBufInCache(*cache, poolId, kStdMap, *iobuf);
XDCHECK(res);
}
}
}
void benchFrozenMap() {
auto getMap = [&] {
auto it = cache->find(kFrozenStdMap);
XDCHECK(it);
folly::StringPiece range{
it->template getMemoryAs<const char>(),
it->template getMemoryAs<const char>() + it->getSize()};
return apache::thrift::frozen::mapFrozen<StdMap>(range);
};
std::mt19937 gen{1};
std::discrete_distribution<> rwDist({1 - FLAGS_write_rate, FLAGS_write_rate});
std::string val{Value::kValueSize, 'c'};
for (int i = 0; i < FLAGS_num_ops; ++i) {
auto s = getMap();
int key = i % FLAGS_num_keys;
if (rwDist(gen) == 0) {
s.m().find(key);
} else {
auto mutableS = s.thaw();
mutableS.m_ref()[key] = val;
insertFrozenMap(mutableS, kFrozenStdMap);
}
}
}
void benchFollyCacheStdMap() {
std::mt19937 gen{1};
std::discrete_distribution<> rwDist({1 - FLAGS_write_rate, FLAGS_write_rate});
std::string val{Value::kValueSize, 'c'};
for (int i = 0; i < FLAGS_num_ops; ++i) {
auto& s = follyCache->get(kFollyCacheStdMap);
int key = i % FLAGS_num_keys;
if (rwDist(gen) == 0) {
s.m_ref()->find(key);
} else {
s.m_ref()[key] = val;
}
}
}
} // namespace cachelib
} // namespace facebook
namespace cl = facebook::cachelib;
BENCHMARK(cachelib_range_map) { cl::benchCachelibRangeMap(); }
BENCHMARK_RELATIVE(std_map_on_cachelib) { cl::benchStdMap(); }
BENCHMARK_RELATIVE(frozen_map_on_cachelib) { cl::benchFrozenMap(); }
BENCHMARK_RELATIVE(std_map_on_folly_evcting_cache_map) {
cl::benchFollyCacheStdMap();
}
int main(int argc, char** argv) {
folly::init(&argc, &argv);
cl::setup();
folly::runBenchmarks();
}