Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Index Btree Benchmark #237

Merged
merged 3 commits into from
Dec 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,8 @@ if (${non_coverage_build})
add_executable(log_store_benchmark)
target_sources(log_store_benchmark PRIVATE log_store_benchmark.cpp)
target_link_libraries(log_store_benchmark hs_logdev homestore ${COMMON_TEST_DEPS} benchmark::benchmark)
#add_test(NAME LogStoreBench COMMAND test_log_benchmark)

add_executable(index_btree_benchmark)
target_sources(index_btree_benchmark PRIVATE index_btree_benchmark.cpp)
target_link_libraries(index_btree_benchmark homestore ${COMMON_TEST_DEPS} benchmark::benchmark)
endif()
59 changes: 59 additions & 0 deletions src/tests/btree_helpers/btree_decls.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@

/*********************************************************************************
* Modifications Copyright 2017-2019 eBay Inc.
*
*
* 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
* https://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.
*
*********************************************************************************/
#pragma once

#include <homestore/index/index_table.hpp>

struct FixedLenBtree {
using BtreeType = IndexTable< TestFixedKey, TestFixedValue >;
using KeyType = TestFixedKey;
using ValueType = TestFixedValue;
static constexpr btree_node_type leaf_node_type = btree_node_type::FIXED;
static constexpr btree_node_type interior_node_type = btree_node_type::FIXED;
};

struct VarKeySizeBtree {
using BtreeType = IndexTable< TestVarLenKey, TestFixedValue >;
using KeyType = TestVarLenKey;
using ValueType = TestFixedValue;
static constexpr btree_node_type leaf_node_type = btree_node_type::VAR_KEY;
static constexpr btree_node_type interior_node_type = btree_node_type::VAR_KEY;
};

struct VarValueSizeBtree {
using BtreeType = IndexTable< TestVarLenKey, TestVarLenValue >;
using KeyType = TestVarLenKey;
using ValueType = TestVarLenValue;
static constexpr btree_node_type leaf_node_type = btree_node_type::VAR_OBJECT;
static constexpr btree_node_type interior_node_type = btree_node_type::VAR_OBJECT;
};

struct VarObjSizeBtree {
using BtreeType = IndexTable< TestVarLenKey, TestVarLenValue >;
using KeyType = TestVarLenKey;
using ValueType = TestVarLenValue;
static constexpr btree_node_type leaf_node_type = btree_node_type::VAR_OBJECT;
static constexpr btree_node_type interior_node_type = btree_node_type::VAR_OBJECT;
};

struct PrefixIntervalBtree {
using BtreeType = IndexTable< TestIntervalKey, TestIntervalValue >;
using KeyType = TestIntervalKey;
using ValueType = TestIntervalValue;
static constexpr btree_node_type leaf_node_type = btree_node_type::PREFIX;
static constexpr btree_node_type interior_node_type = btree_node_type::FIXED;
};
56 changes: 46 additions & 10 deletions src/tests/btree_helpers/btree_test_helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,35 @@
* specific language governing permissions and limitations under the License.
*
*********************************************************************************/
#pragma once

#include <random>
#include <map>
#include <atomic>
#include <memory>
#include <gtest/gtest.h>
#include <iomgr/io_environment.hpp>
#include <sisl/options/options.h>
#include <sisl/logging/logging.h>
#include <sisl/utility/enum.hpp>
#include <boost/algorithm/string.hpp>

#include <homestore/btree/detail/simple_node.hpp>
#include <homestore/btree/detail/varlen_node.hpp>
#include <homestore/btree/detail/prefix_node.hpp>
#include <homestore/btree/mem_btree.hpp>
#include "test_common/range_scheduler.hpp"
#include "shadow_map.hpp"
#include "btree_test_kvs.hpp"

static constexpr uint32_t g_node_size{4096};

