Skip to content

Commit 81dc88f

Browse files
committed
Auto merge of rust-lang#75105 - ssomers:btree_respect_min_len_hard, r=Mark-Simulacrum
Hard way to respect BTreeMap's minimum node length Resolves rust-lang#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.
2 parents 5e3f1b1 + 17ab457 commit 81dc88f

File tree

1 file changed

+65
-16
lines changed
  • library/alloc/src/collections/btree

1 file changed

+65
-16
lines changed

library/alloc/src/collections/btree/node.rs

+65-16
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,53 @@ impl<BorrowType, K, V, NodeType> Handle<NodeRef<BorrowType, K, V, NodeType>, mar
821821
}
822822
}
823823

824+
enum InsertionPlace {
825+
Left(usize),
826+
Right(usize),
827+
}
828+
829+
/// Given an edge index where we want to insert into a node filled to capacity,
830+
/// computes a sensible KV index of a split point and where to perform the insertion.
831+
/// The goal of the split point is for its key and value to end up in a parent node;
832+
/// the keys, values and edges to the left of the split point become the left child;
833+
/// the keys, values and edges to the right of the split point become the right child.
834+
fn splitpoint(edge_idx: usize) -> (usize, InsertionPlace) {
835+
debug_assert!(edge_idx <= CAPACITY);
836+
// Rust issue #74834 tries to explain these symmetric rules.
837+
let middle_kv_idx;
838+
let insertion;
839+
if edge_idx <= B - 2 {
840+
middle_kv_idx = B - 2;
841+
insertion = InsertionPlace::Left(edge_idx);
842+
} else if edge_idx == B - 1 {
843+
middle_kv_idx = B - 1;
844+
insertion = InsertionPlace::Left(edge_idx);
845+
} else if edge_idx == B {
846+
middle_kv_idx = B - 1;
847+
insertion = InsertionPlace::Right(0);
848+
} else {
849+
middle_kv_idx = B;
850+
let new_edge_idx = edge_idx - (B + 1);
851+
insertion = InsertionPlace::Right(new_edge_idx);
852+
}
853+
let mut left_len = middle_kv_idx;
854+
let mut right_len = CAPACITY - middle_kv_idx - 1;
855+
match insertion {
856+
InsertionPlace::Left(edge_idx) => {
857+
debug_assert!(edge_idx <= left_len);
858+
left_len += 1;
859+
}
860+
InsertionPlace::Right(edge_idx) => {
861+
debug_assert!(edge_idx <= right_len);
862+
right_len += 1;
863+
}
864+
}
865+
debug_assert!(left_len >= MIN_LEN);
866+
debug_assert!(right_len >= MIN_LEN);
867+
debug_assert!(left_len + right_len == CAPACITY);
868+
(middle_kv_idx, insertion)
869+
}
870+
824871
impl<'a, K, V, NodeType> Handle<NodeRef<marker::Mut<'a>, K, V, NodeType>, marker::Edge> {
825872
/// Helps implementations of `insert_fit` for a particular `NodeType`,
826873
/// 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
863910
let kv = unsafe { Handle::new_kv(self.node, self.idx) };
864911
(InsertResult::Fit(kv), ptr)
865912
} else {
866-
let middle = unsafe { Handle::new_kv(self.node, B) };
913+
let (middle_kv_idx, insertion) = splitpoint(self.idx);
914+
let middle = unsafe { Handle::new_kv(self.node, middle_kv_idx) };
867915
let (mut left, k, v, mut right) = middle.split();
868-
let ptr = if self.idx <= B {
869-
unsafe { Handle::new_edge(left.reborrow_mut(), self.idx).insert_fit(key, val) }
870-
} else {
871-
unsafe {
916+
let ptr = match insertion {
917+
InsertionPlace::Left(insert_idx) => unsafe {
918+
Handle::new_edge(left.reborrow_mut(), insert_idx).insert_fit(key, val)
919+
},
920+
InsertionPlace::Right(insert_idx) => unsafe {
872921
Handle::new_edge(
873922
right.node_as_mut().cast_unchecked::<marker::Leaf>(),
874-
self.idx - (B + 1),
923+
insert_idx,
875924
)
876925
.insert_fit(key, val)
877-
}
926+
},
878927
};
879928
(InsertResult::Split(SplitResult { left: left.forget_type(), k, v, right }), ptr)
880929
}
@@ -936,20 +985,20 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::
936985
let kv = unsafe { Handle::new_kv(self.node, self.idx) };
937986
InsertResult::Fit(kv)
938987
} else {
939-
let middle = unsafe { Handle::new_kv(self.node, B) };
988+
let (middle_kv_idx, insertion) = splitpoint(self.idx);
989+
let middle = unsafe { Handle::new_kv(self.node, middle_kv_idx) };
940990
let (mut left, k, v, mut right) = middle.split();
941-
if self.idx <= B {
942-
unsafe {
943-
Handle::new_edge(left.reborrow_mut(), self.idx).insert_fit(key, val, edge);
944-
}
945-
} else {
946-
unsafe {
991+
match insertion {
992+
InsertionPlace::Left(insert_idx) => unsafe {
993+
Handle::new_edge(left.reborrow_mut(), insert_idx).insert_fit(key, val, edge);
994+
},
995+
InsertionPlace::Right(insert_idx) => unsafe {
947996
Handle::new_edge(
948997
right.node_as_mut().cast_unchecked::<marker::Internal>(),
949-
self.idx - (B + 1),
998+
insert_idx,
950999
)
9511000
.insert_fit(key, val, edge);
952-
}
1001+
},
9531002
}
9541003
InsertResult::Split(SplitResult { left: left.forget_type(), k, v, right })
9551004
}

0 commit comments

Comments
 (0)