summaryrefslogtreecommitdiffstats
path: root/src/rocksdb/microbench/db_basic_bench.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/rocksdb/microbench/db_basic_bench.cc
parentInitial commit. (diff)
downloadceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz
ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/rocksdb/microbench/db_basic_bench.cc')
-rw-r--r--src/rocksdb/microbench/db_basic_bench.cc1575
1 files changed, 1575 insertions, 0 deletions
diff --git a/src/rocksdb/microbench/db_basic_bench.cc b/src/rocksdb/microbench/db_basic_bench.cc
new file mode 100644
index 000000000..6c70ad21d
--- /dev/null
+++ b/src/rocksdb/microbench/db_basic_bench.cc
@@ -0,0 +1,1575 @@
+// 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).
+
+#ifndef OS_WIN
+#include <unistd.h>
+#endif // ! OS_WIN
+
+#include "benchmark/benchmark.h"
+#include "db/db_impl/db_impl.h"
+#include "rocksdb/db.h"
+#include "rocksdb/filter_policy.h"
+#include "rocksdb/options.h"
+#include "table/block_based/block.h"
+#include "table/block_based/block_builder.h"
+#include "util/random.h"
+#include "utilities/merge_operators.h"
+
+namespace ROCKSDB_NAMESPACE {
+
+class KeyGenerator {
+ public:
+ // Generate next key
+ // buff: the caller needs to make sure there's enough space for generated key
+ // offset: to control the group of the key, 0 means normal key, 1 means
+ // non-existing key, 2 is reserved prefix_only: only return a prefix
+ Slice Next(char* buff, int8_t offset = 0, bool prefix_only = false) {
+ assert(max_key_ < std::numeric_limits<uint32_t>::max() /
+ MULTIPLIER); // TODO: add large key support
+
+ uint32_t k;
+ if (is_sequential_) {
+ assert(next_sequential_key_ < max_key_);
+ k = (next_sequential_key_ % max_key_) * MULTIPLIER + offset;
+ if (next_sequential_key_ + 1 == max_key_) {
+ next_sequential_key_ = 0;
+ } else {
+ next_sequential_key_++;
+ }
+ } else {
+ k = (rnd_->Next() % max_key_) * MULTIPLIER + offset;
+ }
+ // TODO: make sure the buff is large enough
+ memset(buff, 0, key_size_);
+ if (prefix_num_ > 0) {
+ uint32_t prefix = (k % prefix_num_) * MULTIPLIER + offset;
+ Encode(buff, prefix);
+ if (prefix_only) {
+ return {buff, prefix_size_};
+ }
+ }
+ Encode(buff + prefix_size_, k);
+ return {buff, key_size_};
+ }
+
+ // use internal buffer for generated key, make sure there's only one caller in
+ // single thread
+ Slice Next() { return Next(buff_); }
+
+ // user internal buffer for generated prefix
+ Slice NextPrefix() {
+ assert(prefix_num_ > 0);
+ return Next(buff_, 0, true);
+ }
+
+ // helper function to get non exist key
+ Slice NextNonExist() { return Next(buff_, 1); }
+
+ Slice MaxKey(char* buff) const {
+ memset(buff, 0xff, key_size_);
+ return {buff, key_size_};
+ }
+
+ Slice MinKey(char* buff) const {
+ memset(buff, 0, key_size_);
+ return {buff, key_size_};
+ }
+
+ // max_key: the max key that it could generate
+ // prefix_num: the max prefix number
+ // key_size: in bytes
+ explicit KeyGenerator(Random* rnd, uint64_t max_key = 100 * 1024 * 1024,
+ size_t prefix_num = 0, size_t key_size = 10) {
+ prefix_num_ = prefix_num;
+ key_size_ = key_size;
+ max_key_ = max_key;
+ rnd_ = rnd;
+ if (prefix_num > 0) {
+ prefix_size_ = 4; // TODO: support different prefix_size
+ }
+ }
+
+ // generate sequential keys
+ explicit KeyGenerator(uint64_t max_key = 100 * 1024 * 1024,
+ size_t key_size = 10) {
+ key_size_ = key_size;
+ max_key_ = max_key;
+ rnd_ = nullptr;
+ is_sequential_ = true;
+ }
+
+ private:
+ Random* rnd_;
+ size_t prefix_num_ = 0;
+ size_t prefix_size_ = 0;
+ size_t key_size_;
+ uint64_t max_key_;
+ bool is_sequential_ = false;
+ uint32_t next_sequential_key_ = 0;
+ char buff_[256] = {0};
+ const int MULTIPLIER = 3;
+
+ void static Encode(char* buf, uint32_t value) {
+ if (port::kLittleEndian) {
+ buf[0] = static_cast<char>((value >> 24) & 0xff);
+ buf[1] = static_cast<char>((value >> 16) & 0xff);
+ buf[2] = static_cast<char>((value >> 8) & 0xff);
+ buf[3] = static_cast<char>(value & 0xff);
+ } else {
+ memcpy(buf, &value, sizeof(value));
+ }
+ }
+};
+
+static void SetupDB(benchmark::State& state, Options& options,
+ std::unique_ptr<DB>* db,
+ const std::string& test_name = "") {
+ options.create_if_missing = true;
+ auto env = Env::Default();
+ std::string db_path;
+ Status s = env->GetTestDirectory(&db_path);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+ std::string db_name =
+ db_path + kFilePathSeparator + test_name + std::to_string(getpid());
+ DestroyDB(db_name, options);
+
+ DB* db_ptr = nullptr;
+ s = DB::Open(options, db_name, &db_ptr);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+ db->reset(db_ptr);
+}
+
+static void TeardownDB(benchmark::State& state, const std::unique_ptr<DB>& db,
+ const Options& options, KeyGenerator& kg) {
+ char min_buff[256], max_buff[256];
+ const Range r(kg.MinKey(min_buff), kg.MaxKey(max_buff));
+ uint64_t size;
+ Status s = db->GetApproximateSizes(&r, 1, &size);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ state.counters["db_size"] = static_cast<double>(size);
+
+ std::string db_name = db->GetName();
+ s = db->Close();
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ DestroyDB(db_name, options);
+}
+
+static void DBOpen(benchmark::State& state) {
+ // create DB
+ std::unique_ptr<DB> db;
+ Options options;
+ SetupDB(state, options, &db, "DBOpen");
+
+ std::string db_name = db->GetName();
+ db->Close();
+
+ options.create_if_missing = false;
+
+ auto rnd = Random(123);
+
+ for (auto _ : state) {
+ {
+ DB* db_ptr = nullptr;
+ Status s = DB::Open(options, db_name, &db_ptr);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ db.reset(db_ptr);
+ }
+ state.PauseTiming();
+ auto wo = WriteOptions();
+ Status s;
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 100; j++) {
+ s = db->Put(wo, rnd.RandomString(10), rnd.RandomString(100));
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+ s = db->Flush(FlushOptions());
+ }
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ s = db->Close();
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ state.ResumeTiming();
+ }
+ DestroyDB(db_name, options);
+}
+
+BENCHMARK(DBOpen)->Iterations(200); // specify iteration number as the db size
+ // is impacted by iteration number
+
+static void DBClose(benchmark::State& state) {
+ // create DB
+ std::unique_ptr<DB> db;
+ Options options;
+ SetupDB(state, options, &db, "DBClose");
+
+ std::string db_name = db->GetName();
+ db->Close();
+
+ options.create_if_missing = false;
+
+ auto rnd = Random(12345);
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ {
+ DB* db_ptr = nullptr;
+ Status s = DB::Open(options, db_name, &db_ptr);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ db.reset(db_ptr);
+ }
+ auto wo = WriteOptions();
+ Status s;
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 100; j++) {
+ s = db->Put(wo, rnd.RandomString(10), rnd.RandomString(100));
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+ s = db->Flush(FlushOptions());
+ }
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ state.ResumeTiming();
+ s = db->Close();
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+ DestroyDB(db_name, options);
+}
+
+BENCHMARK(DBClose)->Iterations(200); // specify iteration number as the db size
+ // is impacted by iteration number
+
+static void DBPut(benchmark::State& state) {
+ auto compaction_style = static_cast<CompactionStyle>(state.range(0));
+ uint64_t max_data = state.range(1);
+ uint64_t per_key_size = state.range(2);
+ bool enable_statistics = state.range(3);
+ bool enable_wal = state.range(4);
+ uint64_t key_num = max_data / per_key_size;
+
+ // setup DB
+ static std::unique_ptr<DB> db = nullptr;
+ Options options;
+ if (enable_statistics) {
+ options.statistics = CreateDBStatistics();
+ }
+ options.compaction_style = compaction_style;
+
+ auto rnd = Random(301 + state.thread_index());
+ KeyGenerator kg(&rnd, key_num);
+
+ if (state.thread_index() == 0) {
+ SetupDB(state, options, &db, "DBPut");
+ }
+
+ auto wo = WriteOptions();
+ wo.disableWAL = !enable_wal;
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ Slice key = kg.Next();
+ std::string val = rnd.RandomString(static_cast<int>(per_key_size));
+ state.ResumeTiming();
+ Status s = db->Put(wo, key, val);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+
+ if (state.thread_index() == 0) {
+ auto db_full = static_cast_with_check<DBImpl>(db.get());
+ Status s = db_full->WaitForCompact(true);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+ if (enable_statistics) {
+ HistogramData histogram_data;
+ options.statistics->histogramData(DB_WRITE, &histogram_data);
+ state.counters["put_mean"] = histogram_data.average * std::milli::den;
+ state.counters["put_p95"] = histogram_data.percentile95 * std::milli::den;
+ state.counters["put_p99"] = histogram_data.percentile99 * std::milli::den;
+ }
+
+ TeardownDB(state, db, options, kg);
+ }
+}
+
+static void DBPutArguments(benchmark::internal::Benchmark* b) {
+ for (int comp_style : {kCompactionStyleLevel, kCompactionStyleUniversal,
+ kCompactionStyleFIFO}) {
+ for (int64_t max_data : {100l << 30}) {
+ for (int64_t per_key_size : {256, 1024}) {
+ for (bool enable_statistics : {false, true}) {
+ for (bool wal : {false, true}) {
+ b->Args(
+ {comp_style, max_data, per_key_size, enable_statistics, wal});
+ }
+ }
+ }
+ }
+ }
+ b->ArgNames(
+ {"comp_style", "max_data", "per_key_size", "enable_statistics", "wal"});
+}
+
+static const uint64_t DBPutNum = 409600l;
+BENCHMARK(DBPut)->Threads(1)->Iterations(DBPutNum)->Apply(DBPutArguments);
+BENCHMARK(DBPut)->Threads(8)->Iterations(DBPutNum / 8)->Apply(DBPutArguments);
+
+static void ManualCompaction(benchmark::State& state) {
+ auto compaction_style = static_cast<CompactionStyle>(state.range(0));
+ uint64_t max_data = state.range(1);
+ uint64_t per_key_size = state.range(2);
+ bool enable_statistics = state.range(3);
+ uint64_t key_num = max_data / per_key_size;
+
+ // setup DB
+ static std::unique_ptr<DB> db;
+ Options options;
+ if (enable_statistics) {
+ options.statistics = CreateDBStatistics();
+ }
+ options.compaction_style = compaction_style;
+ // No auto compaction
+ options.disable_auto_compactions = true;
+ options.level0_file_num_compaction_trigger = (1 << 30);
+ options.level0_slowdown_writes_trigger = (1 << 30);
+ options.level0_stop_writes_trigger = (1 << 30);
+ options.soft_pending_compaction_bytes_limit = 0;
+ options.hard_pending_compaction_bytes_limit = 0;
+
+ auto rnd = Random(301 + state.thread_index());
+ KeyGenerator kg(&rnd, key_num);
+
+ if (state.thread_index() == 0) {
+ SetupDB(state, options, &db, "ManualCompaction");
+ }
+
+ auto wo = WriteOptions();
+ wo.disableWAL = true;
+ uint64_t flush_mod = key_num / 4; // at least generate 4 files for compaction
+ for (uint64_t i = 0; i < key_num; i++) {
+ Status s = db->Put(wo, kg.Next(),
+ rnd.RandomString(static_cast<int>(per_key_size)));
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ if (i + 1 % flush_mod == 0) {
+ s = db->Flush(FlushOptions());
+ }
+ }
+ FlushOptions fo;
+ Status s = db->Flush(fo);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ std::vector<LiveFileMetaData> files_meta;
+ db->GetLiveFilesMetaData(&files_meta);
+ std::vector<std::string> files_before_compact;
+ files_before_compact.reserve(files_meta.size());
+ for (const LiveFileMetaData& file : files_meta) {
+ files_before_compact.emplace_back(file.name);
+ }
+
+ SetPerfLevel(kEnableTime);
+ get_perf_context()->EnablePerLevelPerfContext();
+ get_perf_context()->Reset();
+ CompactionOptions co;
+ for (auto _ : state) {
+ s = db->CompactFiles(co, files_before_compact, 1);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+
+ if (state.thread_index() == 0) {
+ auto db_full = static_cast_with_check<DBImpl>(db.get());
+ s = db_full->WaitForCompact(true);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+ if (enable_statistics) {
+ HistogramData histogram_data;
+ options.statistics->histogramData(COMPACTION_TIME, &histogram_data);
+ state.counters["comp_time"] = histogram_data.average;
+ options.statistics->histogramData(COMPACTION_CPU_TIME, &histogram_data);
+ state.counters["comp_cpu_time"] = histogram_data.average;
+ options.statistics->histogramData(COMPACTION_OUTFILE_SYNC_MICROS,
+ &histogram_data);
+ state.counters["comp_outfile_sync"] = histogram_data.average;
+
+ state.counters["comp_read"] = static_cast<double>(
+ options.statistics->getTickerCount(COMPACT_READ_BYTES));
+ state.counters["comp_write"] = static_cast<double>(
+ options.statistics->getTickerCount(COMPACT_WRITE_BYTES));
+
+ state.counters["user_key_comparison_count"] =
+ static_cast<double>(get_perf_context()->user_key_comparison_count);
+ state.counters["block_read_count"] =
+ static_cast<double>(get_perf_context()->block_read_count);
+ state.counters["block_read_time"] =
+ static_cast<double>(get_perf_context()->block_read_time);
+ state.counters["block_checksum_time"] =
+ static_cast<double>(get_perf_context()->block_checksum_time);
+ state.counters["new_table_block_iter_nanos"] =
+ static_cast<double>(get_perf_context()->new_table_block_iter_nanos);
+ state.counters["new_table_iterator_nanos"] =
+ static_cast<double>(get_perf_context()->new_table_iterator_nanos);
+ state.counters["find_table_nanos"] =
+ static_cast<double>(get_perf_context()->find_table_nanos);
+ }
+
+ TeardownDB(state, db, options, kg);
+ }
+}
+
+static void ManualCompactionArguments(benchmark::internal::Benchmark* b) {
+ for (int comp_style : {kCompactionStyleLevel, kCompactionStyleUniversal}) {
+ for (int64_t max_data : {32l << 20, 128l << 20}) {
+ for (int64_t per_key_size : {256, 1024}) {
+ for (bool enable_statistics : {false, true}) {
+ b->Args({comp_style, max_data, per_key_size, enable_statistics});
+ }
+ }
+ }
+ }
+ b->ArgNames({"comp_style", "max_data", "per_key_size", "enable_statistics"});
+}
+
+BENCHMARK(ManualCompaction)->Iterations(1)->Apply(ManualCompactionArguments);
+
+static void ManualFlush(benchmark::State& state) {
+ uint64_t key_num = state.range(0);
+ uint64_t per_key_size = state.range(1);
+ bool enable_statistics = true;
+
+ // setup DB
+ static std::unique_ptr<DB> db;
+ Options options;
+ if (enable_statistics) {
+ options.statistics = CreateDBStatistics();
+ }
+ options.disable_auto_compactions = true;
+ options.level0_file_num_compaction_trigger = (1 << 30);
+ options.level0_slowdown_writes_trigger = (1 << 30);
+ options.level0_stop_writes_trigger = (1 << 30);
+ options.soft_pending_compaction_bytes_limit = 0;
+ options.hard_pending_compaction_bytes_limit = 0;
+ options.write_buffer_size = 2l << 30; // 2G to avoid auto flush
+
+ auto rnd = Random(301 + state.thread_index());
+ KeyGenerator kg(&rnd, key_num);
+
+ if (state.thread_index() == 0) {
+ SetupDB(state, options, &db, "ManualFlush");
+ }
+
+ auto wo = WriteOptions();
+ for (auto _ : state) {
+ state.PauseTiming();
+ for (uint64_t i = 0; i < key_num; i++) {
+ Status s = db->Put(wo, kg.Next(),
+ rnd.RandomString(static_cast<int>(per_key_size)));
+ }
+ FlushOptions fo;
+ state.ResumeTiming();
+ Status s = db->Flush(fo);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+
+ if (state.thread_index() == 0) {
+ auto db_full = static_cast_with_check<DBImpl>(db.get());
+ Status s = db_full->WaitForCompact(true);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+ if (enable_statistics) {
+ HistogramData histogram_data;
+ options.statistics->histogramData(FLUSH_TIME, &histogram_data);
+ state.counters["flush_time"] = histogram_data.average;
+ state.counters["flush_write_bytes"] = static_cast<double>(
+ options.statistics->getTickerCount(FLUSH_WRITE_BYTES));
+ }
+
+ TeardownDB(state, db, options, kg);
+ }
+}
+
+static void ManualFlushArguments(benchmark::internal::Benchmark* b) {
+ for (int64_t key_num : {1l << 10, 8l << 10, 64l << 10}) {
+ for (int64_t per_key_size : {256, 1024}) {
+ b->Args({key_num, per_key_size});
+ }
+ }
+ b->ArgNames({"key_num", "per_key_size"});
+}
+
+BENCHMARK(ManualFlush)->Iterations(1)->Apply(ManualFlushArguments);
+
+static void DBGet(benchmark::State& state) {
+ auto compaction_style = static_cast<CompactionStyle>(state.range(0));
+ uint64_t max_data = state.range(1);
+ uint64_t per_key_size = state.range(2);
+ bool enable_statistics = state.range(3);
+ bool negative_query = state.range(4);
+ bool enable_filter = state.range(5);
+ bool mmap = state.range(6);
+ uint64_t key_num = max_data / per_key_size;
+
+ // setup DB
+ static std::unique_ptr<DB> db;
+ Options options;
+ if (enable_statistics) {
+ options.statistics = CreateDBStatistics();
+ }
+ if (mmap) {
+ options.allow_mmap_reads = true;
+ options.compression = kNoCompression;
+ }
+ options.compaction_style = compaction_style;
+
+ BlockBasedTableOptions table_options;
+ if (enable_filter) {
+ table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
+ }
+ if (mmap) {
+ table_options.no_block_cache = true;
+ table_options.block_restart_interval = 1;
+ }
+ options.table_factory.reset(NewBlockBasedTableFactory(table_options));
+
+ auto rnd = Random(301 + state.thread_index());
+ KeyGenerator kg(&rnd, key_num);
+
+ if (state.thread_index() == 0) {
+ SetupDB(state, options, &db, "DBGet");
+
+ // load db
+ auto wo = WriteOptions();
+ wo.disableWAL = true;
+ for (uint64_t i = 0; i < key_num; i++) {
+ Status s = db->Put(wo, kg.Next(),
+ rnd.RandomString(static_cast<int>(per_key_size)));
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+
+ FlushOptions fo;
+ Status s = db->Flush(fo);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+
+ auto db_full = static_cast_with_check<DBImpl>(db.get());
+ s = db_full->WaitForCompact(true);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+ }
+
+ auto ro = ReadOptions();
+ if (mmap) {
+ ro.verify_checksums = false;
+ }
+ size_t not_found = 0;
+ if (negative_query) {
+ for (auto _ : state) {
+ std::string val;
+ Status s = db->Get(ro, kg.NextNonExist(), &val);
+ if (s.IsNotFound()) {
+ not_found++;
+ }
+ }
+ } else {
+ for (auto _ : state) {
+ std::string val;
+ Status s = db->Get(ro, kg.Next(), &val);
+ if (s.IsNotFound()) {
+ not_found++;
+ }
+ }
+ }
+
+ state.counters["neg_qu_pct"] = benchmark::Counter(
+ static_cast<double>(not_found * 100), benchmark::Counter::kAvgIterations);
+
+ if (state.thread_index() == 0) {
+ if (enable_statistics) {
+ HistogramData histogram_data;
+ options.statistics->histogramData(DB_GET, &histogram_data);
+ state.counters["get_mean"] = histogram_data.average * std::milli::den;
+ state.counters["get_p95"] = histogram_data.percentile95 * std::milli::den;
+ state.counters["get_p99"] = histogram_data.percentile99 * std::milli::den;
+ }
+
+ TeardownDB(state, db, options, kg);
+ }
+}
+
+static void DBGetArguments(benchmark::internal::Benchmark* b) {
+ for (int comp_style : {kCompactionStyleLevel, kCompactionStyleUniversal,
+ kCompactionStyleFIFO}) {
+ for (int64_t max_data : {128l << 20, 512l << 20}) {
+ for (int64_t per_key_size : {256, 1024}) {
+ for (bool enable_statistics : {false, true}) {
+ for (bool negative_query : {false, true}) {
+ for (bool enable_filter : {false, true}) {
+ for (bool mmap : {false, true}) {
+ b->Args({comp_style, max_data, per_key_size, enable_statistics,
+ negative_query, enable_filter, mmap});
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ b->ArgNames({"comp_style", "max_data", "per_key_size", "enable_statistics",
+ "negative_query", "enable_filter", "mmap"});
+}
+
+static constexpr uint64_t kDBGetNum = 1l << 20;
+BENCHMARK(DBGet)->Threads(1)->Iterations(kDBGetNum)->Apply(DBGetArguments);
+BENCHMARK(DBGet)->Threads(8)->Iterations(kDBGetNum / 8)->Apply(DBGetArguments);
+
+static void SimpleGetWithPerfContext(benchmark::State& state) {
+ // setup DB
+ static std::unique_ptr<DB> db;
+ std::string db_name;
+ Options options;
+ options.create_if_missing = true;
+ options.arena_block_size = 8 << 20;
+
+ auto rnd = Random(301 + state.thread_index());
+ KeyGenerator kg(&rnd, 1024);
+
+ if (state.thread_index() == 0) {
+ auto env = Env::Default();
+ std::string db_path;
+ Status s = env->GetTestDirectory(&db_path);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+ db_name = db_path + "/simple_get_" + std::to_string(getpid());
+ DestroyDB(db_name, options);
+
+ {
+ DB* db_ptr = nullptr;
+ s = DB::Open(options, db_name, &db_ptr);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+ db.reset(db_ptr);
+ }
+ // load db
+ auto wo = WriteOptions();
+ wo.disableWAL = true;
+ for (uint64_t i = 0; i < 1024; i++) {
+ s = db->Put(wo, kg.Next(), rnd.RandomString(1024));
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+ auto db_full = static_cast_with_check<DBImpl>(db.get());
+ s = db_full->WaitForCompact(true);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+ FlushOptions fo;
+ s = db->Flush(fo);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+
+ auto ro = ReadOptions();
+ size_t not_found = 0;
+ uint64_t user_key_comparison_count = 0;
+ uint64_t block_read_time = 0;
+ uint64_t block_checksum_time = 0;
+ uint64_t get_snapshot_time = 0;
+ uint64_t get_post_process_time = 0;
+ uint64_t get_from_output_files_time = 0;
+ uint64_t new_table_block_iter_nanos = 0;
+ uint64_t block_seek_nanos = 0;
+ uint64_t get_cpu_nanos = 0;
+ uint64_t get_from_table_nanos = 0;
+ SetPerfLevel(kEnableTime);
+ get_perf_context()->EnablePerLevelPerfContext();
+ for (auto _ : state) {
+ std::string val;
+ get_perf_context()->Reset();
+ Status s = db->Get(ro, kg.NextNonExist(), &val);
+ if (s.IsNotFound()) {
+ not_found++;
+ }
+ user_key_comparison_count += get_perf_context()->user_key_comparison_count;
+ block_read_time += get_perf_context()->block_read_time;
+ block_checksum_time += get_perf_context()->block_checksum_time;
+ get_snapshot_time += get_perf_context()->get_snapshot_time;
+ get_post_process_time += get_perf_context()->get_post_process_time;
+ get_from_output_files_time +=
+ get_perf_context()->get_from_output_files_time;
+ new_table_block_iter_nanos +=
+ get_perf_context()->new_table_block_iter_nanos;
+ block_seek_nanos += get_perf_context()->block_seek_nanos;
+ get_cpu_nanos += get_perf_context()->get_cpu_nanos;
+ get_from_table_nanos +=
+ (*(get_perf_context()->level_to_perf_context))[0].get_from_table_nanos;
+ }
+
+ state.counters["neg_qu_pct"] = benchmark::Counter(
+ static_cast<double>(not_found * 100), benchmark::Counter::kAvgIterations);
+ state.counters["user_key_comparison_count"] =
+ benchmark::Counter(static_cast<double>(user_key_comparison_count),
+ benchmark::Counter::kAvgIterations);
+ state.counters["block_read_time"] = benchmark::Counter(
+ static_cast<double>(block_read_time), benchmark::Counter::kAvgIterations);
+ state.counters["block_checksum_time"] =
+ benchmark::Counter(static_cast<double>(block_checksum_time),
+ benchmark::Counter::kAvgIterations);
+ state.counters["get_snapshot_time"] =
+ benchmark::Counter(static_cast<double>(get_snapshot_time),
+ benchmark::Counter::kAvgIterations);
+ state.counters["get_post_process_time"] =
+ benchmark::Counter(static_cast<double>(get_post_process_time),
+ benchmark::Counter::kAvgIterations);
+ state.counters["get_from_output_files_time"] =
+ benchmark::Counter(static_cast<double>(get_from_output_files_time),
+ benchmark::Counter::kAvgIterations);
+ state.counters["new_table_block_iter_nanos"] =
+ benchmark::Counter(static_cast<double>(new_table_block_iter_nanos),
+ benchmark::Counter::kAvgIterations);
+ state.counters["block_seek_nanos"] =
+ benchmark::Counter(static_cast<double>(block_seek_nanos),
+ benchmark::Counter::kAvgIterations);
+ state.counters["get_cpu_nanos"] = benchmark::Counter(
+ static_cast<double>(get_cpu_nanos), benchmark::Counter::kAvgIterations);
+ state.counters["get_from_table_nanos"] =
+ benchmark::Counter(static_cast<double>(get_from_table_nanos),
+ benchmark::Counter::kAvgIterations);
+
+ if (state.thread_index() == 0) {
+ TeardownDB(state, db, options, kg);
+ }
+}
+
+BENCHMARK(SimpleGetWithPerfContext)->Iterations(1000000);
+
+static void DBGetMergeOperandsInMemtable(benchmark::State& state) {
+ const uint64_t kDataLen = 16 << 20; // 16MB
+ const uint64_t kValueLen = 64;
+ const uint64_t kNumEntries = kDataLen / kValueLen;
+ const uint64_t kNumEntriesPerKey = state.range(0);
+ const uint64_t kNumKeys = kNumEntries / kNumEntriesPerKey;
+
+ // setup DB
+ static std::unique_ptr<DB> db;
+
+ Options options;
+ options.merge_operator = MergeOperators::CreateStringAppendOperator();
+ // Make memtable large enough that automatic flush will not be triggered.
+ options.write_buffer_size = 2 * kDataLen;
+
+ KeyGenerator sequential_key_gen(kNumKeys);
+ auto rnd = Random(301 + state.thread_index());
+
+ if (state.thread_index() == 0) {
+ SetupDB(state, options, &db, "DBGetMergeOperandsInMemtable");
+
+ // load db
+ auto write_opts = WriteOptions();
+ write_opts.disableWAL = true;
+ for (uint64_t i = 0; i < kNumEntries; i++) {
+ Status s = db->Merge(write_opts, sequential_key_gen.Next(),
+ rnd.RandomString(static_cast<int>(kValueLen)));
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+ }
+
+ KeyGenerator random_key_gen(kNumKeys);
+ std::vector<PinnableSlice> value_operands;
+ value_operands.resize(kNumEntriesPerKey);
+ GetMergeOperandsOptions get_merge_ops_opts;
+ get_merge_ops_opts.expected_max_number_of_operands =
+ static_cast<int>(kNumEntriesPerKey);
+ for (auto _ : state) {
+ int num_value_operands = 0;
+ Status s = db->GetMergeOperands(
+ ReadOptions(), db->DefaultColumnFamily(), random_key_gen.Next(),
+ value_operands.data(), &get_merge_ops_opts, &num_value_operands);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ if (num_value_operands != static_cast<int>(kNumEntriesPerKey)) {
+ state.SkipWithError("Unexpected number of merge operands found for key");
+ }
+ for (auto& value_operand : value_operands) {
+ value_operand.Reset();
+ }
+ }
+
+ if (state.thread_index() == 0) {
+ TeardownDB(state, db, options, random_key_gen);
+ }
+}
+
+static void DBGetMergeOperandsInSstFile(benchmark::State& state) {
+ const uint64_t kDataLen = 16 << 20; // 16MB
+ const uint64_t kValueLen = 64;
+ const uint64_t kNumEntries = kDataLen / kValueLen;
+ const uint64_t kNumEntriesPerKey = state.range(0);
+ const uint64_t kNumKeys = kNumEntries / kNumEntriesPerKey;
+ const bool kMmap = state.range(1);
+
+ // setup DB
+ static std::unique_ptr<DB> db;
+
+ BlockBasedTableOptions table_options;
+ if (kMmap) {
+ table_options.no_block_cache = true;
+ } else {
+ // Make block cache large enough that eviction will not be triggered.
+ table_options.block_cache = NewLRUCache(2 * kDataLen);
+ }
+
+ Options options;
+ if (kMmap) {
+ options.allow_mmap_reads = true;
+ }
+ options.compression = kNoCompression;
+ options.merge_operator = MergeOperators::CreateStringAppendOperator();
+ options.table_factory.reset(NewBlockBasedTableFactory(table_options));
+ // Make memtable large enough that automatic flush will not be triggered.
+ options.write_buffer_size = 2 * kDataLen;
+
+ KeyGenerator sequential_key_gen(kNumKeys);
+ auto rnd = Random(301 + state.thread_index());
+
+ if (state.thread_index() == 0) {
+ SetupDB(state, options, &db, "DBGetMergeOperandsInBlockCache");
+
+ // load db
+ //
+ // Take a snapshot after each cycle of merges to ensure flush cannot
+ // merge any entries.
+ std::vector<const Snapshot*> snapshots;
+ snapshots.resize(kNumEntriesPerKey);
+ auto write_opts = WriteOptions();
+ write_opts.disableWAL = true;
+ for (uint64_t i = 0; i < kNumEntriesPerKey; i++) {
+ for (uint64_t j = 0; j < kNumKeys; j++) {
+ Status s = db->Merge(write_opts, sequential_key_gen.Next(),
+ rnd.RandomString(static_cast<int>(kValueLen)));
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+ snapshots[i] = db->GetSnapshot();
+ }
+
+ // Flush to an L0 file; read back to prime the cache/mapped memory.
+ db->Flush(FlushOptions());
+ for (uint64_t i = 0; i < kNumKeys; ++i) {
+ std::string value;
+ Status s = db->Get(ReadOptions(), sequential_key_gen.Next(), &value);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+
+ if (state.thread_index() == 0) {
+ for (uint64_t i = 0; i < kNumEntriesPerKey; ++i) {
+ db->ReleaseSnapshot(snapshots[i]);
+ }
+ }
+ }
+
+ KeyGenerator random_key_gen(kNumKeys);
+ std::vector<PinnableSlice> value_operands;
+ value_operands.resize(kNumEntriesPerKey);
+ GetMergeOperandsOptions get_merge_ops_opts;
+ get_merge_ops_opts.expected_max_number_of_operands =
+ static_cast<int>(kNumEntriesPerKey);
+ for (auto _ : state) {
+ int num_value_operands = 0;
+ ReadOptions read_opts;
+ read_opts.verify_checksums = false;
+ Status s = db->GetMergeOperands(
+ read_opts, db->DefaultColumnFamily(), random_key_gen.Next(),
+ value_operands.data(), &get_merge_ops_opts, &num_value_operands);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ if (num_value_operands != static_cast<int>(kNumEntriesPerKey)) {
+ state.SkipWithError("Unexpected number of merge operands found for key");
+ }
+ for (auto& value_operand : value_operands) {
+ value_operand.Reset();
+ }
+ }
+
+ if (state.thread_index() == 0) {
+ TeardownDB(state, db, options, random_key_gen);
+ }
+}
+
+static void DBGetMergeOperandsInMemtableArguments(
+ benchmark::internal::Benchmark* b) {
+ for (int entries_per_key : {1, 32, 1024}) {
+ b->Args({entries_per_key});
+ }
+ b->ArgNames({"entries_per_key"});
+}
+
+static void DBGetMergeOperandsInSstFileArguments(
+ benchmark::internal::Benchmark* b) {
+ for (int entries_per_key : {1, 32, 1024}) {
+ for (bool mmap : {false, true}) {
+ b->Args({entries_per_key, mmap});
+ }
+ }
+ b->ArgNames({"entries_per_key", "mmap"});
+}
+
+BENCHMARK(DBGetMergeOperandsInMemtable)
+ ->Threads(1)
+ ->Apply(DBGetMergeOperandsInMemtableArguments);
+BENCHMARK(DBGetMergeOperandsInMemtable)
+ ->Threads(8)
+ ->Apply(DBGetMergeOperandsInMemtableArguments);
+BENCHMARK(DBGetMergeOperandsInSstFile)
+ ->Threads(1)
+ ->Apply(DBGetMergeOperandsInSstFileArguments);
+BENCHMARK(DBGetMergeOperandsInSstFile)
+ ->Threads(8)
+ ->Apply(DBGetMergeOperandsInSstFileArguments);
+
+std::string GenerateKey(int primary_key, int secondary_key, int padding_size,
+ Random* rnd) {
+ char buf[50];
+ char* p = &buf[0];
+ snprintf(buf, sizeof(buf), "%6d%4d", primary_key, secondary_key);
+ std::string k(p);
+ if (padding_size) {
+ k += rnd->RandomString(padding_size);
+ }
+
+ return k;
+}
+
+void GenerateRandomKVs(std::vector<std::string>* keys,
+ std::vector<std::string>* values, const int from,
+ const int len, const int step = 1,
+ const int padding_size = 0,
+ const int keys_share_prefix = 1) {
+ Random rnd(302);
+
+ // generate different prefix
+ for (int i = from; i < from + len; i += step) {
+ // generating keys that share the prefix
+ for (int j = 0; j < keys_share_prefix; ++j) {
+ keys->emplace_back(GenerateKey(i, j, padding_size, &rnd));
+ // 100 bytes values
+ values->emplace_back(rnd.RandomString(100));
+ }
+ }
+}
+
+// TODO: move it to different files, as it's testing an internal API
+static void DataBlockSeek(benchmark::State& state) {
+ Random rnd(301);
+ Options options = Options();
+
+ BlockBuilder builder(16, true, false,
+ BlockBasedTableOptions::kDataBlockBinarySearch);
+
+ int num_records = 500;
+ std::vector<std::string> keys;
+ std::vector<std::string> values;
+
+ GenerateRandomKVs(&keys, &values, 0, num_records);
+
+ for (int i = 0; i < num_records; i++) {
+ std::string ukey(keys[i] + "1");
+ InternalKey ikey(ukey, 0, kTypeValue);
+ builder.Add(ikey.Encode().ToString(), values[i]);
+ }
+
+ Slice rawblock = builder.Finish();
+
+ BlockContents contents;
+ contents.data = rawblock;
+ Block reader(std::move(contents));
+
+ SetPerfLevel(kEnableTime);
+ uint64_t total = 0;
+ for (auto _ : state) {
+ DataBlockIter* iter = reader.NewDataIterator(options.comparator,
+ kDisableGlobalSequenceNumber);
+ uint32_t index = rnd.Uniform(static_cast<int>(num_records));
+ std::string ukey(keys[index] + "1");
+ InternalKey ikey(ukey, 0, kTypeValue);
+ get_perf_context()->Reset();
+ bool may_exist = iter->SeekForGet(ikey.Encode().ToString());
+ if (!may_exist) {
+ state.SkipWithError("key not found");
+ }
+ total += get_perf_context()->block_seek_nanos;
+ delete iter;
+ }
+ state.counters["seek_ns"] = benchmark::Counter(
+ static_cast<double>(total), benchmark::Counter::kAvgIterations);
+}
+
+BENCHMARK(DataBlockSeek)->Iterations(1000000);
+
+static void IteratorSeek(benchmark::State& state) {
+ auto compaction_style = static_cast<CompactionStyle>(state.range(0));
+ uint64_t max_data = state.range(1);
+ uint64_t per_key_size = state.range(2);
+ bool enable_statistics = state.range(3);
+ bool negative_query = state.range(4);
+ bool enable_filter = state.range(5);
+ uint64_t key_num = max_data / per_key_size;
+
+ // setup DB
+ static std::unique_ptr<DB> db;
+ Options options;
+ if (enable_statistics) {
+ options.statistics = CreateDBStatistics();
+ }
+ options.compaction_style = compaction_style;
+
+ if (enable_filter) {
+ BlockBasedTableOptions table_options;
+ table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
+ options.table_factory.reset(NewBlockBasedTableFactory(table_options));
+ }
+
+ auto rnd = Random(301 + state.thread_index());
+ KeyGenerator kg(&rnd, key_num);
+
+ if (state.thread_index() == 0) {
+ SetupDB(state, options, &db, "IteratorSeek");
+
+ // load db
+ auto wo = WriteOptions();
+ wo.disableWAL = true;
+ for (uint64_t i = 0; i < key_num; i++) {
+ Status s = db->Put(wo, kg.Next(),
+ rnd.RandomString(static_cast<int>(per_key_size)));
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+
+ FlushOptions fo;
+ Status s = db->Flush(fo);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+
+ auto db_full = static_cast_with_check<DBImpl>(db.get());
+ s = db_full->WaitForCompact(true);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+ }
+
+ for (auto _ : state) {
+ std::unique_ptr<Iterator> iter{nullptr};
+ state.PauseTiming();
+ if (!iter) {
+ iter.reset(db->NewIterator(ReadOptions()));
+ }
+ Slice key = negative_query ? kg.NextNonExist() : kg.Next();
+ if (!iter->status().ok()) {
+ state.SkipWithError(iter->status().ToString().c_str());
+ return;
+ }
+ state.ResumeTiming();
+ iter->Seek(key);
+ }
+
+ if (state.thread_index() == 0) {
+ TeardownDB(state, db, options, kg);
+ }
+}
+
+static void IteratorSeekArguments(benchmark::internal::Benchmark* b) {
+ for (int comp_style : {kCompactionStyleLevel, kCompactionStyleUniversal,
+ kCompactionStyleFIFO}) {
+ for (int64_t max_data : {128l << 20, 512l << 20}) {
+ for (int64_t per_key_size : {256, 1024}) {
+ for (bool enable_statistics : {false, true}) {
+ for (bool negative_query : {false, true}) {
+ for (bool enable_filter : {false, true}) {
+ b->Args({comp_style, max_data, per_key_size, enable_statistics,
+ negative_query, enable_filter});
+ }
+ }
+ }
+ }
+ }
+ }
+ b->ArgNames({"comp_style", "max_data", "per_key_size", "enable_statistics",
+ "negative_query", "enable_filter"});
+}
+
+static constexpr uint64_t kDBSeekNum = 10l << 10;
+BENCHMARK(IteratorSeek)
+ ->Threads(1)
+ ->Iterations(kDBSeekNum)
+ ->Apply(IteratorSeekArguments);
+BENCHMARK(IteratorSeek)
+ ->Threads(8)
+ ->Iterations(kDBSeekNum / 8)
+ ->Apply(IteratorSeekArguments);
+
+static void IteratorNext(benchmark::State& state) {
+ auto compaction_style = static_cast<CompactionStyle>(state.range(0));
+ uint64_t max_data = state.range(1);
+ uint64_t per_key_size = state.range(2);
+ uint64_t key_num = max_data / per_key_size;
+
+ // setup DB
+ static std::unique_ptr<DB> db;
+ Options options;
+ options.compaction_style = compaction_style;
+
+ auto rnd = Random(301 + state.thread_index());
+ KeyGenerator kg(&rnd, key_num);
+
+ if (state.thread_index() == 0) {
+ SetupDB(state, options, &db, "IteratorNext");
+ // load db
+ auto wo = WriteOptions();
+ wo.disableWAL = true;
+ for (uint64_t i = 0; i < key_num; i++) {
+ Status s = db->Put(wo, kg.Next(),
+ rnd.RandomString(static_cast<int>(per_key_size)));
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+
+ FlushOptions fo;
+ Status s = db->Flush(fo);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+
+ auto db_full = static_cast_with_check<DBImpl>(db.get());
+ s = db_full->WaitForCompact(true);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+ }
+
+ for (auto _ : state) {
+ std::unique_ptr<Iterator> iter{nullptr};
+ state.PauseTiming();
+ if (!iter) {
+ iter.reset(db->NewIterator(ReadOptions()));
+ }
+ while (!iter->Valid()) {
+ iter->Seek(kg.Next());
+ if (!iter->status().ok()) {
+ state.SkipWithError(iter->status().ToString().c_str());
+ }
+ }
+ state.ResumeTiming();
+ iter->Next();
+ }
+
+ if (state.thread_index() == 0) {
+ TeardownDB(state, db, options, kg);
+ }
+}
+
+static void IteratorNextArguments(benchmark::internal::Benchmark* b) {
+ for (int comp_style : {kCompactionStyleLevel, kCompactionStyleUniversal,
+ kCompactionStyleFIFO}) {
+ for (int64_t max_data : {128l << 20, 512l << 20}) {
+ for (int64_t per_key_size : {256, 1024}) {
+ b->Args({comp_style, max_data, per_key_size});
+ }
+ }
+ }
+ b->ArgNames({"comp_style", "max_data", "per_key_size"});
+}
+static constexpr uint64_t kIteratorNextNum = 10l << 10;
+BENCHMARK(IteratorNext)
+ ->Iterations(kIteratorNextNum)
+ ->Apply(IteratorNextArguments);
+
+static void IteratorNextWithPerfContext(benchmark::State& state) {
+ // setup DB
+ static std::unique_ptr<DB> db;
+ Options options;
+
+ auto rnd = Random(301 + state.thread_index());
+ KeyGenerator kg(&rnd, 1024);
+
+ if (state.thread_index() == 0) {
+ SetupDB(state, options, &db, "IteratorNextWithPerfContext");
+ // load db
+ auto wo = WriteOptions();
+ wo.disableWAL = true;
+ for (uint64_t i = 0; i < 1024; i++) {
+ Status s = db->Put(wo, kg.Next(), rnd.RandomString(1024));
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+ auto db_full = static_cast_with_check<DBImpl>(db.get());
+ Status s = db_full->WaitForCompact(true);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+ FlushOptions fo;
+ s = db->Flush(fo);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+
+ uint64_t user_key_comparison_count = 0;
+ uint64_t internal_key_skipped_count = 0;
+ uint64_t find_next_user_entry_time = 0;
+ uint64_t iter_next_cpu_nanos = 0;
+
+ SetPerfLevel(kEnableTime);
+ get_perf_context()->EnablePerLevelPerfContext();
+
+ for (auto _ : state) {
+ std::unique_ptr<Iterator> iter{nullptr};
+ state.PauseTiming();
+ if (!iter) {
+ iter.reset(db->NewIterator(ReadOptions()));
+ }
+ while (!iter->Valid()) {
+ iter->Seek(kg.Next());
+ if (!iter->status().ok()) {
+ state.SkipWithError(iter->status().ToString().c_str());
+ }
+ }
+ get_perf_context()->Reset();
+ state.ResumeTiming();
+
+ iter->Next();
+ user_key_comparison_count += get_perf_context()->user_key_comparison_count;
+ internal_key_skipped_count +=
+ get_perf_context()->internal_key_skipped_count;
+ find_next_user_entry_time += get_perf_context()->find_next_user_entry_time;
+ iter_next_cpu_nanos += get_perf_context()->iter_next_cpu_nanos;
+ }
+
+ state.counters["user_key_comparison_count"] =
+ benchmark::Counter(static_cast<double>(user_key_comparison_count),
+ benchmark::Counter::kAvgIterations);
+ state.counters["internal_key_skipped_count"] =
+ benchmark::Counter(static_cast<double>(internal_key_skipped_count),
+ benchmark::Counter::kAvgIterations);
+ state.counters["find_next_user_entry_time"] =
+ benchmark::Counter(static_cast<double>(find_next_user_entry_time),
+ benchmark::Counter::kAvgIterations);
+ state.counters["iter_next_cpu_nanos"] =
+ benchmark::Counter(static_cast<double>(iter_next_cpu_nanos),
+ benchmark::Counter::kAvgIterations);
+
+ if (state.thread_index() == 0) {
+ TeardownDB(state, db, options, kg);
+ }
+}
+
+BENCHMARK(IteratorNextWithPerfContext)->Iterations(100000);
+
+static void IteratorPrev(benchmark::State& state) {
+ auto compaction_style = static_cast<CompactionStyle>(state.range(0));
+ uint64_t max_data = state.range(1);
+ uint64_t per_key_size = state.range(2);
+ uint64_t key_num = max_data / per_key_size;
+
+ // setup DB
+ static std::unique_ptr<DB> db;
+ std::string db_name;
+ Options options;
+ options.compaction_style = compaction_style;
+
+ auto rnd = Random(301 + state.thread_index());
+ KeyGenerator kg(&rnd, key_num);
+
+ if (state.thread_index() == 0) {
+ SetupDB(state, options, &db, "IteratorPrev");
+ // load db
+ auto wo = WriteOptions();
+ wo.disableWAL = true;
+ for (uint64_t i = 0; i < key_num; i++) {
+ Status s = db->Put(wo, kg.Next(),
+ rnd.RandomString(static_cast<int>(per_key_size)));
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+
+ FlushOptions fo;
+ Status s = db->Flush(fo);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+
+ auto db_full = static_cast_with_check<DBImpl>(db.get());
+ s = db_full->WaitForCompact(true);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+ }
+
+ for (auto _ : state) {
+ std::unique_ptr<Iterator> iter{nullptr};
+ state.PauseTiming();
+ if (!iter) {
+ iter.reset(db->NewIterator(ReadOptions()));
+ }
+ while (!iter->Valid()) {
+ iter->Seek(kg.Next());
+ if (!iter->status().ok()) {
+ state.SkipWithError(iter->status().ToString().c_str());
+ }
+ }
+ state.ResumeTiming();
+ iter->Prev();
+ }
+
+ if (state.thread_index() == 0) {
+ TeardownDB(state, db, options, kg);
+ }
+}
+
+static void IteratorPrevArguments(benchmark::internal::Benchmark* b) {
+ for (int comp_style : {kCompactionStyleLevel, kCompactionStyleUniversal,
+ kCompactionStyleFIFO}) {
+ for (int64_t max_data : {128l << 20, 512l << 20}) {
+ for (int64_t per_key_size : {256, 1024}) {
+ b->Args({comp_style, max_data, per_key_size});
+ }
+ }
+ }
+ b->ArgNames({"comp_style", "max_data", "per_key_size"});
+}
+
+static constexpr uint64_t kIteratorPrevNum = 10l << 10;
+BENCHMARK(IteratorPrev)
+ ->Iterations(kIteratorPrevNum)
+ ->Apply(IteratorPrevArguments);
+
+static void PrefixSeek(benchmark::State& state) {
+ auto compaction_style = static_cast<CompactionStyle>(state.range(0));
+ uint64_t max_data = state.range(1);
+ uint64_t per_key_size = state.range(2);
+ bool enable_statistics = state.range(3);
+ bool enable_filter = state.range(4);
+ uint64_t key_num = max_data / per_key_size;
+
+ // setup DB
+ static std::unique_ptr<DB> db;
+ Options options;
+ if (enable_statistics) {
+ options.statistics = CreateDBStatistics();
+ }
+ options.compaction_style = compaction_style;
+ options.prefix_extractor.reset(NewFixedPrefixTransform(4));
+
+ if (enable_filter) {
+ BlockBasedTableOptions table_options;
+ table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
+ options.table_factory.reset(NewBlockBasedTableFactory(table_options));
+ }
+
+ auto rnd = Random(301 + state.thread_index());
+ KeyGenerator kg(&rnd, key_num, key_num / 100);
+
+ if (state.thread_index() == 0) {
+ SetupDB(state, options, &db, "PrefixSeek");
+
+ // load db
+ auto wo = WriteOptions();
+ wo.disableWAL = true;
+ for (uint64_t i = 0; i < key_num; i++) {
+ Status s = db->Put(wo, kg.Next(),
+ rnd.RandomString(static_cast<int>(per_key_size)));
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+
+ FlushOptions fo;
+ Status s = db->Flush(fo);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+
+ auto db_full = static_cast_with_check<DBImpl>(db.get());
+ s = db_full->WaitForCompact(true);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+ }
+
+ for (auto _ : state) {
+ std::unique_ptr<Iterator> iter{nullptr};
+ state.PauseTiming();
+ if (!iter) {
+ iter.reset(db->NewIterator(ReadOptions()));
+ }
+ state.ResumeTiming();
+ iter->Seek(kg.NextPrefix());
+ if (!iter->status().ok()) {
+ state.SkipWithError(iter->status().ToString().c_str());
+ return;
+ }
+ }
+
+ if (state.thread_index() == 0) {
+ TeardownDB(state, db, options, kg);
+ }
+}
+
+static void PrefixSeekArguments(benchmark::internal::Benchmark* b) {
+ for (int comp_style : {kCompactionStyleLevel, kCompactionStyleUniversal,
+ kCompactionStyleFIFO}) {
+ for (int64_t max_data : {128l << 20, 512l << 20}) {
+ for (int64_t per_key_size : {256, 1024}) {
+ for (bool enable_statistics : {false, true}) {
+ for (bool enable_filter : {false, true}) {
+ b->Args({comp_style, max_data, per_key_size, enable_statistics,
+ enable_filter});
+ }
+ }
+ }
+ }
+ }
+ b->ArgNames({"comp_style", "max_data", "per_key_size", "enable_statistics",
+ "enable_filter"});
+}
+
+static constexpr uint64_t kPrefixSeekNum = 10l << 10;
+BENCHMARK(PrefixSeek)->Iterations(kPrefixSeekNum)->Apply(PrefixSeekArguments);
+BENCHMARK(PrefixSeek)
+ ->Threads(8)
+ ->Iterations(kPrefixSeekNum / 8)
+ ->Apply(PrefixSeekArguments);
+
+// TODO: move it to different files, as it's testing an internal API
+static void RandomAccessFileReaderRead(benchmark::State& state) {
+ bool enable_statistics = state.range(0);
+ constexpr int kFileNum = 10;
+ auto env = Env::Default();
+ auto fs = env->GetFileSystem();
+ std::string db_path;
+ Status s = env->GetTestDirectory(&db_path);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ return;
+ }
+
+ // Setup multiple `RandomAccessFileReader`s with different parameters to be
+ // used for test
+ Random rand(301);
+ std::string fname_base =
+ db_path + kFilePathSeparator + "random-access-file-reader-read";
+ std::vector<std::unique_ptr<RandomAccessFileReader>> readers;
+ auto statistics_share = CreateDBStatistics();
+ Statistics* statistics = enable_statistics ? statistics_share.get() : nullptr;
+ for (int i = 0; i < kFileNum; i++) {
+ std::string fname = fname_base + std::to_string(i);
+ std::string content = rand.RandomString(kDefaultPageSize);
+ std::unique_ptr<WritableFile> tgt_file;
+ env->NewWritableFile(fname, &tgt_file, EnvOptions());
+ tgt_file->Append(content);
+ tgt_file->Close();
+
+ std::unique_ptr<FSRandomAccessFile> f;
+ fs->NewRandomAccessFile(fname, FileOptions(), &f, nullptr);
+ int rand_num = rand.Next() % 3;
+ auto temperature = rand_num == 0 ? Temperature::kUnknown
+ : rand_num == 1 ? Temperature::kWarm
+ : Temperature::kCold;
+ readers.emplace_back(new RandomAccessFileReader(
+ std::move(f), fname, env->GetSystemClock().get(), nullptr, statistics,
+ 0, nullptr, nullptr, {}, temperature, rand_num == 1));
+ }
+
+ IOOptions io_options;
+ std::unique_ptr<char[]> scratch(new char[2048]);
+ Slice result;
+ uint64_t idx = 0;
+ for (auto _ : state) {
+ s = readers[idx++ % kFileNum]->Read(io_options, 0, kDefaultPageSize / 3,
+ &result, scratch.get(), nullptr,
+ Env::IO_TOTAL);
+ if (!s.ok()) {
+ state.SkipWithError(s.ToString().c_str());
+ }
+ }
+
+ // clean up
+ for (int i = 0; i < kFileNum; i++) {
+ std::string fname = fname_base + std::to_string(i);
+ env->DeleteFile(fname); // ignore return, okay to fail cleanup
+ }
+}
+
+BENCHMARK(RandomAccessFileReaderRead)
+ ->Iterations(1000000)
+ ->Arg(0)
+ ->Arg(1)
+ ->ArgName("enable_statistics");
+
+} // namespace ROCKSDB_NAMESPACE
+
+BENCHMARK_MAIN();