Skip to content

Commit ecb99a9

Browse files
committed
Optimize BTreeMap::append() using CursorMut
1 parent 3f98535 commit ecb99a9

File tree

3 files changed

+77
-61
lines changed

3 files changed

+77
-61
lines changed

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

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,8 @@
11
use core::alloc::Allocator;
2-
use core::iter::FusedIterator;
32

4-
use super::merge_iter::MergeIterInner;
53
use super::node::{self, Root};
64

75
impl<K, V> Root<K, V> {
8-
/// Appends all key-value pairs from the union of two ascending iterators,
9-
/// incrementing a `length` variable along the way. The latter makes it
10-
/// easier for the caller to avoid a leak when a drop handler panicks.
11-
///
12-
/// If both iterators produce the same key, this method drops the pair from
13-
/// the left iterator and appends the pair from the right iterator.
14-
///
15-
/// If you want the tree to end up in a strictly ascending order, like for
16-
/// a `BTreeMap`, both iterators should produce keys in strictly ascending
17-
/// order, each greater than all keys in the tree, including any keys
18-
/// already in the tree upon entry.
19-
pub(super) fn append_from_sorted_iters<I, A: Allocator + Clone>(
20-
&mut self,
21-
left: I,
22-
right: I,
23-
length: &mut usize,
24-
alloc: A,
25-
) where
26-
K: Ord,
27-
I: Iterator<Item = (K, V)> + FusedIterator,
28-
{
29-
// We prepare to merge `left` and `right` into a sorted sequence in linear time.
30-
let iter = MergeIter(MergeIterInner::new(left, right));
31-
32-
// Meanwhile, we build a tree from the sorted sequence in linear time.
33-
self.bulk_push(iter, length, alloc)
34-
}
35-
366
/// Pushes all key-value pairs to the end of the tree, incrementing a
377
/// `length` variable along the way. The latter makes it easier for the
388
/// caller to avoid a leak when the iterator panicks.
@@ -94,24 +64,3 @@ impl<K, V> Root<K, V> {
9464
self.fix_right_border_of_plentiful();
9565
}
9666
}
97-
98-
// An iterator for merging two sorted sequences into one
99-
struct MergeIter<K, V, I: Iterator<Item = (K, V)>>(MergeIterInner<I>);
100-
101-
impl<K: Ord, V, I> Iterator for MergeIter<K, V, I>
102-
where
103-
I: Iterator<Item = (K, V)> + FusedIterator,
104-
{
105-
type Item = (K, V);
106-
107-
/// If two keys are equal, returns the key from the left and the value from the right.
108-
fn next(&mut self) -> Option<(K, V)> {
109-
let (a_next, b_next) = self.0.nexts(|a: &(K, V), b: &(K, V)| K::cmp(&a.0, &b.0));
110-
match (a_next, b_next) {
111-
(Some((a_k, _)), Some((_, b_v))) => Some((a_k, b_v)),
112-
(Some(a), None) => Some(a),
113-
(None, Some(b)) => Some(b),
114-
(None, None) => None,
115-
}
116-
}
117-
}

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

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,15 +1229,82 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
12291229
return;
12301230
}
12311231

1232-
let self_iter = mem::replace(self, Self::new_in((*self.alloc).clone())).into_iter();
1233-
let other_iter = mem::replace(other, Self::new_in((*self.alloc).clone())).into_iter();
1234-
let root = self.root.get_or_insert_with(|| Root::new((*self.alloc).clone()));
1235-
root.append_from_sorted_iters(
1236-
self_iter,
1237-
other_iter,
1238-
&mut self.length,
1239-
(*self.alloc).clone(),
1240-
)
1232+
// Because `other` takes a &mut Self, in order for us to own
1233+
// the K,V pairs, we need to use mem::replace
1234+
let mut other_iter = mem::replace(other, Self::new_in((*self.alloc).clone())).into_iter();
1235+
let (first_other_key, first_other_val) = other_iter.next().unwrap();
1236+
1237+
// find the first gap that has the smallest key greater than or equal to
1238+
// the first key from other
1239+
let mut self_cursor = self.lower_bound_mut(Bound::Included(&first_other_key));
1240+
1241+
if let Some((self_key, _)) = self_cursor.peek_next() {
1242+
match K::cmp(self_key, &first_other_key) {
1243+
Ordering::Equal => {
1244+
if let Some((_, v)) = self_cursor.next() {
1245+
*v = first_other_val;
1246+
}
1247+
}
1248+
Ordering::Greater =>
1249+
// SAFETY: we know our other_key's ordering is less than self_key,
1250+
// so inserting before will guarantee sorted order
1251+
unsafe {
1252+
self_cursor.insert_before_unchecked(first_other_key, first_other_val);
1253+
},
1254+
Ordering::Less => {
1255+
unreachable!("Cursor's peek_next should return None.");
1256+
}
1257+
}
1258+
} else {
1259+
// SAFETY: reaching here means our cursor is at the end
1260+
// self BTreeMap so we just insert other_key here
1261+
unsafe {
1262+
self_cursor.insert_before_unchecked(first_other_key, first_other_val);
1263+
}
1264+
}
1265+
1266+
for (other_key, other_val) in other_iter {
1267+
loop {
1268+
if let Some((self_key, _)) = self_cursor.peek_next() {
1269+
match K::cmp(self_key, &other_key) {
1270+
Ordering::Equal => {
1271+
if let Some((_, v)) = self_cursor.next() {
1272+
*v = other_val;
1273+
}
1274+
break;
1275+
}
1276+
Ordering::Greater => {
1277+
// SAFETY: we know our self_key's ordering is greater than other_key,
1278+
// so inserting before will guarantee sorted order
1279+
unsafe {
1280+
self_cursor.insert_before_unchecked(other_key, other_val);
1281+
}
1282+
break;
1283+
}
1284+
Ordering::Less => {
1285+
// FIXME: instead of doing a linear search here,
1286+
// this can be optimized to search the tree by starting
1287+
// from self_cursor and going towards the root and then
1288+
// back down to the proper node -- that should probably
1289+
// be a new method on Cursor*.
1290+
self_cursor.next();
1291+
}
1292+
}
1293+
} else {
1294+
// FIXME: If we get here, that means all of other's keys are greater than
1295+
// self's keys. For performance, this should really do a bulk insertion of items
1296+
// from other_iter into the end of self `BTreeMap`. Maybe this should be
1297+
// a method for Cursor*?
1298+
1299+
// SAFETY: reaching here means our cursor is at the end
1300+
// self BTreeMap so we just insert other_key here
1301+
unsafe {
1302+
self_cursor.insert_before_unchecked(other_key, other_val);
1303+
}
1304+
break;
1305+
}
1306+
}
1307+
}
12411308
}
12421309

12431310
/// Moves all elements from `other` into `self`, leaving `other` empty.

library/alloc/src/collections/btree/map/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2224,7 +2224,7 @@ fn test_append_drop_leak() {
22242224

22252225
catch_unwind(move || left.append(&mut right)).unwrap_err();
22262226
assert_eq!(a.dropped(), 1);
2227-
assert_eq!(b.dropped(), 1); // should be 2 were it not for Rust issue #47949
2227+
assert_eq!(b.dropped(), 2);
22282228
assert_eq!(c.dropped(), 2);
22292229
}
22302230

0 commit comments

Comments
 (0)