From 5b917829cb2e607364c6a9395452aa4d62a3828c Mon Sep 17 00:00:00 2001 From: tzaeschke Date: Thu, 15 Dec 2022 17:41:16 +0100 Subject: [PATCH 1/6] initial --- include/phtree/v16/phtree_v16.h | 133 ++++++++++++++++++++++++++++- test/phtree_multimap_box_d_test.cc | 49 ++--------- 2 files changed, 139 insertions(+), 43 deletions(-) diff --git a/include/phtree/v16/phtree_v16.h b/include/phtree/v16/phtree_v16.h index dc7e951f..965b38e4 100644 --- a/include/phtree/v16/phtree_v16.h +++ b/include/phtree/v16/phtree_v16.h @@ -459,7 +459,7 @@ class PhTreeV16 { * - CREATES the destination entry if it does not exist! */ template - size_t _relocate_mm( + size_t _relocate_mm2( const KeyT& old_key, const KeyT& new_key, bool verify_exists, @@ -540,6 +540,137 @@ class PhTreeV16 { return result; } + template + size_t _relocate_mm( + const KeyT& old_key, + const KeyT& new_key, + bool verify_exists, + RELOCATE&& relocate_fn, + COUNT&& count_fn) { + bit_width_t n_diverging_bits = NumberOfDivergingBits(old_key, new_key); + + // EntryIterator iter = root_.GetNode().End(); + auto iter = root_.GetNode().End(); + EntryT* current_entry = &root_; // An entry. + EntryT* old_node_entry = nullptr; // Node that contains entry to be removed + EntryT* old_node_entry_parent = nullptr; // Parent of the old_node_entry + EntryT* new_node_entry = nullptr; // Node that will contain new entry + // Find node for removal + while (current_entry && current_entry->IsNode()) { + old_node_entry_parent = old_node_entry; + old_node_entry = current_entry; + auto postfix_len = old_node_entry->GetNodePostfixLen(); + if (postfix_len + 1 >= n_diverging_bits) { + new_node_entry = old_node_entry; + } + // TODO stop earlier, we are going to have to redo this after insert.... + bool is_found = false; + iter = current_entry->GetNode().LowerBound(old_key, postfix_len, is_found); + current_entry = is_found ? &iter->second : nullptr; + } + EntryT* old_entry = current_entry; // Entry to be removed + + // Can we stop already? + if (old_entry == nullptr) { + return 0; // old_key not found! + } + + // Are the keys equal? + if (n_diverging_bits == 0) { + return count_fn(old_entry->GetValue()); + } + // Are the keys in the same quadrant? -> same entry + if (old_node_entry->GetNodePostfixLen() >= n_diverging_bits) { + if (old_entry->GetValue().size() == 1) { + auto result = count_fn(old_entry->GetValue()); + if (result > 0) { + old_entry->SetKey(new_key); + } + return result; + } + + // insert new entry and recalculate old_entry. +// bool is_inserted; +// auto new_entry = &new_node_entry->GetNode().Emplace( +// iter, is_inserted, new_key, new_node_entry->GetNodePostfixLen()); +// ++num_entries_; +// assert(is_inserted); +// old_entry = old_node_entry; +// while (old_entry && old_entry->IsNode()) { +// old_node_entry = old_entry; +// old_entry = old_entry->GetNode().Find(old_key, old_entry->GetNodePostfixLen()); +// } +// auto result = relocate_fn(old_entry->GetValue(), new_entry->GetValue()); +// if (result == 0) { +// clean_up(new_key, new_entry, new_node_entry); +// } else { +// clean_up(old_key, old_entry, old_node_entry); +// } +// return result; + } + + bool same_node = old_node_entry == new_node_entry; + + // Find node for insertion + auto new_entry = new_node_entry; + bool is_found = false; + while (new_entry && new_entry->IsNode()) { + is_found = false; // TODO fix for non-MM?!?! + new_node_entry = new_entry; + iter = + new_entry->GetNode().LowerBound(new_key, new_entry->GetNodePostfixLen(), is_found); + new_entry = is_found ? &iter->second : nullptr; + } + if (!is_found) { + bool is_inserted = false; + new_entry = &new_node_entry->GetNode().Emplace( + iter, + is_inserted, + new_key, + new_node_entry->GetNodePostfixLen()); // TODO only MM + num_entries_ += is_inserted; + + // adjust old_entry if necessary + if (same_node) { + old_entry = old_node_entry; + while (old_entry && old_entry->IsNode()) { + old_node_entry = old_entry; + old_entry = old_entry->GetNode().Find(old_key, old_entry->GetNodePostfixLen()); + } + } + } + + // vanilla relocate + auto result = relocate_fn(old_entry->GetValue(), new_entry->GetValue()); + + // Erase old value. See comments in try_emplace(iterator) for details. + if (old_node_entry_parent == new_node_entry) { + // In this case the old_node_entry may have been invalidated by the previous + // insertion. + old_node_entry = old_node_entry_parent; + } + + clean_up(new_key, new_entry, new_node_entry); + clean_up(old_key, old_entry, old_node_entry); + return result; + } + + private: + void clean_up(const KeyT& key, EntryT* entry, EntryT* node_entry) { + // It may happen that node_entry is not the immediate parent, but that is okay! + // TODO use iterators? + if (entry != nullptr && entry->GetValue().empty()) { + bool found = false; // TODO fix in non-MM + while (node_entry != nullptr && node_entry->IsNode()) { + found = false; + node_entry = + node_entry->GetNode().Erase(key, node_entry, node_entry != &root_, found); + } + num_entries_ -= found; + } + } + + public: auto _find_or_create_two_mm(const KeyT& old_key, const KeyT& new_key, bool count_equals) { using Iter = IteratorWithParent; bit_width_t n_diverging_bits = NumberOfDivergingBits(old_key, new_key); diff --git a/test/phtree_multimap_box_d_test.cc b/test/phtree_multimap_box_d_test.cc index 386de516..abb4ada7 100644 --- a/test/phtree_multimap_box_d_test.cc +++ b/test/phtree_multimap_box_d_test.cc @@ -499,41 +499,6 @@ TEST(PhTreeMMBoxDTest, TestUpdateWithEmplaceHint) { tree.clear(); } -// TEST(PhTreeMMDTest, TestUpdateWithRelocate) { -// const dimension_t dim = 3; -// TestTree tree; -// size_t N = 10000; -// std::array deltas{0, 0.1, 1, 10}; -// std::vector> points; -// populate(tree, points, N); -// -// for (auto delta : deltas) { -// size_t i = 0; -// for (auto& p : points) { -// auto pOld = p; -// TestPoint pNew; -// if (relocate_to_existing_coordinate) { -// pNew = delta > 0.0 ? points[(i + 17) % N] : pOld; -// } else { -// pNew = {pOld[0] + delta, pOld[1] + delta, pOld[2] + delta}; -// } -// PhPointD min{pOld.min()[0] + delta, pOld.min()[1] + delta, pOld.min()[2] + -// delta}; PhPointD max{pOld.max()[0] + delta, pOld.max()[1] + delta, pOld.max()[2] -// + delta}; TestPoint pNew{min, max}; ASSERT_EQ(1, tree.relocate(pOld, pNew, -// Id(i))); if (delta > 0.0) { -// // second time fails because value has already been moved -// ASSERT_EQ(0, tree.relocate(pOld, pNew, Id(i))); -// } -// ASSERT_EQ(Id(i), *tree.find(pNew, Id(i))); -// p = pNew; -// ++i; -// } -// } -// -// ASSERT_EQ(N, tree.size()); -// tree.clear(); -// } - void TestUpdateWithRelocate(bool relocate_to_existing_coordinate) { const dimension_t dim = 3; TestTree tree; @@ -575,15 +540,15 @@ void TestUpdateWithRelocate(bool relocate_to_existing_coordinate) { tree.clear(); } -TEST(PhTreeMMDTest, TestUpdateWithRelocateDelta) { +TEST(PhTreeMMBoxDTest, TestUpdateWithRelocateDelta) { TestUpdateWithRelocate(false); } -TEST(PhTreeMMDTest, TestUpdateWithRelocateToExisting) { +TEST(PhTreeMMBoxDTest, TestUpdateWithRelocateToExisting) { TestUpdateWithRelocate(true); } -TEST(PhTreeMMDTest, TestUpdateWithRelocateCornerCases) { +TEST(PhTreeMMBoxDTest, TestUpdateWithRelocateCornerCases) { const dimension_t dim = 3; TestTree tree; TestPoint point0{{1, 2, 3}, {2, 3, 4}}; @@ -703,7 +668,7 @@ struct FilterEvenId { } }; -TEST(PhTreeMMDTest, TestExtentFilter) { +TEST(PhTreeMMBoxDTest, TestExtentFilter) { const dimension_t dim = 3; TestTree tree; size_t N = 10000; @@ -720,7 +685,7 @@ TEST(PhTreeMMDTest, TestExtentFilter) { ASSERT_EQ(N, num_e * 2); } -TEST(PhTreeMMDTest, TestExtentForEachFilter) { +TEST(PhTreeMMBoxDTest, TestExtentForEachFilter) { const dimension_t dim = 3; TestTree tree; size_t N = 10000; @@ -764,7 +729,7 @@ TEST(PhTreeMMBoxDTest, TestRangeBasedForLoop) { ASSERT_EQ(N, num_e2); } -TEST(PhTreeMMDTest, TestEstimateCountIntersect) { +TEST(PhTreeMMBoxDTest, TestEstimateCountIntersect) { const dimension_t dim = 3; TestTree tree; size_t N = 1000; @@ -792,7 +757,7 @@ TEST(PhTreeMMDTest, TestEstimateCountIntersect) { ASSERT_EQ(N, n_all); } -TEST(PhTreeMMDTest, TestEstimateCountInclude) { +TEST(PhTreeMMBoxDTest, TestEstimateCountInclude) { const dimension_t dim = 3; TestTree tree; size_t N = 1000; From fc8e17f43431571ea87b87c12a04f510ee359e9f Mon Sep 17 00:00:00 2001 From: tzaeschke Date: Fri, 16 Dec 2022 17:37:54 +0100 Subject: [PATCH 2/6] initial --- include/phtree/v16/phtree_v16.h | 230 ++++++++++++++++++++++++++------ 1 file changed, 190 insertions(+), 40 deletions(-) diff --git a/include/phtree/v16/phtree_v16.h b/include/phtree/v16/phtree_v16.h index 965b38e4..4d93d1d8 100644 --- a/include/phtree/v16/phtree_v16.h +++ b/include/phtree/v16/phtree_v16.h @@ -26,6 +26,7 @@ #include "iterator_knn_hs.h" #include "iterator_with_parent.h" #include "node.h" +#include namespace improbable::phtree::v16 { @@ -459,7 +460,7 @@ class PhTreeV16 { * - CREATES the destination entry if it does not exist! */ template - size_t _relocate_mm2( + size_t _relocate_mm1( const KeyT& old_key, const KeyT& new_key, bool verify_exists, @@ -541,7 +542,7 @@ class PhTreeV16 { } template - size_t _relocate_mm( + size_t _relocate_mm3( const KeyT& old_key, const KeyT& new_key, bool verify_exists, @@ -549,13 +550,184 @@ class PhTreeV16 { COUNT&& count_fn) { bit_width_t n_diverging_bits = NumberOfDivergingBits(old_key, new_key); - // EntryIterator iter = root_.GetNode().End(); + if (!verify_exists && n_diverging_bits == 0) { + return 1; // We omit calling because that would require looking up the entry... + } + auto iter = root_.GetNode().End(); + EntryT* new_entry = &root_; // An entry. + EntryT* old_node_entry = nullptr; // Node that contains entry to be removed + EntryT* new_node_entry = nullptr; // Node that will contain new entry + // Find the deepest common parent node for removal and insertion + bool is_inserted = false; + while (new_entry && new_entry->IsNode()) { + new_node_entry = new_entry; + auto postfix_len = new_entry->GetNodePostfixLen(); + if (postfix_len + 1 >= n_diverging_bits) { + old_node_entry = new_node_entry; + } + new_entry = new_entry->GetNode().Find(new_key, postfix_len); + // We could instead use the following, this may be faster for sparse_map/b_plus_map + // but is very slow for array_map (even with '-mbmi2' +// bool is_found = false; +// iter = new_entry->GetNode().LowerBound(new_key, postfix_len, is_found); +// new_entry = is_found ? &iter->second : nullptr; + } + //old_node_entry = new_node_entry; + + if (new_entry != nullptr && n_diverging_bits == 0) { + // keys are equal ... + return count_fn(new_entry->GetValue()); + } + + // Are we inserting in same node and same quadrant? + // This works only if the predicate has the same result for ALL entries. This can only + // be guaranteed if there is only one entry (or if we had proper TRUE/FALSE) predicates. + if (new_entry != nullptr && + new_node_entry->GetNodePostfixLen() >= n_diverging_bits && + new_entry->GetValue().size() == 1) { + // Technically this should be: (old_entry->GetValue().size() == 1), but that's the same. + auto result = count_fn(new_entry->GetValue()); + if (result > 0) { + new_entry->SetKey(new_key); + } + return result; + } + + // Find node for insertion of new bucket + if (new_entry == nullptr) { + new_entry = &new_node_entry->GetNode().Emplace( + is_inserted, new_key, new_node_entry->GetNodePostfixLen()); + num_entries_ += is_inserted; + assert(new_entry != nullptr); + } + + auto* old_entry = old_node_entry; + while (old_entry && old_entry->IsNode()) { + old_node_entry = old_entry; + old_entry = old_entry->GetNode().Find(old_key, old_entry->GetNodePostfixLen()); + } + + size_t result; + if (old_entry == nullptr) { + // Does old_entry exist? + result = 0; // old_key not found or invalid! + } else if (n_diverging_bits == 0) { + // keys are equal ... + result = count_fn(old_entry->GetValue()); +// } else if ( +// old_node_entry->GetNodePostfixLen() >= n_diverging_bits && +// old_entry->GetValue().size() == 1) { +// // Are we inserting in same node and same quadrant? +// // This works only if the predicate has the same result for ALL entries. This can only +// // be guaranteed if there is only one entry (or if we had proper TRUE/FALSE) predicates. +// result = count_fn(old_entry->GetValue()); +// if (result > 0) { +// old_entry->SetKey(new_key); +// } + } else { + // vanilla relocate + result = relocate_fn(old_entry->GetValue(), new_entry->GetValue()); + } + + if (old_entry != nullptr && old_entry->GetValue().empty()) { + bool found = false; + old_node_entry->GetNode().Erase( + old_key, old_node_entry, old_node_entry != &root_, found); + num_entries_ -= found; + } else if (new_entry->GetValue().empty()) { + bool found = false; + // new_node_entry may not be the immediate parent because Node::emplace() may create + // subnodes. + while (new_node_entry != nullptr && new_node_entry->IsNode()) { + new_node_entry = new_node_entry->GetNode().Erase( + new_key, new_node_entry, new_node_entry != &root_, found); + } + num_entries_ -= found; + } + + return result; + } + + inline static ulong N{0}; + inline static ulong X1{0}; + inline static ulong X2{0}; + inline static ulong X3{0}; + inline static ulong X4{0}; + inline static ulong X5{0}; + inline static ulong X6{0}; + inline static ulong X7{0}; + inline static ulong PREV_SIZE{0}; + + inline static ulong IBOTH{0}; + inline static ulong INEW{0}; + inline static ulong IOLD{0}; + inline static ulong ICLEAN{0}; + + inline static ulong I1{0}; + inline static ulong I2{0}; + inline static ulong I3{0}; + inline static ulong I4{0}; + inline static ulong I5{0}; + inline static ulong I6{0}; + + void print() { + if (num_entries_ >= PREV_SIZE * 10 || num_entries_*4 < PREV_SIZE) { +// std::cout << "S=" << num_entries_ << " N=" << N << " X=" << X1 << " " << X2 << " " +// << X3 << " " << X4 << " " << X5 << " " << X6 << " " << X7 << " " +// << std::endl; +// std::cout << " both/n/o/c=" << IBOTH << " " << INEW << " " << IOLD << " " << ICLEAN +// << " I=" << I1 << " " << I2 << " " << I3 << " " << I4 << " " << I5 << " " +// << std::endl; + PREV_SIZE = num_entries_; + N = 0; + X1 = 0; + X2 = 0; + X3 = 0; + X4 = 0; + X5 = 0; + X6 = 0; + X7 = 0; + IBOTH = 0; + ICLEAN = 0; + INEW = 0; + IOLD = 0; + I1 = 0; + I2 = 0; + I3 = 0; + I4 = 0; + I5 = 0; + } + + ++N; + if (N % 200000 == 0 && num_entries_ > 200000) { + auto tot = I1 + I2 + I3 + I4 + I5; + std::cout << "N=" << N << " X=" << X1 << " " << X2 << " " << X3 << " " << X4 << " " + << X5 << " " << X6 << " " << X7 << " " << std::endl; + std::cout << " both/n/o/c=" << IBOTH << " " << INEW << " " << IOLD << " " << ICLEAN + << " I=" << I1 << " " << I2 << " " << I3 << " " << I4 << " " << I5 + << " = " << tot << std::endl; + } + } + + template + size_t _relocate_mm( + const KeyT& old_key, + const KeyT& new_key, + bool verify_exists, + RELOCATE&& relocate_fn, + COUNT&& count_fn) { + bit_width_t n_diverging_bits = NumberOfDivergingBits(old_key, new_key); + + if (!verify_exists && n_diverging_bits == 0) { + return 1; // We omit calling because that would require looking up the entry... + } + EntryT* current_entry = &root_; // An entry. EntryT* old_node_entry = nullptr; // Node that contains entry to be removed EntryT* old_node_entry_parent = nullptr; // Parent of the old_node_entry EntryT* new_node_entry = nullptr; // Node that will contain new entry - // Find node for removal + // Find node or entry for removal while (current_entry && current_entry->IsNode()) { old_node_entry_parent = old_node_entry; old_node_entry = current_entry; @@ -564,9 +736,10 @@ class PhTreeV16 { new_node_entry = old_node_entry; } // TODO stop earlier, we are going to have to redo this after insert.... - bool is_found = false; - iter = current_entry->GetNode().LowerBound(old_key, postfix_len, is_found); - current_entry = is_found ? &iter->second : nullptr; +// bool is_found = false; +// iter = current_entry->GetNode().LowerBound(old_key, postfix_len, is_found); +// current_entry = is_found ? &iter->second : nullptr; + current_entry = current_entry->GetNode().Find(old_key, postfix_len); } EntryT* old_entry = current_entry; // Entry to be removed @@ -588,52 +761,28 @@ class PhTreeV16 { } return result; } - - // insert new entry and recalculate old_entry. -// bool is_inserted; -// auto new_entry = &new_node_entry->GetNode().Emplace( -// iter, is_inserted, new_key, new_node_entry->GetNodePostfixLen()); -// ++num_entries_; -// assert(is_inserted); -// old_entry = old_node_entry; -// while (old_entry && old_entry->IsNode()) { -// old_node_entry = old_entry; -// old_entry = old_entry->GetNode().Find(old_key, old_entry->GetNodePostfixLen()); -// } -// auto result = relocate_fn(old_entry->GetValue(), new_entry->GetValue()); -// if (result == 0) { -// clean_up(new_key, new_entry, new_node_entry); -// } else { -// clean_up(old_key, old_entry, old_node_entry); -// } -// return result; } bool same_node = old_node_entry == new_node_entry; // Find node for insertion auto new_entry = new_node_entry; - bool is_found = false; + bool is_inserted = false; while (new_entry && new_entry->IsNode()) { - is_found = false; // TODO fix for non-MM?!?! + //is_found = false; // TODO fix for non-MM?!?! new_node_entry = new_entry; - iter = - new_entry->GetNode().LowerBound(new_key, new_entry->GetNodePostfixLen(), is_found); - new_entry = is_found ? &iter->second : nullptr; - } - if (!is_found) { - bool is_inserted = false; - new_entry = &new_node_entry->GetNode().Emplace( - iter, - is_inserted, - new_key, - new_node_entry->GetNodePostfixLen()); // TODO only MM + is_inserted = false; + new_entry = + &new_entry->GetNode().Emplace(is_inserted, new_key, new_entry->GetNodePostfixLen()); num_entries_ += is_inserted; - + } + if (is_inserted) { // adjust old_entry if necessary if (same_node) { old_entry = old_node_entry; while (old_entry && old_entry->IsNode()) { + ++I3; + ++IOLD; old_node_entry = old_entry; old_entry = old_entry->GetNode().Find(old_key, old_entry->GetNodePostfixLen()); } @@ -662,6 +811,7 @@ class PhTreeV16 { if (entry != nullptr && entry->GetValue().empty()) { bool found = false; // TODO fix in non-MM while (node_entry != nullptr && node_entry->IsNode()) { + ++ICLEAN; found = false; node_entry = node_entry->GetNode().Erase(key, node_entry, node_entry != &root_, found); From 3527d2dc4e00c47f4427d353a6c749cd4a3e5d2a Mon Sep 17 00:00:00 2001 From: tzaeschke Date: Fri, 16 Dec 2022 18:18:10 +0100 Subject: [PATCH 3/6] initial --- include/phtree/v16/phtree_v16.h | 317 +++----------------------------- 1 file changed, 28 insertions(+), 289 deletions(-) diff --git a/include/phtree/v16/phtree_v16.h b/include/phtree/v16/phtree_v16.h index 4d93d1d8..e9e5ad6e 100644 --- a/include/phtree/v16/phtree_v16.h +++ b/include/phtree/v16/phtree_v16.h @@ -453,263 +453,20 @@ class PhTreeV16 { public: /* - * Tries to locate two entries that are 'close' to each other. + * This function is used (internally) by the PH-tree multimap. * - * Special behavior: - * - returns end() if old_key does not exist; - * - CREATES the destination entry if it does not exist! + * Relocate (move) an entry from one position to another, subject to a predicate. + * + * @param old_key + * @param new_key + * @param verify_exists. If true, verifies that the keys exists, even if the keys are identical. + * If false, identical keys simply return '1', even if the keys don“t actually + * exist. This avoid searching the tree. + * @param RELOCATE A function that handles relocation between buckets. + * @param COUNT A function that veifies relocation in the same bucket, e.g. for identical + * keys, or if the whole bucket is relocated. + * @return The number of relocated entries. */ - template - size_t _relocate_mm1( - const KeyT& old_key, - const KeyT& new_key, - bool verify_exists, - RELOCATE&& relocate_fn, - COUNT&& count_fn) { - bit_width_t n_diverging_bits = NumberOfDivergingBits(old_key, new_key); - - if (!verify_exists && n_diverging_bits == 0) { - return 1; // We omit calling because that would require looking up the entry... - } - - EntryT* new_entry = &root_; // An entry. - EntryT* old_node_entry = nullptr; // Node that contains entry to be removed - EntryT* new_node_entry = nullptr; // Node that will contain new entry - // Find the deepest common parent node for removal and insertion - bool is_inserted = false; - while (new_entry && new_entry->IsNode() && - new_entry->GetNodePostfixLen() + 1 >= n_diverging_bits) { - new_node_entry = new_entry; - auto postfix_len = new_entry->GetNodePostfixLen(); - new_entry = &new_entry->GetNode().Emplace(is_inserted, new_key, postfix_len); - } - old_node_entry = new_node_entry; - - // Find node for insertion of new bucket - while (new_entry->IsNode()) { - new_node_entry = new_entry; - new_entry = - &new_entry->GetNode().Emplace(is_inserted, new_key, new_entry->GetNodePostfixLen()); - } - num_entries_ += is_inserted; - assert(new_entry != nullptr); - - auto* old_entry = old_node_entry; - while (old_entry && old_entry->IsNode()) { - old_node_entry = old_entry; - old_entry = old_entry->GetNode().Find(old_key, old_entry->GetNodePostfixLen()); - } - - size_t result; - if (old_entry == nullptr) { - // Does old_entry exist? - result = 0; // old_key not found or invalid! - } else if (n_diverging_bits == 0) { - // keys are equal ... - result = count_fn(old_entry->GetValue()); - } else if ( - old_node_entry->GetNodePostfixLen() >= n_diverging_bits && - old_entry->GetValue().size() == 1) { - // Are we inserting in same node and same quadrant? - // This works only if the predicate has the same result for ALL entries. This can only - // be guaranteed if there is only one entry (or if we had proper TRUE/FALSE) predicates. - result = count_fn(old_entry->GetValue()); - if (result > 0) { - old_entry->SetKey(new_key); - } - } else { - // vanilla relocate - result = relocate_fn(old_entry->GetValue(), new_entry->GetValue()); - } - - if (old_entry != nullptr && old_entry->GetValue().empty()) { - bool found = false; - old_node_entry->GetNode().Erase( - old_key, old_node_entry, old_node_entry != &root_, found); - num_entries_ -= found; - } else if (new_entry->GetValue().empty()) { - bool found = false; - // new_node_entry may not be the immediate parent because Node::emplace() may create - // subnodes. - while (new_node_entry != nullptr && new_node_entry->IsNode()) { - new_node_entry = new_node_entry->GetNode().Erase( - new_key, new_node_entry, new_node_entry != &root_, found); - } - num_entries_ -= found; - } - - return result; - } - - template - size_t _relocate_mm3( - const KeyT& old_key, - const KeyT& new_key, - bool verify_exists, - RELOCATE&& relocate_fn, - COUNT&& count_fn) { - bit_width_t n_diverging_bits = NumberOfDivergingBits(old_key, new_key); - - if (!verify_exists && n_diverging_bits == 0) { - return 1; // We omit calling because that would require looking up the entry... - } - - auto iter = root_.GetNode().End(); - EntryT* new_entry = &root_; // An entry. - EntryT* old_node_entry = nullptr; // Node that contains entry to be removed - EntryT* new_node_entry = nullptr; // Node that will contain new entry - // Find the deepest common parent node for removal and insertion - bool is_inserted = false; - while (new_entry && new_entry->IsNode()) { - new_node_entry = new_entry; - auto postfix_len = new_entry->GetNodePostfixLen(); - if (postfix_len + 1 >= n_diverging_bits) { - old_node_entry = new_node_entry; - } - new_entry = new_entry->GetNode().Find(new_key, postfix_len); - // We could instead use the following, this may be faster for sparse_map/b_plus_map - // but is very slow for array_map (even with '-mbmi2' -// bool is_found = false; -// iter = new_entry->GetNode().LowerBound(new_key, postfix_len, is_found); -// new_entry = is_found ? &iter->second : nullptr; - } - //old_node_entry = new_node_entry; - - if (new_entry != nullptr && n_diverging_bits == 0) { - // keys are equal ... - return count_fn(new_entry->GetValue()); - } - - // Are we inserting in same node and same quadrant? - // This works only if the predicate has the same result for ALL entries. This can only - // be guaranteed if there is only one entry (or if we had proper TRUE/FALSE) predicates. - if (new_entry != nullptr && - new_node_entry->GetNodePostfixLen() >= n_diverging_bits && - new_entry->GetValue().size() == 1) { - // Technically this should be: (old_entry->GetValue().size() == 1), but that's the same. - auto result = count_fn(new_entry->GetValue()); - if (result > 0) { - new_entry->SetKey(new_key); - } - return result; - } - - // Find node for insertion of new bucket - if (new_entry == nullptr) { - new_entry = &new_node_entry->GetNode().Emplace( - is_inserted, new_key, new_node_entry->GetNodePostfixLen()); - num_entries_ += is_inserted; - assert(new_entry != nullptr); - } - - auto* old_entry = old_node_entry; - while (old_entry && old_entry->IsNode()) { - old_node_entry = old_entry; - old_entry = old_entry->GetNode().Find(old_key, old_entry->GetNodePostfixLen()); - } - - size_t result; - if (old_entry == nullptr) { - // Does old_entry exist? - result = 0; // old_key not found or invalid! - } else if (n_diverging_bits == 0) { - // keys are equal ... - result = count_fn(old_entry->GetValue()); -// } else if ( -// old_node_entry->GetNodePostfixLen() >= n_diverging_bits && -// old_entry->GetValue().size() == 1) { -// // Are we inserting in same node and same quadrant? -// // This works only if the predicate has the same result for ALL entries. This can only -// // be guaranteed if there is only one entry (or if we had proper TRUE/FALSE) predicates. -// result = count_fn(old_entry->GetValue()); -// if (result > 0) { -// old_entry->SetKey(new_key); -// } - } else { - // vanilla relocate - result = relocate_fn(old_entry->GetValue(), new_entry->GetValue()); - } - - if (old_entry != nullptr && old_entry->GetValue().empty()) { - bool found = false; - old_node_entry->GetNode().Erase( - old_key, old_node_entry, old_node_entry != &root_, found); - num_entries_ -= found; - } else if (new_entry->GetValue().empty()) { - bool found = false; - // new_node_entry may not be the immediate parent because Node::emplace() may create - // subnodes. - while (new_node_entry != nullptr && new_node_entry->IsNode()) { - new_node_entry = new_node_entry->GetNode().Erase( - new_key, new_node_entry, new_node_entry != &root_, found); - } - num_entries_ -= found; - } - - return result; - } - - inline static ulong N{0}; - inline static ulong X1{0}; - inline static ulong X2{0}; - inline static ulong X3{0}; - inline static ulong X4{0}; - inline static ulong X5{0}; - inline static ulong X6{0}; - inline static ulong X7{0}; - inline static ulong PREV_SIZE{0}; - - inline static ulong IBOTH{0}; - inline static ulong INEW{0}; - inline static ulong IOLD{0}; - inline static ulong ICLEAN{0}; - - inline static ulong I1{0}; - inline static ulong I2{0}; - inline static ulong I3{0}; - inline static ulong I4{0}; - inline static ulong I5{0}; - inline static ulong I6{0}; - - void print() { - if (num_entries_ >= PREV_SIZE * 10 || num_entries_*4 < PREV_SIZE) { -// std::cout << "S=" << num_entries_ << " N=" << N << " X=" << X1 << " " << X2 << " " -// << X3 << " " << X4 << " " << X5 << " " << X6 << " " << X7 << " " -// << std::endl; -// std::cout << " both/n/o/c=" << IBOTH << " " << INEW << " " << IOLD << " " << ICLEAN -// << " I=" << I1 << " " << I2 << " " << I3 << " " << I4 << " " << I5 << " " -// << std::endl; - PREV_SIZE = num_entries_; - N = 0; - X1 = 0; - X2 = 0; - X3 = 0; - X4 = 0; - X5 = 0; - X6 = 0; - X7 = 0; - IBOTH = 0; - ICLEAN = 0; - INEW = 0; - IOLD = 0; - I1 = 0; - I2 = 0; - I3 = 0; - I4 = 0; - I5 = 0; - } - - ++N; - if (N % 200000 == 0 && num_entries_ > 200000) { - auto tot = I1 + I2 + I3 + I4 + I5; - std::cout << "N=" << N << " X=" << X1 << " " << X2 << " " << X3 << " " << X4 << " " - << X5 << " " << X6 << " " << X7 << " " << std::endl; - std::cout << " both/n/o/c=" << IBOTH << " " << INEW << " " << IOLD << " " << ICLEAN - << " I=" << I1 << " " << I2 << " " << I3 << " " << I4 << " " << I5 - << " = " << tot << std::endl; - } - } - template size_t _relocate_mm( const KeyT& old_key, @@ -720,25 +477,19 @@ class PhTreeV16 { bit_width_t n_diverging_bits = NumberOfDivergingBits(old_key, new_key); if (!verify_exists && n_diverging_bits == 0) { - return 1; // We omit calling because that would require looking up the entry... + return 1; // We omit calling COUNT because that would require looking up the entry... } - EntryT* current_entry = &root_; // An entry. - EntryT* old_node_entry = nullptr; // Node that contains entry to be removed - EntryT* old_node_entry_parent = nullptr; // Parent of the old_node_entry - EntryT* new_node_entry = nullptr; // Node that will contain new entry + EntryT* current_entry = &root_; // An entry. + EntryT* old_node_entry = nullptr; // Node that contains entry to be removed + EntryT* new_node_entry = nullptr; // Node that will contain the new entry // Find node or entry for removal while (current_entry && current_entry->IsNode()) { - old_node_entry_parent = old_node_entry; old_node_entry = current_entry; auto postfix_len = old_node_entry->GetNodePostfixLen(); if (postfix_len + 1 >= n_diverging_bits) { new_node_entry = old_node_entry; } - // TODO stop earlier, we are going to have to redo this after insert.... -// bool is_found = false; -// iter = current_entry->GetNode().LowerBound(old_key, postfix_len, is_found); -// current_entry = is_found ? &iter->second : nullptr; current_entry = current_entry->GetNode().Find(old_key, postfix_len); } EntryT* old_entry = current_entry; // Entry to be removed @@ -763,43 +514,33 @@ class PhTreeV16 { } } - bool same_node = old_node_entry == new_node_entry; - // Find node for insertion auto new_entry = new_node_entry; + bool same_node = old_node_entry == new_node_entry; bool is_inserted = false; while (new_entry && new_entry->IsNode()) { - //is_found = false; // TODO fix for non-MM?!?! new_node_entry = new_entry; is_inserted = false; new_entry = &new_entry->GetNode().Emplace(is_inserted, new_key, new_entry->GetNodePostfixLen()); num_entries_ += is_inserted; } - if (is_inserted) { - // adjust old_entry if necessary - if (same_node) { - old_entry = old_node_entry; - while (old_entry && old_entry->IsNode()) { - ++I3; - ++IOLD; - old_node_entry = old_entry; - old_entry = old_entry->GetNode().Find(old_key, old_entry->GetNodePostfixLen()); - } + + // Adjust old_entry if necessary, it may have been invalidated by emplace() in the same node + if (is_inserted && same_node) { + old_entry = old_node_entry; + while (old_entry && old_entry->IsNode()) { + old_node_entry = old_entry; + old_entry = old_entry->GetNode().Find(old_key, old_entry->GetNodePostfixLen()); } } - // vanilla relocate + // relocate auto result = relocate_fn(old_entry->GetValue(), new_entry->GetValue()); - // Erase old value. See comments in try_emplace(iterator) for details. - if (old_node_entry_parent == new_node_entry) { - // In this case the old_node_entry may have been invalidated by the previous - // insertion. - old_node_entry = old_node_entry_parent; + if (result == 0) { + clean_up(new_key, new_entry, new_node_entry); } - - clean_up(new_key, new_entry, new_node_entry); clean_up(old_key, old_entry, old_node_entry); return result; } @@ -807,11 +548,9 @@ class PhTreeV16 { private: void clean_up(const KeyT& key, EntryT* entry, EntryT* node_entry) { // It may happen that node_entry is not the immediate parent, but that is okay! - // TODO use iterators? if (entry != nullptr && entry->GetValue().empty()) { - bool found = false; // TODO fix in non-MM + bool found = false; while (node_entry != nullptr && node_entry->IsNode()) { - ++ICLEAN; found = false; node_entry = node_entry->GetNode().Erase(key, node_entry, node_entry != &root_, found); From ab9ad8ab4a67835d1635d517daf93c1e03c61979 Mon Sep 17 00:00:00 2001 From: tzaeschke Date: Fri, 16 Dec 2022 19:30:27 +0100 Subject: [PATCH 4/6] initial --- include/phtree/v16/phtree_v16.h | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/include/phtree/v16/phtree_v16.h b/include/phtree/v16/phtree_v16.h index e9e5ad6e..7483eefc 100644 --- a/include/phtree/v16/phtree_v16.h +++ b/include/phtree/v16/phtree_v16.h @@ -330,8 +330,6 @@ class PhTreeV16 { auto relocate_if(const KeyT& old_key, const KeyT& new_key, PRED&& pred) { bit_width_t n_diverging_bits = NumberOfDivergingBits(old_key, new_key); - // EntryIterator iter = root_.GetNode().End(); - auto iter = root_.GetNode().End(); EntryT* current_entry = &root_; // An entry. EntryT* old_node_entry = nullptr; // Node that contains entry to be removed EntryT* old_node_entry_parent = nullptr; // Parent of the old_node_entry @@ -345,9 +343,7 @@ class PhTreeV16 { new_node_entry = old_node_entry; } // TODO stop earlier, we are going to have to redo this after insert.... - bool is_found = false; - iter = current_entry->GetNode().LowerBound(old_key, postfix_len, is_found); - current_entry = is_found ? &iter->second : nullptr; + current_entry = current_entry->GetNode().Find(old_key, postfix_len); } EntryT* old_entry = current_entry; // Entry to be removed @@ -364,19 +360,15 @@ class PhTreeV16 { // Find node for insertion auto new_entry = new_node_entry; - bool is_found = false; while (new_entry && new_entry->IsNode()) { new_node_entry = new_entry; - iter = - new_entry->GetNode().LowerBound(new_key, new_entry->GetNodePostfixLen(), is_found); - new_entry = is_found ? &iter->second : nullptr; + new_entry = new_entry->GetNode().Find(new_key, new_entry->GetNodePostfixLen()); } - if (is_found) { + if (new_entry != nullptr) { return 0; // Entry exists } bool is_inserted = false; new_entry = &new_node_entry->GetNode().Emplace( - iter, is_inserted, new_key, new_node_entry->GetNodePostfixLen(), @@ -389,7 +381,7 @@ class PhTreeV16 { old_node_entry = old_node_entry_parent; } - is_found = false; + bool is_found = false; // TODO use in-node iterator if possible while (old_node_entry) { old_node_entry = old_node_entry->GetNode().Erase( From 73eff80a758859a6d5e016b97483a80a0966f42a Mon Sep 17 00:00:00 2001 From: tzaeschke Date: Fri, 16 Dec 2022 20:08:32 +0100 Subject: [PATCH 5/6] initial --- include/phtree/v16/phtree_v16.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/phtree/v16/phtree_v16.h b/include/phtree/v16/phtree_v16.h index 7483eefc..4030b738 100644 --- a/include/phtree/v16/phtree_v16.h +++ b/include/phtree/v16/phtree_v16.h @@ -353,7 +353,10 @@ class PhTreeV16 { } // Are the keys equal? Or is the quadrant the same? -> same entry - if (n_diverging_bits == 0 || old_node_entry->GetNodePostfixLen() >= n_diverging_bits) { + if (n_diverging_bits == 0) { + return 1; + } + if (old_node_entry->GetNodePostfixLen() >= n_diverging_bits) { old_entry->SetKey(new_key); return 1; } @@ -382,7 +385,6 @@ class PhTreeV16 { } bool is_found = false; - // TODO use in-node iterator if possible while (old_node_entry) { old_node_entry = old_node_entry->GetNode().Erase( old_key, old_node_entry, old_node_entry != &root_, is_found); From 67a74f68832e2068e2ac0bba9626cf76d492d2cb Mon Sep 17 00:00:00 2001 From: tzaeschke Date: Fri, 16 Dec 2022 20:13:14 +0100 Subject: [PATCH 6/6] initial --- CHANGELOG.md | 3 ++- include/phtree/v16/phtree_v16.h | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15334446..e7ea9ffd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Rewrote relocate(). This should be much cleaner now and slightly faster. [#98](https://github.com/tzaeschke/phtree-cpp/pull/98), [#99](https://github.com/tzaeschke/phtree-cpp/pull/99), - [#101](https://github.com/tzaeschke/phtree-cpp/pull/101) + [#101](https://github.com/tzaeschke/phtree-cpp/pull/101), + [#104](https://github.com/tzaeschke/phtree-cpp/pull/104) - Cleaned up HandleCollision() and key comparison functions. [#97](https://github.com/tzaeschke/phtree-cpp/pull/97) - Improved performance by eliminating memory indirection for DIM > 3. This was enabled by referencing "Node" directly in "Entry" which was enabled by diff --git a/include/phtree/v16/phtree_v16.h b/include/phtree/v16/phtree_v16.h index 4030b738..6db28d41 100644 --- a/include/phtree/v16/phtree_v16.h +++ b/include/phtree/v16/phtree_v16.h @@ -26,7 +26,6 @@ #include "iterator_knn_hs.h" #include "iterator_with_parent.h" #include "node.h" -#include namespace improbable::phtree::v16 {