diff options
| author | bors <bors@rust-lang.org> | 2020-08-13 21:36:02 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2020-08-13 21:36:02 +0000 |
| commit | 81dc88f88f92ba8ad7465f9cba10c12d3a7b70f1 (patch) | |
| tree | a9ef432b92933100f46efe74c1f07900582ada9f | |
| parent | 5e3f1b148db5bfa27fee52464ae1f5d34c49d77b (diff) | |
| parent | 17ab457f21700caad62c3f3fcfeff48f21d2393b (diff) | |
| download | rust-81dc88f88f92ba8ad7465f9cba10c12d3a7b70f1.tar.gz rust-81dc88f88f92ba8ad7465f9cba10c12d3a7b70f1.zip | |
Auto merge of #75105 - ssomers:btree_respect_min_len_hard, r=Mark-Simulacrum
Hard way to respect BTreeMap's minimum node length Resolves #74834 the hard way (though not the hardest imaginable). Benchmarks (which are all biased/realistic, inserting keys in ascending order) say: ``` benchcmp r0 r1 --threshold 10 name r0 ns/iter r1 ns/iter diff ns/iter diff % speedup btree::map::clone_slim_100_and_clear 2,183 2,723 540 24.74% x 0.80 btree::map::clone_slim_100_and_drain_all 3,652 4,173 521 14.27% x 0.88 btree::map::clone_slim_100_and_drain_half 3,320 3,940 620 18.67% x 0.84 btree::map::clone_slim_100_and_into_iter 2,154 2,717 563 26.14% x 0.79 btree::map::clone_slim_100_and_pop_all 3,372 3,870 498 14.77% x 0.87 btree::map::clone_slim_100_and_remove_all 5,111 5,647 536 10.49% x 0.91 btree::map::clone_slim_100_and_remove_half 3,259 3,821 562 17.24% x 0.85 btree::map::iter_0 1,733 1,509 -224 -12.93% x 1.15 btree::map::iter_100 2,714 3,739 1,025 37.77% x 0.73 btree::map::iter_10k 3,728 4,269 541 14.51% x 0.87 btree::map::range_unbounded_unbounded 28,426 36,631 8,205 28.86% x 0.78 btree::map::range_unbounded_vs_iter 28,808 34,056 5,248 18.22% x 0.85 ``` This difference is not caused by the `debug_assert`-related code in the function `splitpoint`, it's the same without.
| -rw-r--r-- | library/alloc/src/collections/btree/node.rs | 81 |
1 files changed, 65 insertions, 16 deletions
diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index f70869148d5..aa959c667ac 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -821,6 +821,53 @@ impl<BorrowType, K, V, NodeType> Handle<NodeRef<BorrowType, K, V, NodeType>, mar } } +enum InsertionPlace { + Left(usize), + Right(usize), +} + +/// Given an edge index where we want to insert into a node filled to capacity, +/// computes a sensible KV index of a split point and where to perform the insertion. +/// The goal of the split point is for its key and value to end up in a parent node; +/// the keys, values and edges to the left of the split point become the left child; +/// the keys, values and edges to the right of the split point become the right child. +fn splitpoint(edge_idx: usize) -> (usize, InsertionPlace) { + debug_assert!(edge_idx <= CAPACITY); + // Rust issue #74834 tries to explain these symmetric rules. + let middle_kv_idx; + let insertion; + if edge_idx <= B - 2 { + middle_kv_idx = B - 2; + insertion = InsertionPlace::Left(edge_idx); + } else if edge_idx == B - 1 { + middle_kv_idx = B - 1; + insertion = InsertionPlace::Left(edge_idx); + } else if edge_idx == B { + middle_kv_idx = B - 1; + insertion = InsertionPlace::Right(0); + } else { + middle_kv_idx = B; + let new_edge_idx = edge_idx - (B + 1); + insertion = InsertionPlace::Right(new_edge_idx); + } + let mut left_len = middle_kv_idx; + let mut right_len = CAPACITY - middle_kv_idx - 1; + match insertion { + InsertionPlace::Left(edge_idx) => { + debug_assert!(edge_idx <= left_len); + left_len += 1; + } + InsertionPlace::Right(edge_idx) => { + debug_assert!(edge_idx <= right_len); + right_len += 1; + } + } + debug_assert!(left_len >= MIN_LEN); + debug_assert!(right_len >= MIN_LEN); + debug_assert!(left_len + right_len == CAPACITY); + (middle_kv_idx, insertion) +} + impl<'a, K, V, NodeType> Handle<NodeRef<marker::Mut<'a>, K, V, NodeType>, marker::Edge> { /// Helps implementations of `insert_fit` for a particular `NodeType`, /// by taking care of leaf data. @@ -863,18 +910,20 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge let kv = unsafe { Handle::new_kv(self.node, self.idx) }; (InsertResult::Fit(kv), ptr) } else { - let middle = unsafe { Handle::new_kv(self.node, B) }; + let (middle_kv_idx, insertion) = splitpoint(self.idx); + let middle = unsafe { Handle::new_kv(self.node, middle_kv_idx) }; let (mut left, k, v, mut right) = middle.split(); - let ptr = if self.idx <= B { - unsafe { Handle::new_edge(left.reborrow_mut(), self.idx).insert_fit(key, val) } - } else { - unsafe { + let ptr = match insertion { + InsertionPlace::Left(insert_idx) => unsafe { + Handle::new_edge(left.reborrow_mut(), insert_idx).insert_fit(key, val) + }, + InsertionPlace::Right(insert_idx) => unsafe { Handle::new_edge( right.node_as_mut().cast_unchecked::<marker::Leaf>(), - self.idx - (B + 1), + insert_idx, ) .insert_fit(key, val) - } + }, }; (InsertResult::Split(SplitResult { left: left.forget_type(), k, v, right }), ptr) } @@ -936,20 +985,20 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker:: let kv = unsafe { Handle::new_kv(self.node, self.idx) }; InsertResult::Fit(kv) } else { - let middle = unsafe { Handle::new_kv(self.node, B) }; + let (middle_kv_idx, insertion) = splitpoint(self.idx); + let middle = unsafe { Handle::new_kv(self.node, middle_kv_idx) }; let (mut left, k, v, mut right) = middle.split(); - if self.idx <= B { - unsafe { - Handle::new_edge(left.reborrow_mut(), self.idx).insert_fit(key, val, edge); - } - } else { - unsafe { + match insertion { + InsertionPlace::Left(insert_idx) => unsafe { + Handle::new_edge(left.reborrow_mut(), insert_idx).insert_fit(key, val, edge); + }, + InsertionPlace::Right(insert_idx) => unsafe { Handle::new_edge( right.node_as_mut().cast_unchecked::<marker::Internal>(), - self.idx - (B + 1), + insert_idx, ) .insert_fit(key, val, edge); - } + }, } InsertResult::Split(SplitResult { left: left.forget_type(), k, v, right }) } |