template < typename TestType >
struct BtreeTestHelper : public testing::Test {
struct BtreeTestHelper {
using T = TestType;
using K = typename TestType::KeyType;
using V = typename TestType::ValueType;
using mutex = iomgr::FiberManagerLib::shared_mutex;
using op_func_t = std::function< void(void) >;

BtreeTestHelper() : testing::Test(), m_shadow_map{SISL_OPTIONS["num_entries"].as< uint32_t >()} {}
BtreeTestHelper() : m_shadow_map{SISL_OPTIONS["num_entries"].as< uint32_t >()} {}

void SetUp() override {
void SetUp() {
m_cfg.m_leaf_node_type = T::leaf_node_type;
m_cfg.m_int_node_type = T::interior_node_type;
m_max_range_input = SISL_OPTIONS["num_entries"].as< uint32_t >();
Expand All @@ -66,7 +64,7 @@ struct BtreeTestHelper : public testing::Test {
m_operations["query"] = std::bind(&BtreeTestHelper::query_random, this);
}

void TearDown() override {}
void TearDown() {}

protected:
std::shared_ptr< typename T::BtreeType > m_bt;
Expand All @@ -82,6 +80,7 @@ struct BtreeTestHelper : public testing::Test {
std::condition_variable m_test_done_cv;

std::random_device m_re;
std::atomic< uint32_t > m_num_ops{0};

public:
void preload(uint32_t preload_size) {
Expand Down Expand Up @@ -110,6 +109,8 @@ struct BtreeTestHelper : public testing::Test {
LOGINFO("Preload Done");
}

uint32_t get_op_num() const { return m_num_ops.load(); }

////////////////////// All put operation variants ///////////////////////////////
void put(uint64_t k, btree_put_type put_type) { do_put(k, put_type, V::generate_rand()); }

Expand Down Expand Up @@ -364,6 +365,7 @@ struct BtreeTestHelper : public testing::Test {
}
}

protected:
void run_in_parallel(const std::vector< std::pair< std::string, int > >& op_list) {
auto test_count = m_fibers.size();
for (auto it = m_fibers.begin(); it < m_fibers.end(); ++it) {
Expand All @@ -379,10 +381,13 @@ struct BtreeTestHelper : public testing::Test {
// Construct a weighted distribution based on the input frequencies
std::discrete_distribution< uint32_t > s_rand_op_generator(weights.begin(), weights.end());
auto m_start_time = Clock::now();
auto time_to_stop = [this, m_start_time]() {return (get_elapsed_time_sec(m_start_time) > m_run_time);};
auto time_to_stop = [this, m_start_time]() {
return (get_elapsed_time_sec(m_start_time) > m_run_time);
};
for (uint32_t i = 0; i < num_iters_per_thread && !time_to_stop(); i++) {
uint32_t op_idx = s_rand_op_generator(re);
(this->m_operations[op_list[op_idx].first])();
m_num_ops.fetch_add(1);
}
{
std::unique_lock lg(m_test_done_mtx);
Expand All @@ -397,4 +402,35 @@ struct BtreeTestHelper : public testing::Test {
}
LOGINFO("ALL parallel jobs joined");
}

std::vector< std::pair< std::string, int > > build_op_list(std::vector< std::string >& input_ops) {
std::vector< std::pair< std::string, int > > ops;
int total = std::accumulate(input_ops.begin(), input_ops.end(), 0, [](int sum, const auto& str) {
std::vector< std::string > tokens;
boost::split(tokens, str, boost::is_any_of(":"));
if (tokens.size() == 2) {
try {
return sum + std::stoi(tokens[1]);
} catch (const std::exception&) {
// Invalid frequency, ignore this element
}
}
return sum; // Ignore malformed strings
});

std::transform(input_ops.begin(), input_ops.end(), std::back_inserter(ops), [total](const auto& str) {
std::vector< std::string > tokens;
boost::split(tokens, str, boost::is_any_of(":"));
if (tokens.size() == 2) {
try {
return std::make_pair(tokens[0], (int)(100.0 * std::stoi(tokens[1]) / total));
} catch (const std::exception&) {
// Invalid frequency, ignore this element
}
}
return std::make_pair(std::string(), 0);
});

return ops;
}
};
5 changes: 5 additions & 0 deletions src/tests/btree_helpers/btree_test_kvs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@
*
*********************************************************************************/
#pragma once

#include <string>
#include <random>
#include <map>
#include <memory>
#include <array>

#include <homestore/btree/btree_kv.hpp>
#include <homestore/btree/detail/simple_node.hpp>
#include <homestore/btree/detail/varlen_node.hpp>
#include <homestore/btree/detail/prefix_node.hpp>

static constexpr uint32_t g_max_keysize{100}; // for node size = 512 : free space : 442 => 100+100+6(record size) = 46%
static constexpr uint32_t g_max_valsize{100};
Expand Down
142 changes: 142 additions & 0 deletions src/tests/index_btree_benchmark.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*********************************************************************************
* Modifications Copyright 2017-2019 eBay Inc.
*
*
* 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
* https://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.
*
*********************************************************************************/

#include <benchmark/benchmark.h>
#include <boost/uuid/random_generator.hpp>
#include <stdint.h>

#include <iomgr/io_environment.hpp>
#include <sisl/options/options.h>
#include <homestore/btree/detail/btree_internal.hpp>
#include "test_common/homestore_test_common.hpp"
#include "btree_helpers/btree_test_kvs.hpp"
#include "test_common/range_scheduler.hpp"
#include "btree_helpers/btree_test_helper.hpp"
#include "btree_helpers/btree_decls.h"

using namespace homestore;

#define INDEX_BETREE_BENCHMARK(BTREE_TYPE) \
BENCHMARK(run_benchmark< BTREE_TYPE >) \
->Setup(BM_Setup< BTREE_TYPE >) \
->Teardown(BM_Teardown< BTREE_TYPE >) \
->UseRealTime() \
->Iterations(1) \
->Name(#BTREE_TYPE);

// this is used to splite the setup and teardown from the benchmark to get a more accurate result
void* globle_helper{nullptr};

#define GET_BENCHMARK_HELPER(BTREE_TYPE) static_cast< IndexBtreeBenchmark< BTREE_TYPE >* >(globle_helper)

SISL_LOGGING_INIT(HOMESTORE_LOG_MODS)
std::vector< std::string > test_common::HSTestHelper::s_dev_names;
SISL_OPTIONS_ENABLE(logging, index_btree_benchmark, iomgr, test_common_setup)

SISL_OPTION_GROUP(index_btree_benchmark,
(num_iters, "", "num_iters", "number of iterations for rand ops",
::cxxopts::value< uint32_t >()->default_value("500"), "number"),
(num_entries, "", "num_entries", "number of entries to test with",
::cxxopts::value< uint32_t >()->default_value("5000"), "number"),
(run_time, "", "run_time", "run time for io", ::cxxopts::value< uint32_t >()->default_value("30"),
"seconds"),
(operation_list, "", "operation_list",
"operation list instead of default created following by percentage",
::cxxopts::value< std::vector< std::string > >()->default_value({"put:100"}), "operations [...]"),
(preload_size, "", "preload_size", "number of entries to preload tree with",
::cxxopts::value< uint32_t >()->default_value("1000"), "number"))

template < typename TestType >
struct IndexBtreeBenchmark : public BtreeTestHelper< TestType > {
using T = TestType;
using K = typename TestType::KeyType;
using V = typename TestType::ValueType;
IndexBtreeBenchmark() { SetUp(); }

~IndexBtreeBenchmark() { TearDown(); }

void SetUp() {
test_common::HSTestHelper::start_homestore(
"index_btree_benchmark", {{HS_SERVICE::META, {.size_pct = 10.0}}, {HS_SERVICE::INDEX, {.size_pct = 70.0}}});

this->m_cfg = BtreeConfig(hs()->index_service().node_size());
this->m_is_multi_threaded = true;

auto uuid = boost::uuids::random_generator()();
auto parent_uuid = boost::uuids::random_generator()();

BtreeTestHelper< TestType >::SetUp();
this->m_bt = std::make_shared< typename T::BtreeType >(uuid, parent_uuid, 0, this->m_cfg);
hs()->index_service().add_index_table(this->m_bt);
auto input_ops = SISL_OPTIONS["operation_list"].as< std::vector< std::string > >();
m_op_list = this->build_op_list(input_ops);
}

void TearDown() {
BtreeTestHelper< TestType >::TearDown();
test_common::HSTestHelper::shutdown_homestore();
}

void run_benchmark() { this->run_in_parallel(m_op_list); }

private:
std::vector< std::pair< std::string, int > > m_op_list;
};

template < class BenchmarkType >
void BM_Setup(const benchmark::State& state) {
globle_helper = new IndexBtreeBenchmark< BenchmarkType >();
auto helper = GET_BENCHMARK_HELPER(BenchmarkType);
helper->preload(SISL_OPTIONS["preload_size"].as< uint32_t >());
}

template < class BenchmarkType >
void BM_Teardown(const benchmark::State& state) {
delete GET_BENCHMARK_HELPER(BenchmarkType);
}

template < class BenchmarkType >
void add_custom_counter(benchmark::State& state) {
auto helper = GET_BENCHMARK_HELPER(BenchmarkType);
auto totol_ops = helper->get_op_num();
state.counters["thread_num"] = SISL_OPTIONS["num_threads"].as< uint32_t >();
state.counters["fiber_num"] = SISL_OPTIONS["num_fibers"].as< uint32_t >();
state.counters["total_ops"] = totol_ops;
state.counters["rate"] = benchmark::Counter(totol_ops, benchmark::Counter::kIsRate);
state.counters["InvRate"] =
benchmark::Counter(totol_ops, benchmark::Counter::kIsRate | benchmark::Counter::kInvert);
}

template < class BenchmarkType >
void run_benchmark(benchmark::State& state) {
auto helper = GET_BENCHMARK_HELPER(BenchmarkType);
for (auto _ : state) {
helper->run_benchmark();
}
add_custom_counter< BenchmarkType >(state);
}

INDEX_BETREE_BENCHMARK(FixedLenBtree)
INDEX_BETREE_BENCHMARK(VarKeySizeBtree)
INDEX_BETREE_BENCHMARK(VarValueSizeBtree)
INDEX_BETREE_BENCHMARK(VarObjSizeBtree)
INDEX_BETREE_BENCHMARK(PrefixIntervalBtree)

int main(int argc, char** argv) {
SISL_OPTIONS_LOAD(argc, argv, logging, index_btree_benchmark, iomgr, test_common_setup);
::benchmark::Initialize(&argc, argv);
::benchmark::RunSpecifiedBenchmarks();
}
3 changes: 0 additions & 3 deletions src/tests/test_btree_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
*
*********************************************************************************/
#include <gtest/gtest.h>
#include <random>
#include <map>
#include <memory>

#include <sisl/options/options.h>
#include <sisl/logging/logging.h>
Expand Down
6 changes: 4 additions & 2 deletions src/tests/test_common/homestore_test_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <sisl/logging/logging.h>
#include <sisl/options/options.h>
#include <sisl/settings/settings.hpp>
#include <iomgr/io_environment.hpp>
#include <homestore/homestore.hpp>
#include <homestore/index_service.hpp>
#include <homestore/replication_service.hpp>
Expand Down Expand Up @@ -152,7 +153,8 @@ class HSTestHelper {
}

LOGINFO("Starting iomgr with {} threads, spdk: {}", num_threads, is_spdk);
ioenvironment.with_iomgr(iomgr::iomgr_params{.num_threads = num_threads, .is_spdk = is_spdk, .num_fibers = num_fibers});
ioenvironment.with_iomgr(
iomgr::iomgr_params{.num_threads = num_threads, .is_spdk = is_spdk, .num_fibers = num_fibers});

auto const http_port = SISL_OPTIONS["http_port"].as< int >();
if (http_port != 0) {
Expand Down Expand Up @@ -305,4 +307,4 @@ class HSTestHelper {
}
}
};
} // namespace test_common
}; // namespace test_common
Loading