xo-numeric/utest/bplustree.cpp

814 lines
38 KiB
C++

/* @file bplustree.cpp */
#define CATCH_CONFIG_ENABLE_BENCHMARKING
#include "random_tree_ops.hpp"
#include "xo/ordinaltree/BplusTree.hpp"
#include "xo/randomgen/random_seed.hpp"
#include "xo/randomgen/print.hpp"
#include "indentlog/scope.hpp"
#include "catch2/catch.hpp"
namespace {
using xo::tree::BplusTree;
using xo::tree::BplusStdProperties;
using xo::tree::NullReduce;
using xo::tree::Machdep;
using xo::rng::Seed;
using utest::TreeUtil;
using xo::scope;
using xo::scope_setup;
using xo::xtag;
using BtreeKey = int;
using BtreeValue = double;
using BtreeProperties = BplusStdProperties<BtreeKey, BtreeValue, xo::tree::tags::ordinal_enabled>;
//using BtreeProperties = BplusStdProperties<BtreeKey, BtreeValue, xo::tree::tags::ordinal_disabled>;
using BpTree = BplusTree<BtreeKey,
BtreeValue,
NullReduce<BtreeKey>,
BtreeProperties>;
/* random test data (e.g. permutation of integers [0 .. n-1]).
* will do various tree operations using these permutations to control order
* in which keys are presented
*/
struct RandomTestData {
RandomTestData(std::size_t n,
xo::rng::xoshiro256ss * p_rgen);
std::vector<std::uint32_t> const & u1v() const { return u1v_; }
std::vector<std::uint32_t> const & u2v() const { return u2v_; }
std::vector<std::uint32_t> const & u12_v() const { return u12_v_; }
private:
/* a set comprising n randomly chosen elements drawn from [0 .. 2n-1].
* here n = .u1v.size = .u2v.size
*/
std::vector<std::uint32_t> u1v_;
/* complement of .u1v w.r.t. [0 .. 2n-1] */
std::vector<std::uint32_t> u2v_;
/* .u1v + .u2v */
std::vector<std::uint32_t> u12_v_;
}; /*RandomTestData*/
RandomTestData::RandomTestData(std::size_t n,
xo::rng::xoshiro256ss * p_rgen)
: u1v_(n), u2v_(n), u12_v_(2*n)
{
/* permutation of [0 .. 2n-1] */
std::vector<std::uint32_t> u(2*n);
for (std::uint32_t i=0; i<2*n; ++i)
u[i] = i;
std::shuffle(u.begin(), u.end(), *p_rgen);
u1v_ = std::vector<std::uint32_t>(u.begin(), u.begin() + n);
u2v_ = std::vector<std::uint32_t>(u.begin() + n, u.end());
u12_v_ = std::move(u);
} /*ctor*/
/* representation-independent feature benchmarks for tree algorithms.
*
* +------------------+
* |AbstractTestParams|
* +------------------+
* ^
* | isa +----------------+
* +------------|StdMapTestParams| benchmark std::map<BtreeKey, BtreeValue> (bogey!)
* | +----------------+
* |
* | isa +---------------+
* +------------|BtreeTestParams| benchmark BplusTree<BtreeKey, BtreeValue, ..>
* +---------------+
*/
struct AbstractTestParams {
virtual ~AbstractTestParams() = default;
/* insert benchmark:
* 1. prime tree by inserting RandomTestData.u1v (random subset comprising n draws from [0 .. 2n-1])
* 2. measure cost of inserting RandomTestData.u2v (complement of u1v w.r.t [0 .. 2n-1])
*/
virtual void run_insert_benchmark(RandomTestData const & random_testdata) const = 0;
virtual void run_erase_benchmark(RandomTestData const & random_testdata) const = 0;
virtual void run_lookup_benchmark(RandomTestData const & random_testdata) const = 0;
virtual void run_traverse_benchmark(RandomTestData const & random_testdata) const = 0;
};
struct StdMapTestParams : public AbstractTestParams {
StdMapTestParams(char const * name)
: test_name_{name} {}
/* 1. make map containing keys in random_testdata.u1v.
* 2. during construction, interleave inserts against a temporary map,
* to spoil sequential heap allocation (i.e. simulate fragmentation)
*/
std::map<BtreeKey, BtreeValue> make_random_map1(RandomTestData const & random_testdata) const {
std::map<BtreeKey, BtreeValue> tree;
/* 2nd tree to interfere with locality */
std::map<BtreeKey, BtreeValue> tree2;
for (std::uint32_t x : random_testdata.u1v()) {
tree.insert({x, 10*x});
/* 2nd tree to interfere with locality */
for (std::uint32_t y = 0; y < 8; ++y)
tree2.insert({8*x+y, 10*8*x+y});
}
return tree;
} /*make_random_map1*/
/* 1. make map containing keys in both random_testdata.u1v + random_testdata.u2v
* 2. during construction, interleave inserts against a temporary map,
* to spoil sequential heap allocation (i.e. simulate fragmentation)
*/
std::map<BtreeKey, BtreeValue> make_random_map12(RandomTestData const & random_testdata) const {
std::map<BtreeKey, BtreeValue> tree;
/* temporary tree to interfere with locality */
std::map<BtreeKey, BtreeValue> tree2;
for (std::uint32_t x : random_testdata.u12_v()) {
tree.insert({x, 10*x});
/* 2nd tree to interfere with memory locality */
for (std::uint32_t y = 0; y < 8; ++y)
tree2.insert({8*x+y, 10*8*x+y});
}
return tree;
} /*make_random_map12*/
virtual void run_insert_benchmark(RandomTestData const & random_testdata) const override;
virtual void run_erase_benchmark(RandomTestData const & random_testdata) const override;
virtual void run_lookup_benchmark(RandomTestData const & random_testdata) const override;
virtual void run_traverse_benchmark(RandomTestData const & random_testdata) const override;
char const * test_name_ = nullptr;
};
void
StdMapTestParams::run_insert_benchmark(RandomTestData const & random_testdata) const
{
/* see also: BtreeTestParams::run_insert_benchmark() */
BENCHMARK_ADVANCED(this->test_name_)(Catch::Benchmark::Chronometer clock)
{
std::size_t n = random_testdata.u1v().size();
std::map<BtreeKey, BtreeValue> tree
= std::move(this->make_random_map1(random_testdata));
/* benchmark additional inserts */
clock.measure([&](int seq) {
std::size_t key = random_testdata.u2v()[seq % n];
double value = 10 * key;
tree.insert({key, value});
return tree.size();
});
};
} /*run_insert_benchmark*/
void
StdMapTestParams::run_erase_benchmark(RandomTestData const & random_testdata) const
{
BENCHMARK_ADVANCED(this->test_name_)(Catch::Benchmark::Chronometer clock)
{
std::size_t n = random_testdata.u1v().size();
std::map<BtreeKey, BtreeValue> tree
= std::move(this->make_random_map12(random_testdata));;
clock.measure([&](int seq) {
/* catch2 decides how many times to run this lambda,
* in effort to get statistically valid sample.
*
* If it calls lambda n times, then seq will increase from [0 .. n-1]
*/
std::size_t key = random_testdata.u1v()[seq % n];
//std::clog << "i=" << i << std::endl;
tree.erase(key);
return tree.size();
});
};
} /*run_erase_benchmark*/
void
StdMapTestParams::run_lookup_benchmark(RandomTestData const & random_testdata) const
{
BENCHMARK_ADVANCED(this->test_name_)(Catch::Benchmark::Chronometer clock)
{
std::size_t n = random_testdata.u1v().size();
std::map<BtreeKey, BtreeValue> tree
= std::move(this->make_random_map1(random_testdata));
clock.measure([&](int seq) {
/* catch2 decides how many times to run this lambda,
* in effort to get statistically valid sample.
*
* If it calls lambda n times, then seq will increase from [0 .. n-1]
*/
std::size_t key = random_testdata.u1v()[seq % n];
//std::clog << "i=" << i << std::endl;
double value = tree[key];
return value;
});
};
} /*run_lookup_benchmark*/
void
StdMapTestParams::run_traverse_benchmark(RandomTestData const & random_testdata) const
{
BENCHMARK_ADVANCED(this->test_name_)(Catch::Benchmark::Chronometer clock)
{
std::size_t n = random_testdata.u1v().size();
std::map<BtreeKey, BtreeValue> tree
= std::move(this->make_random_map1(random_testdata));
clock.measure([&](int seq) {
/* catch2 decides how many times to run this lambda,
* in effort to get statistically valid sample.
*
* If it calls lambda n times, then seq will increase from [0 .. n-1]
*/
std::size_t key = random_testdata.u1v()[seq % n];
//std::clog << "i=" << i << std::endl;
double value = tree[key];
return value;
});
};
} /*run_traverse_benchmark*/
struct BtreeTestParams : public AbstractTestParams {
BtreeTestParams(char const * name, std::size_t bf, bool debug_flag)
: test_name_{name}, branching_factor_{bf}, debug_flag_{debug_flag} {}
BpTree make_empty_bptree() const {
BtreeProperties properties(branching_factor_,
debug_flag_);
return BpTree(properties);
}
/* 1. make b+ tree containing keys in random_testdata.u1v.
* 2. during constructions, interleave inserts against a temporary b+ tree,
* to spoil sequential heap allocation (i.e. simulate fragmentation)
*/
BpTree make_random_bptree1(RandomTestData const & random_testdata) const {
BpTree bptree = this->make_empty_bptree();
/* 2nd tree, just to spoil memory locality */
BpTree bptree2 = this->make_empty_bptree();
for (std::uint32_t x : random_testdata.u1v()) {
bptree.insert(BpTree::value_type(x, 10 * x));
/* 2nd tree to interfere with locality */
for (std::uint32_t y = 0; y < 8; ++y) {
bptree2.insert(BpTree::value_type(8*x+y, 10 * (8*x+y)));
}
}
return bptree;
} /*make_random_bptree1*/
BpTree make_random_bptree12(RandomTestData const & random_testdata) const {
BpTree bptree = this->make_empty_bptree();
/* 2nd tree, just to spoil memory locality */
BpTree bptree2 = this->make_empty_bptree();
for (std::uint32_t x : random_testdata.u12_v()) {
bptree.insert(BpTree::value_type(x, 10 * x));
/* 2nd tree to interfere with locality */
for (std::uint32_t y = 0; y < 8; ++y) {
bptree2.insert(BpTree::value_type(8*x+y, 10 * (8*x+y)));
}
}
return bptree;
} /*make_random_bptree12*/
void run_unit_test(xo::rng::xoshiro256ss * p_rgen) const;
virtual void run_insert_benchmark(RandomTestData const & random_testdata) const override;
virtual void run_erase_benchmark(RandomTestData const & random_testdata) const override;
virtual void run_lookup_benchmark(RandomTestData const & random_testdata) const override;
virtual void run_traverse_benchmark(RandomTestData const & random_testdata) const override;
/* test (or benchmark) name -- 1st argument to catch2 TEST_CASE() / BENCHMARK() / SECTION() macro */
char const * test_name_ = nullptr;
/* exercise B+ tree with this branching factor */
std::size_t branching_factor_ = 0;
/* for benchmarks only: if true enable verbose logging of B+ tree operations. otherwise not used */
bool debug_flag_ = false;
}; /*BtreeTestParams*/
void
BtreeTestParams::run_unit_test(xo::rng::xoshiro256ss * p_rgen) const
{
std::size_t branching_factor = this->branching_factor_;
/* perform a series of tests with increasing scale */
for (std::uint32_t n = 0; n <= 1024;) {
if (n == 0) {
bool ok_flag = false;
for (std::uint32_t attention = 0; !ok_flag && (attention < 2); ++attention) {
ok_flag = true;
bool debug_flag = (attention == 1);
BtreeProperties properties(branching_factor,
debug_flag);
BpTree bptree(properties);
scope log(XO_DEBUG2(debug_flag, "bptree"),
xtag("vm_page_size", Machdep::get_page_size()),
xtag("branching_factor", bptree.branching_factor()),
xtag("leaf_node_size", sizeof(BpTree::LeafNodeType)),
xtag("internal_node_size", sizeof(BpTree::InternalNodeType)));
REQUIRE_ORCAPTURE(ok_flag, debug_flag, bptree.size() == 0);
REQUIRE_ORCAPTURE(ok_flag, debug_flag, bptree.verify_ok(true) == true);
log && log(xtag("size", n));
ok_flag &= TreeUtil<BpTree>::check_bidirectional_iterator(0 /*dvalue - not used*/,
debug_flag,
bptree);
ok_flag &= TreeUtil<BpTree>::test_clear(debug_flag, &bptree);
log.end_scope();
}
} else {
/* for each tree size, do multiple trials;
* choosing different pseudorandom key order for each trial
*/
for (std::uint32_t trial = 0; trial < 10; ++trial) {
/* repeated trials with different rng state */
bool ok_flag = false;
for (std::uint32_t attention = 0; !ok_flag && (attention < 2); ++attention) {
ok_flag = true;
/* attention=0:
* - no logging
* - detect assertion failures, but don't report them to catch
* attention=1:
* - only runs if failure detected with attention=0
* - full logging
* - report to catch
*/
bool debug_flag = (attention == 1);
BtreeProperties properties(branching_factor,
debug_flag);
BpTree bptree(properties);
scope log(XO_DEBUG2(debug_flag, "bptree"),
xtag("vm_page_size", Machdep::get_page_size()),
xtag("branching_factor", bptree.branching_factor()),
xtag("leaf_node_size", sizeof(BpTree::LeafNodeType)),
xtag("internal_node_size", sizeof(BpTree::InternalNodeType)));
REQUIRE_ORCAPTURE(ok_flag, debug_flag, bptree.size() == 0);
REQUIRE_ORCAPTURE(ok_flag, debug_flag, bptree.verify_ok(true) == true);
log && log(xtag("size", n), xtag("trial", trial));
/* insert [0..n-1] in random order */
ok_flag &= TreeUtil<BpTree>::random_inserts(n, debug_flag, p_rgen, &bptree);
/* verification problem -> print tree */
log && log(xtag("bptree", (char const *)"..."));
if (log) bptree.print(std::cout, log.nesting_level() + 2);
try {
REQUIRE_ORCAPTURE(ok_flag, debug_flag, bptree.verify_ok(debug_flag));
} catch(std::exception & ex) {
log && log(xtag("exception", ex.what()));
}
if (properties.ordinal_enabled()) {
ok_flag &= TreeUtil<BpTree>::check_ordinal_lookup(0 /*dvalue*/,
debug_flag,
bptree);
}
/* verify inorder traverse, using iterator api */
ok_flag &= TreeUtil<BpTree>::check_bidirectional_iterator(0,
debug_flag,
bptree);
ok_flag &= TreeUtil<BpTree>::random_lookups(debug_flag,
bptree,
p_rgen);
if (properties.ordinal_enabled()) {
/* paranoid check that iteration / random_lookups didn't somehow disturb tree */
ok_flag &= TreeUtil<BpTree>::check_ordinal_lookup(0 /*dvalue*/,
debug_flag,
bptree);
}
/* TODO:
* - check_reduced_sum()
* - check_ordinal_lookup()
* - check_bidirectional_iterator()
* - random_updates()
* - check_ordinal_lookup()
* - check_bidirectional_iterator()
* - check_reduced_sum()
*/
/* remove [0..n-1] in random order */
ok_flag &= TreeUtil<BpTree>::random_removes(debug_flag, p_rgen, &bptree);
/* insert [0..n-1] again, so we can test .clear() */
ok_flag &= TreeUtil<BpTree>::random_inserts(n, debug_flag, p_rgen, &bptree);
ok_flag &= TreeUtil<BpTree>::test_clear(debug_flag, &bptree);
log.end_scope();
} /*loop over attention value*/
} /*loop over trial#*/
}
if (n == 0)
n = 1;
else
n = 2*n;
}
} /*run_unit_test*/
void
BtreeTestParams::run_insert_benchmark(RandomTestData const & random_testdata) const
{
BENCHMARK_ADVANCED(this->test_name_)(Catch::Benchmark::Chronometer clock)
{
std::size_t n = random_testdata.u1v().size();
BpTree bptree = std::move(this->make_random_bptree1(random_testdata));
/* benchmark additional inserts (don't want to benchmark on empty tree) */
clock.measure([&](int seq) {
/* catch2 decides how many times to run this lambda,
* in effort to get statistically valid sample.
*
* If it calls lambda n times, then seq will increase from [0 .. n-1]
*/
std::size_t key = random_testdata.u2v()[seq % n];
double value = 10 * key;
bptree.insert(BpTree::value_type(key, value));
return bptree.size();
});
};
} /*run_insert_benchmark*/
void
BtreeTestParams::run_erase_benchmark(RandomTestData const & random_testdata) const
{
BENCHMARK_ADVANCED(this->test_name_)(Catch::Benchmark::Chronometer clock)
{
std::size_t n = random_testdata.u1v().size();
/* b+ tree with 2n elements */
BpTree bptree = std::move(this->make_random_bptree12(random_testdata));
/* measure time to remove n elements */
clock.measure([&](int seq) {
/* catch2 decides how many times to run this lambda,
* in effort to get statistically valid sample.
*
* If it calls lambda n times, then seq will increase from [0 .. n-1]
*/
//std::clog << "i=" << i << std::endl;
bptree.erase(random_testdata.u1v()[seq % n]);
return bptree.size();
});
};
} /*run_erase_benchmark*/
void
BtreeTestParams::run_lookup_benchmark(RandomTestData const & random_testdata) const
{
BENCHMARK_ADVANCED(this->test_name_)(Catch::Benchmark::Chronometer clock)
{
std::size_t n = random_testdata.u1v().size();
BpTree bptree = std::move(this->make_random_bptree1(random_testdata));
/* benchmark random lookups */
clock.measure([&](int seq) {
/* catch2 decides how many times to run this lambda,
* in effort to get statistically valid sample.
*
* If it calls lambda n times, then seq will increase from [0 .. n-1]
*/
std::size_t key = random_testdata.u1v()[seq % n];
double value = bptree[key];
return value;
});
};
} /*run_lookup_benchmark*/
void
BtreeTestParams::run_traverse_benchmark(RandomTestData const & random_testdata) const
{
BENCHMARK_ADVANCED(this->test_name_)(Catch::Benchmark::Chronometer clock)
{
std::size_t n = random_testdata.u1v().size();
BpTree bptree = std::move(this->make_random_bptree1(random_testdata));
/* benchmark traverse */
BpTree::const_iterator ix = bptree.begin();
clock.measure([&](int seq) {
/* catch2 decides how many times to run this lambda,
* in effort to get statistically valid sample.
*
* If it calls lambda n times, then seq will increase from [0 .. n-1]
*/
if (seq % n == 0)
ix = bptree.begin();
return ix++;
});
};
} /*run_traverse_benchmark*/
TEST_CASE("bptree", "[bplustree]") {
uint64_t seed = 14950349842636922572UL;
/* can reseed from /dev/random with: */
//Seed<xo::rng::xoshiro256ss> seed;
auto rgen = xo::rng::xoshiro256ss(seed);
/* exercise multiple branching factors */
std::array<BtreeTestParams, 4> const params_v
= {{
BtreeTestParams("bf=4",
4 /*branching_factor*/,
false /*debug_flag - not used*/),
BtreeTestParams("bf=12",
12 /*branching_factor*/,
false /*debug_flag - not used*/),
BtreeTestParams("bf=28",
28 /*branching_factor*/,
false /*debug_flag - not used*/),
BtreeTestParams("bf=60",
60 /*branching_factor*/,
false /*debug_flag - not used*/)
}};
for (std::uint32_t i_pm = 0; i_pm < params_v.size(); ++i_pm) {
SECTION(params_v[i_pm].test_name_) {
params_v[i_pm].run_unit_test(&rgen);
}
}
} /*TEST_CASE(bptree)*/
/* to run:
* $ ./utest.tree [!benchmark]
*
* looks like ospage4 (1k nodes) gets best performance
*/
TEST_CASE("bptree-benchmark", "[!benchmark]") {
using BtreeProperties = BplusStdProperties<BtreeKey, BtreeValue>;
/* 2 cache lines per node (though note that we're not aligning nodes on cacheline boundaries) */
std::size_t const c_cacheline_branching_factor = 4; // BtreeProperties::default_cacheline_branching_factor();
std::size_t const c_ospage16_branching_factor = BtreeProperties::branching_factor_for_size(Machdep::get_page_size() / 16);
std::size_t const c_ospage8_branching_factor = BtreeProperties::branching_factor_for_size(Machdep::get_page_size() / 8);
std::size_t const c_ospage4_branching_factor = BtreeProperties::branching_factor_for_size(Machdep::get_page_size() / 4);
std::size_t const c_ospage2_branching_factor = BtreeProperties::branching_factor_for_size(Machdep::get_page_size() / 2);
std::size_t const c_ospage1_branching_factor = BtreeProperties::branching_factor_for_size(Machdep::get_page_size());
/* random seed -- we don't need deterministic behavior for benchmarking, unless we encounter internal logic error */
//std::uint64_t seed = 17372468046414980217UL;
Seed<xo::rng::xoshiro256ss> seed;
auto rgen = xo::rng::xoshiro256ss(seed);
constexpr bool c_debug_flag = false;
/* n keys [0 .. n-1] */
std::uint32_t n = 25000;
RandomTestData random_testdata(n, &rgen);
#ifdef OBSOLETE
/* random permutation of [0..n-1] */
std::vector<std::uint32_t> u(n);
{
for (std::uint32_t i=0; i<n; ++i)
u[i] = i;
std::shuffle(u.begin(), u.end(), rgen);
}
/* random permutation of [n..2n-1] */
std::vector<std::uint32_t> u2(n);
{
for (std::uint32_t i=0; i<n; ++i)
u2[i] = n+i;
std::shuffle(u2.begin(), u2.end(), rgen);
}
#endif
std::clog << "rng-seed=" << seed << "\n"
<< "opage16-branching-factor=" << c_ospage16_branching_factor << "\n"
<< "opage16-leaf-size=" << sizeof(BpTree::LeafNodeType) + c_ospage16_branching_factor * sizeof(BpTree::LeafNodeItemType) << "\n"
<< "opage16-internal-size=" << sizeof(BpTree::InternalNodeType) + c_ospage16_branching_factor * sizeof(BpTree::InternalNodeItemType) << "\n"
<< "opage8-branching-factor=" << c_ospage8_branching_factor << "\n"
<< "opage8-leaf-size=" << sizeof(BpTree::LeafNodeType) + c_ospage8_branching_factor * sizeof(BpTree::LeafNodeItemType) << "\n"
<< "opage8-internal-size=" << sizeof(BpTree::InternalNodeType) + c_ospage8_branching_factor * sizeof(BpTree::InternalNodeItemType) << "\n"
<< "opage4-branching-factor=" << c_ospage4_branching_factor << "\n"
<< "opage4-leaf-size=" << sizeof(BpTree::LeafNodeType) + c_ospage4_branching_factor * sizeof(BpTree::LeafNodeItemType) << "\n"
<< "opage4-internal-size=" << sizeof(BpTree::InternalNodeType) + c_ospage4_branching_factor * sizeof(BpTree::InternalNodeItemType) << "\n"
<< "opage2-branching-factor=" << c_ospage2_branching_factor << "\n"
<< "opage2-leaf-size=" << sizeof(BpTree::LeafNodeType) + c_ospage2_branching_factor * sizeof(BpTree::LeafNodeItemType) << "\n"
<< "opage2-internal-size=" << sizeof(BpTree::InternalNodeType) + c_ospage2_branching_factor * sizeof(BpTree::InternalNodeItemType) << "\n"
<< "opage1-branching-factor=" << c_ospage1_branching_factor << "\n"
<< "default-branching-factor[cacheline]=" << BplusStdProperties<BtreeKey, BtreeValue>::default_cacheline_branching_factor() << "\n"
<< "page-size=" << Machdep::get_page_size() << "\n"
<< "cache-line-size=" << Machdep::get_cache_line_size() << "\n"
<< "generic-node-overhead=" << sizeof(BpTree::GenericNodeType) << "\n"
<< "leaf-node-overhead=" << sizeof(BpTree::LeafNodeType) << "\n"
<< "leaf-node-item-size=" << sizeof(BpTree::LeafNodeItemType) << "\n"
<< "internal-node-overhead=" << sizeof(BpTree::InternalNodeType) << "\n"
<< "internal-node-item-size=" << sizeof(BpTree::InternalNodeItemType) << "\n"
<< "actual-cacheline-leaf-node-size=" << sizeof(BpTree::LeafNodeType) + c_cacheline_branching_factor * sizeof(BpTree::LeafNodeItemType) << "\n"
<< "actual-cacheline-internal-node-size=" << sizeof(BpTree::InternalNodeType) + c_cacheline_branching_factor * sizeof(BpTree::InternalNodeItemType) << "\n"
<< "actual-ospage-leaf-node-size=" << sizeof(BpTree::LeafNodeType) + c_ospage1_branching_factor * sizeof(BpTree::LeafNodeItemType) << "\n"
<< "actual-ospage-internal-node-size=" << sizeof(BpTree::InternalNodeType) + c_ospage1_branching_factor * sizeof(BpTree::InternalNodeItemType) << "\n"
<< "n=" << n << "\n"
<< std::endl;
{
std::array<std::unique_ptr<AbstractTestParams>, 7> const params_v
= {{
std::unique_ptr<AbstractTestParams>(new StdMapTestParams("std-map-insert")),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-min-insert",
c_cacheline_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage16-insert",
c_ospage16_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage8-insert",
c_ospage8_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage4-insert",
c_ospage4_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage2-insert",
c_ospage2_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage-insert",
c_ospage1_branching_factor,
false))
}};
/* note: w/cacheline:
* getting 593ms for 10^6 inserts;
* i.e. ~593ns each
* w/ospage:
* getting 188ms for 10^6 inserts;
* i.e. ~188ns each
* (with ospage size 4k -> branching factor 252)
*/
for(std::uint32_t i_bm = 0; i_bm < params_v.size(); ++i_bm) {
params_v[i_bm]->run_insert_benchmark(random_testdata);
}
}
{
std::array<std::unique_ptr<AbstractTestParams>, 7> const params_v
= {{
std::unique_ptr<AbstractTestParams>(new StdMapTestParams("std-map-erase")),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-min-remove",
c_cacheline_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage16-remove",
c_ospage16_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage8-remove",
c_ospage8_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage4-remove",
c_ospage8_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage2-remove",
c_ospage8_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage1-remove",
c_ospage1_branching_factor,
false))
}};
/* note: cacheline: getting 72us for 10^2 removes;
* i.e. ~7.2ns each
*
* ospage: getting 243us for 10^4 removes;
* i.e. ~24ns each
*/
for (std::uint32_t i_bm = 0; i_bm < params_v.size(); ++i_bm) {
params_v[i_bm]->run_erase_benchmark(random_testdata);
}
}
{
std::array<std::unique_ptr<AbstractTestParams>, 7> const params_v
= {{
std::unique_ptr<AbstractTestParams>(new StdMapTestParams("std-map-lookup")),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-min-lookup",
c_cacheline_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage16-lookup",
c_ospage16_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage8-lookup",
c_ospage8_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage4-lookup",
c_ospage4_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage2-lookup",
c_ospage2_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage1-lookup",
c_ospage1_branching_factor,
false))
}};
/* note: cacheline:
* getting 850us for 10^4 lookups;
* -> ~85ns each
* ospage:
* getting 585us for 10^4 lookups;
* -> ~58ns each
*/
for (std::uint32_t i_bm = 0; i_bm < params_v.size(); ++i_bm) {
params_v[i_bm]->run_lookup_benchmark(random_testdata);
}
}
{
std::array<std::unique_ptr<AbstractTestParams>, 7> const params_v
= {{
std::unique_ptr<AbstractTestParams>(new StdMapTestParams("std-map-traverse")),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-min-traverse",
c_cacheline_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage16-traverse",
c_ospage16_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage8-traverse",
c_ospage8_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage4-traverse",
c_ospage4_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage2-traverse",
c_ospage2_branching_factor,
false)),
std::unique_ptr<AbstractTestParams>(new BtreeTestParams("bplus-ospage1-traverse",
c_ospage1_branching_factor,
false))
}};
/* note: cacheline: getting 25us to traverse tree of size 10^4
* -> ~2.5ns each
* note: ospage: getting 6us to traverse tree of size 10^4
* -> ~0.6ns each
*/
for (std::uint32_t i_bm = 0; i_bm < params_v.size(); ++i_bm) {
params_v[i_bm]->run_traverse_benchmark(random_testdata);
}
}
} /*TEST_CASE(bptree-benchmark)*/
} /*namespace*/
/* end bplustree.cpp */