Add micro-benchmark support (#8493)
Summary: Add google benchmark for microbench. Add ribbon_bench for benchmark ribbon filter vs. other filters. Pull Request resolved: https://github.com/facebook/rocksdb/pull/8493 Test Plan: added test to CI To run the benchmark on devhost: Install benchmark: `$ sudo dnf install google-benchmark-devel` Build and run: `$ ROCKSDB_NO_FBCODE=1 DEBUG_LEVEL=0 make microbench` or with cmake: `$ mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release -DWITH_BENCHMARK=1 && make microbench` Reviewed By: pdillinger Differential Revision: D29589649 Pulled By: jay-zhuang fbshipit-source-id: 8fed13b562bef4472f161ecacec1ab6b18911dffmain
parent
f127d459ad
commit
5dd18a8d8e
@ -0,0 +1,16 @@ |
||||
find_package(benchmark REQUIRED) |
||||
find_package(Threads REQUIRED) |
||||
|
||||
file(GLOB_RECURSE ALL_BENCH_CPP *.cc) |
||||
foreach(ONE_BENCH_CPP ${ALL_BENCH_CPP}) |
||||
get_filename_component(TARGET_NAME ${ONE_BENCH_CPP} NAME_WE) |
||||
add_executable(${TARGET_NAME} ${ONE_BENCH_CPP}) |
||||
target_link_libraries(${TARGET_NAME} ${ROCKSDB_LIB} benchmark::benchmark |
||||
${CMAKE_THREAD_LIBS_INIT}) |
||||
# run benchmark like a test, if added, the benchmark tests could be run by `ctest -R Bench_` |
||||
# add_test(Bench_${TARGET_NAME} ${TARGET_NAME}) |
||||
list(APPEND ALL_BENCH_TARGETS ${TARGET_NAME}) |
||||
endforeach() |
||||
add_custom_target(microbench |
||||
COMMAND for t in ${ALL_BENCH_TARGETS}\; do \.\/$$t \|\| exit 1\; done |
||||
DEPENDS ${ALL_BENCH_TARGETS}) |
@ -0,0 +1,156 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
// this is a simple micro-benchmark for compare ribbon filter vs. other filter
|
||||
// for more comprehensive, please check the dedicate util/filter_bench.
|
||||
#include <benchmark/benchmark.h> |
||||
|
||||
#include "table/block_based/filter_policy_internal.h" |
||||
#include "table/block_based/mock_block_based_table.h" |
||||
|
||||
namespace ROCKSDB_NAMESPACE { |
||||
|
||||
struct KeyMaker { |
||||
explicit KeyMaker(size_t avg_size) |
||||
: smallest_size_(avg_size), |
||||
buf_size_(avg_size + 11), // pad to vary key size and alignment
|
||||
buf_(new char[buf_size_]) { |
||||
memset(buf_.get(), 0, buf_size_); |
||||
assert(smallest_size_ > 8); |
||||
} |
||||
size_t smallest_size_; |
||||
size_t buf_size_; |
||||
std::unique_ptr<char[]> buf_; |
||||
|
||||
// Returns a unique(-ish) key based on the given parameter values. Each
|
||||
// call returns a Slice from the same buffer so previously returned
|
||||
// Slices should be considered invalidated.
|
||||
Slice Get(uint32_t filter_num, uint32_t val_num) const { |
||||
size_t start = val_num % 4; |
||||
size_t len = smallest_size_; |
||||
// To get range [avg_size - 2, avg_size + 2]
|
||||
// use range [smallest_size, smallest_size + 4]
|
||||
len += FastRange32((val_num >> 5) * 1234567891, 5); |
||||
char *data = buf_.get() + start; |
||||
// Populate key data such that all data makes it into a key of at
|
||||
// least 8 bytes. We also don't want all the within-filter key
|
||||
// variance confined to a contiguous 32 bits, because then a 32 bit
|
||||
// hash function can "cheat" the false positive rate by
|
||||
// approximating a perfect hash.
|
||||
EncodeFixed32(data, val_num); |
||||
EncodeFixed32(data + 4, filter_num + val_num); |
||||
// ensure clearing leftovers from different alignment
|
||||
EncodeFixed32(data + 8, 0); |
||||
return {data, len}; |
||||
} |
||||
}; |
||||
|
||||
// benchmark arguments:
|
||||
// 0. filter mode
|
||||
// 1. filter config bits_per_key
|
||||
// 2. average data key length
|
||||
// 3. data entry number
|
||||
static void CustomArguments(benchmark::internal::Benchmark *b) { |
||||
for (int filterMode : |
||||
{BloomFilterPolicy::kLegacyBloom, BloomFilterPolicy::kFastLocalBloom, |
||||
BloomFilterPolicy::kStandard128Ribbon}) { |
||||
// for (int bits_per_key : {4, 10, 20, 30}) {
|
||||
for (int bits_per_key : {10, 20}) { |
||||
for (int key_len_avg : {10, 100}) { |
||||
for (int64_t entry_num : {1 << 10, 1 << 20}) { |
||||
b->Args({filterMode, bits_per_key, key_len_avg, entry_num}); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void FilterBuild(benchmark::State &state) { |
||||
// setup data
|
||||
auto filter = new BloomFilterPolicy( |
||||
static_cast<double>(state.range(1)), |
||||
static_cast<BloomFilterPolicy::Mode>(state.range(0))); |
||||
auto tester = new mock::MockBlockBasedTableTester(filter); |
||||
KeyMaker km(state.range(2)); |
||||
std::unique_ptr<const char[]> owner; |
||||
const int64_t kEntryNum = state.range(3); |
||||
auto rnd = Random32(12345); |
||||
uint32_t filter_num = rnd.Next(); |
||||
// run the test
|
||||
for (auto _ : state) { |
||||
std::unique_ptr<FilterBitsBuilder> builder(tester->GetBuilder()); |
||||
for (uint32_t i = 0; i < kEntryNum; i++) { |
||||
builder->AddKey(km.Get(filter_num, i)); |
||||
} |
||||
auto ret = builder->Finish(&owner); |
||||
state.counters["size"] = static_cast<double>(ret.size()); |
||||
} |
||||
} |
||||
BENCHMARK(FilterBuild)->Apply(CustomArguments); |
||||
|
||||
static void FilterQueryPositive(benchmark::State &state) { |
||||
// setup data
|
||||
auto filter = new BloomFilterPolicy( |
||||
static_cast<double>(state.range(1)), |
||||
static_cast<BloomFilterPolicy::Mode>(state.range(0))); |
||||
auto tester = new mock::MockBlockBasedTableTester(filter); |
||||
KeyMaker km(state.range(2)); |
||||
std::unique_ptr<const char[]> owner; |
||||
const int64_t kEntryNum = state.range(3); |
||||
auto rnd = Random32(12345); |
||||
uint32_t filter_num = rnd.Next(); |
||||
std::unique_ptr<FilterBitsBuilder> builder(tester->GetBuilder()); |
||||
for (uint32_t i = 0; i < kEntryNum; i++) { |
||||
builder->AddKey(km.Get(filter_num, i)); |
||||
} |
||||
auto data = builder->Finish(&owner); |
||||
auto reader = filter->GetFilterBitsReader(data); |
||||
|
||||
// run test
|
||||
uint32_t i = 0; |
||||
for (auto _ : state) { |
||||
i++; |
||||
i = i % kEntryNum; |
||||
reader->MayMatch(km.Get(filter_num, i)); |
||||
} |
||||
} |
||||
BENCHMARK(FilterQueryPositive)->Apply(CustomArguments); |
||||
|
||||
static void FilterQueryNegative(benchmark::State &state) { |
||||
// setup data
|
||||
auto filter = new BloomFilterPolicy( |
||||
static_cast<double>(state.range(1)), |
||||
static_cast<BloomFilterPolicy::Mode>(state.range(0))); |
||||
auto tester = new mock::MockBlockBasedTableTester(filter); |
||||
KeyMaker km(state.range(2)); |
||||
std::unique_ptr<const char[]> owner; |
||||
const int64_t kEntryNum = state.range(3); |
||||
auto rnd = Random32(12345); |
||||
uint32_t filter_num = rnd.Next(); |
||||
std::unique_ptr<FilterBitsBuilder> builder(tester->GetBuilder()); |
||||
for (uint32_t i = 0; i < kEntryNum; i++) { |
||||
builder->AddKey(km.Get(filter_num, i)); |
||||
} |
||||
auto data = builder->Finish(&owner); |
||||
auto reader = filter->GetFilterBitsReader(data); |
||||
|
||||
// run test
|
||||
uint32_t i = 0; |
||||
double fp_cnt = 0; |
||||
for (auto _ : state) { |
||||
i++; |
||||
auto result = reader->MayMatch(km.Get(filter_num + 1, i)); |
||||
if (result) { |
||||
fp_cnt++; |
||||
} |
||||
} |
||||
state.counters["FP %"] = |
||||
benchmark::Counter(fp_cnt * 100, benchmark::Counter::kAvgIterations); |
||||
} |
||||
BENCHMARK(FilterQueryNegative)->Apply(CustomArguments); |
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
BENCHMARK_MAIN(); |
Loading…
Reference in new issue