crimson/onode-staged-tree: imlement fair locate split strategy

Also make adjustments in unit tests.

Signed-off-by: Yingxin Cheng <yingxin.cheng@intel.com>
This commit is contained in:
Yingxin Cheng 2020-09-30 09:48:46 +08:00
parent 1757afc98d
commit c32c1032de
3 changed files with 151 additions and 21 deletions

View File

@ -361,7 +361,7 @@ class NodeLayoutT final : public InternalNodeImpl, public LeafNodeImpl {
size_t split_size = 0;
std::optional<bool> _is_insert_left;
split_at.set(node_stage);
STAGE_T::recursively_locate_split_inserted(
bool locate_nxt = STAGE_T::recursively_locate_split_inserted(
split_size, 0, target_split_size, insert_pos,
insert_stage, insert_size, _is_insert_left, split_at);
is_insert_left = *_is_insert_left;
@ -371,7 +371,14 @@ class NodeLayoutT final : public InternalNodeImpl, public LeafNodeImpl {
<< "(target=" << target_split_size
<< ", current=" << node_stage.size_before(node_stage.keys())
<< ")" << std::endl;
assert(split_size <= target_split_size);
// split_size can be larger than target_split_size in strategy B
// assert(split_size <= target_split_size);
if (locate_nxt) {
assert(insert_stage == STAGE);
assert(split_at.get().is_last());
split_at.set_end();
assert(insert_pos.index == split_at.index());
}
}
auto append_at = split_at;

View File

@ -575,6 +575,7 @@ struct staged {
}
// Note: possible to return an end iterator when is_exclusive is true
// insert_index can still be INDEX_LAST or INDEX_END
template <bool is_exclusive>
size_t seek_split_inserted(
size_t start_size, size_t extra_size, size_t target_size,
@ -1424,26 +1425,50 @@ struct staged {
std::optional<iterator_t> iter;
};
static void recursively_locate_split(
static bool recursively_locate_split(
size_t& current_size, size_t extra_size,
size_t target_size, StagedIterator& split_at) {
assert(current_size <= target_size);
iterator_t& split_iter = split_at.get();
current_size = split_iter.seek_split(current_size, extra_size, target_size);
assert(current_size <= target_size);
assert(!split_iter.is_end());
if (split_iter.index() == 0) {
extra_size += iterator_t::header_size();
} else {
extra_size = 0;
}
bool locate_nxt;
if constexpr (!IS_BOTTOM) {
NXT_STAGE_T::recursively_locate_split(
locate_nxt = NXT_STAGE_T::recursively_locate_split(
current_size, extra_size + split_iter.size_to_nxt(),
target_size, split_at.nxt());
} else { // IS_BOTTOM
// located upper_bound, fair split strategy
size_t nxt_size = split_iter.size() + extra_size;
assert(current_size + nxt_size > target_size);
if (current_size + nxt_size/2 < target_size) {
// include next
current_size += nxt_size;
locate_nxt = true;
} else {
// exclude next
locate_nxt = false;
}
}
if (locate_nxt) {
if (split_iter.is_last()) {
return true;
} else {
++split_at;
return false;
}
} else {
return false;
}
}
static void recursively_locate_split_inserted(
static bool recursively_locate_split_inserted(
size_t& current_size, size_t extra_size, size_t target_size,
position_t& insert_pos, match_stage_t insert_stage, size_t insert_size,
std::optional<bool>& is_insert_left, StagedIterator& split_at) {
@ -1456,6 +1481,7 @@ struct staged {
current_size, extra_size, target_size,
insert_index, insert_size, is_insert_left);
assert(is_insert_left.has_value());
assert(current_size <= target_size);
if (split_iter.index() == 0) {
extra_size += iterator_t::header_size();
} else {
@ -1465,16 +1491,71 @@ struct staged {
// split_iter can be end
// found the lower-bound of target_size
// ...[s_index-1] |!| (i_index) [s_index]...
return;
// located upper-bound, fair split strategy
// look at the next slot (the insert item)
size_t nxt_size = insert_size + extra_size;
assert(current_size + nxt_size > target_size);
if (current_size + nxt_size/2 < target_size) {
// include next
*is_insert_left = true;
current_size += nxt_size;
if (split_iter.is_end()) {
// ...[s_index-1] (i_index) |!|
return true;
} else {
return false;
}
} else {
// exclude next
return false;
}
} else {
// Already considered insert effect in the current stage.
// Look into the next stage to identify the target_size lower-bound w/o
// insert effect.
assert(!split_iter.is_end());
bool locate_nxt;
if constexpr (!IS_BOTTOM) {
NXT_STAGE_T::recursively_locate_split(
locate_nxt = NXT_STAGE_T::recursively_locate_split(
current_size, extra_size + split_iter.size_to_nxt(),
target_size, split_at.nxt());
} else { // IS_BOTTOM
// located upper-bound, fair split strategy
// look at the next slot
size_t nxt_size = split_iter.size() + extra_size;
assert(current_size + nxt_size > target_size);
if (current_size + nxt_size/2 < target_size) {
// include next
current_size += nxt_size;
locate_nxt = true;
} else {
// exclude next
locate_nxt = false;
}
}
if (locate_nxt) {
if (split_iter.is_last()) {
auto end_index = split_iter.index() + 1;
if (insert_index == INDEX_END) {
insert_index = end_index;
}
assert(insert_index <= end_index);
if (insert_index == end_index) {
assert(*is_insert_left == false);
split_iter.set_end();
// ...[s_index-1] |!| (i_index)
return false;
} else {
assert(*is_insert_left == true);
return true;
}
} else {
++split_at;
return false;
}
} else {
return false;
}
}
} else {
@ -1484,36 +1565,59 @@ struct staged {
current_size, extra_size, target_size,
insert_index, insert_size, is_insert_left);
assert(!split_iter.is_end());
assert(current_size <= target_size);
if (split_iter.index() == 0) {
extra_size += iterator_t::header_size();
} else {
extra_size = 0;
}
bool locate_nxt;
if (!is_insert_left.has_value()) {
// Considered insert effect in the current stage, and insert happens
// in the lower stage.
// Look into the next stage to identify the target_size lower-bound w/
// insert effect.
assert(split_iter.index() == insert_index);
NXT_STAGE_T::recursively_locate_split_inserted(
locate_nxt = NXT_STAGE_T::recursively_locate_split_inserted(
current_size, extra_size + split_iter.size_to_nxt(), target_size,
insert_pos.nxt, insert_stage, insert_size,
is_insert_left, split_at.nxt());
assert(is_insert_left.has_value());
return;
#ifndef NDEBUG
if (locate_nxt) {
assert(*is_insert_left == true);
}
#endif
} else {
// is_insert_left.has_value() == true
// Insert will *not* happen in the lower stage.
// Need to look into the next stage to identify the target_size
// lower-bound w/ insert effect
NXT_STAGE_T::recursively_locate_split(
assert(split_iter.index() != insert_index);
locate_nxt = NXT_STAGE_T::recursively_locate_split(
current_size, extra_size + split_iter.size_to_nxt(),
target_size, split_at.nxt());
return;
#ifndef NDEBUG
if (split_iter.index() < insert_index) {
assert(*is_insert_left == false);
} else {
assert(*is_insert_left == true);
}
#endif
}
if (locate_nxt) {
if (split_iter.is_last()) {
return true;
} else {
++split_at;
return false;
}
} else {
return false;
}
} else {
assert(false && "impossible path");
return;
return false;;
}
}
}

View File

@ -521,7 +521,7 @@ TEST_F(c_dummy_test_t, 4_split_leaf_node)
TestTree test;
test.build_tree({2, 5}, {2, 5}, {2, 5}, 120).get0();
auto& onode = test.create_onode(1084);
auto& onode = test.create_onode(1144);
logger().info("\n---------------------------------------------"
"\nsplit at stage 2; insert to left front at stage 2, 1, 0\n");
test.split(make_ghobj(1, 1, 1, "ns3", "oid3", 3, 3), onode).get0();
@ -536,7 +536,7 @@ TEST_F(c_dummy_test_t, 4_split_leaf_node)
test.split(make_ghobj(3, 3, 3, "ns1", "oid1", 3, 3), onode).get0();
test.split(make_ghobj(3, 3, 3, "ns2", "oid2", 1, 1), onode).get0();
auto& onode0 = test.create_onode(1476);
auto& onode0 = test.create_onode(1416);
logger().info("\n---------------------------------------------"
"\nsplit at stage 2; insert to right front at stage 0, 1, 2, 1, 0\n");
test.split(make_ghobj(3, 3, 3, "ns4", "oid4", 5, 5), onode0).get0();
@ -581,7 +581,7 @@ TEST_F(c_dummy_test_t, 4_split_leaf_node)
test.split(make_ghobj(4, 4, 4, "ns1", "oid1", 3, 3), onode2).get0();
test.split(make_ghobj(4, 4, 4, "ns2", "oid2", 1, 1), onode2).get0();
auto& onode3 = test.create_onode(964);
auto& onode3 = test.create_onode(834);
logger().info("\n---------------------------------------------"
"\nsplit at stage 0; insert to right middle at stage 0, 1, 2, 1, 0\n");
test.split(make_ghobj(3, 3, 3, "ns4", "oid4", 5, 5), onode3).get0();
@ -912,6 +912,20 @@ TEST_F(c_dummy_test_t, 5_split_internal_node)
"\nbefore internal node insert:\n");
auto padding = std::string(250, '_');
auto keys = build_key_set({2, 6}, {2, 5}, {2, 5}, padding, true);
keys.erase(make_ghobj(2, 2, 2, "ns2", "oid2" + padding, 2, 2));
keys.erase(make_ghobj(2, 2, 2, "ns2", "oid2" + padding, 3, 3));
keys.erase(make_ghobj(2, 2, 2, "ns2", "oid2" + padding, 4, 4));
keys.erase(make_ghobj(5, 5, 5, "ns4", "oid4" + padding, 2, 2));
keys.erase(make_ghobj(5, 5, 5, "ns4", "oid4" + padding, 3, 3));
keys.erase(make_ghobj(5, 5, 5, "ns4", "oid4" + padding, 4, 4));
auto padding_s = std::string(257, '_');
keys.insert(make_ghobj(2, 2, 2, "ns2", "oid2" + padding_s, 2, 2));
keys.insert(make_ghobj(2, 2, 2, "ns2", "oid2" + padding_s, 3, 3));
keys.insert(make_ghobj(2, 2, 2, "ns2", "oid2" + padding_s, 4, 4));
auto padding_e = std::string(248, '_');
keys.insert(make_ghobj(5, 5, 5, "ns4", "oid4" + padding_e, 2, 2));
keys.insert(make_ghobj(5, 5, 5, "ns4", "oid4" + padding_e, 3, 3));
keys.insert(make_ghobj(5, 5, 5, "ns4", "oid4" + padding_e, 4, 4));
pool.build_tree(keys).unsafe_get0();
logger().info("\n---------------------------------------------"
@ -933,7 +947,7 @@ TEST_F(c_dummy_test_t, 5_split_internal_node)
logger().info("\n---------------------------------------------"
"\nsplit at stage 2; insert to right back at stage 0, 1, 2\n");
pool.test_split(
make_ghobj(5, 5, 5, "ns4", "oid4" + padding, 5, 5), search_position_t::end()).get();
make_ghobj(5, 5, 5, "ns4", "oid4" + padding_e, 5, 5), search_position_t::end()).get();
pool.test_split(make_ghobj(5, 5, 5, "ns5", "oid5", 3, 3), search_position_t::end()).get();
pool.test_split(make_ghobj(6, 6, 6, "ns3", "oid3", 3, 3), search_position_t::end()).get();
@ -941,7 +955,7 @@ TEST_F(c_dummy_test_t, 5_split_internal_node)
"\nsplit at stage 0; insert to left front at stage 2, 1, 0\n");
pool.test_split(make_ghobj(1, 1, 1, "ns3", "oid3", 3, 3), {0, {0, {0}}}).get();
pool.test_split(make_ghobj(2, 2, 2, "ns1", "oid1", 3, 3), {0, {0, {0}}}).get();
pool.test_split(make_ghobj(2, 2, 2, "ns2", "oid2" + padding, 1, 1), {0, {0, {0}}}).get();
pool.test_split(make_ghobj(2, 2, 2, "ns2", "oid2" + padding_s, 1, 1), {0, {0, {0}}}).get();
logger().info("\n---------------------------------------------"
"\nsplit at stage 0/1; insert to left middle at stage 0, 1, 2, 1, 0\n");
@ -997,9 +1011,8 @@ TEST_F(c_dummy_test_t, 5_split_internal_node)
{
logger().info("\n---------------------------------------------"
"\nbefore internal node insert (3):\n");
auto padding = std::string(417, '_');
auto padding = std::string(420, '_');
auto keys = build_key_set({2, 5}, {2, 5}, {2, 5}, padding, true);
keys.insert(make_ghobj(4, 4, 4, "ns3", "oid3" + padding, 5, 5));
keys.erase(make_ghobj(4, 4, 4, "ns4", "oid4" + padding, 2, 2));
keys.erase(make_ghobj(4, 4, 4, "ns4", "oid4" + padding, 3, 3));
keys.erase(make_ghobj(4, 4, 4, "ns4", "oid4" + padding, 4, 4));
@ -1015,9 +1028,15 @@ TEST_F(c_dummy_test_t, 5_split_internal_node)
{
logger().info("\n---------------------------------------------"
"\nbefore internal node insert (4):\n");
auto padding = std::string(360, '_');
auto padding = std::string(361, '_');
auto keys = build_key_set({2, 5}, {2, 5}, {2, 5}, padding, true);
keys.insert(make_ghobj(4, 4, 4, "ns4", "oid4" + padding, 5, 5));
keys.erase(make_ghobj(2, 2, 2, "ns2", "oid2" + padding, 2, 2));
keys.erase(make_ghobj(2, 2, 2, "ns2", "oid2" + padding, 3, 3));
keys.erase(make_ghobj(2, 2, 2, "ns2", "oid2" + padding, 4, 4));
auto padding_s = std::string(387, '_');
keys.insert(make_ghobj(2, 2, 2, "ns2", "oid2" + padding_s, 2, 2));
keys.insert(make_ghobj(2, 2, 2, "ns2", "oid2" + padding_s, 3, 3));
keys.insert(make_ghobj(2, 2, 2, "ns2", "oid2" + padding_s, 4, 4));
pool.build_tree(keys).unsafe_get0();
logger().info("\n---------------------------------------------"