Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BTreeMap::entry: Avoid allocating if no insertion #92962

Merged
merged 1 commit into from
Mar 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 18 additions & 17 deletions library/alloc/src/collections/btree/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ impl<K: Clone, V: Clone> Clone for BTreeMap<K, V> {
let mut out_tree = clone_subtree(internal.first_edge().descend());

{
let out_root = BTreeMap::ensure_is_owned(&mut out_tree.root);
let out_root = out_tree.root.as_mut().unwrap();
let mut out_node = out_root.push_internal_level();
let mut in_edge = internal.first_edge();
while let Ok(kv) = in_edge.right_kv() {
Expand Down Expand Up @@ -278,11 +278,12 @@ where

fn replace(&mut self, key: K) -> Option<K> {
let (map, dormant_map) = DormantMutRef::new(self);
let root_node = Self::ensure_is_owned(&mut map.root).borrow_mut();
let root_node = map.root.get_or_insert_with(Root::new).borrow_mut();
match root_node.search_tree::<K>(&key) {
Found(mut kv) => Some(mem::replace(kv.key_mut(), key)),
GoDown(handle) => {
VacantEntry { key, handle, dormant_map, _marker: PhantomData }.insert(());
VacantEntry { key, handle: Some(handle), dormant_map, _marker: PhantomData }
.insert(());
None
}
}
Expand Down Expand Up @@ -1032,7 +1033,7 @@ impl<K, V> BTreeMap<K, V> {

let self_iter = mem::take(self).into_iter();
let other_iter = mem::take(other).into_iter();
let root = BTreeMap::ensure_is_owned(&mut self.root);
let root = self.root.get_or_insert_with(Root::new);
root.append_from_sorted_iters(self_iter, other_iter, &mut self.length)
}

Expand Down Expand Up @@ -1144,14 +1145,20 @@ impl<K, V> BTreeMap<K, V> {
where
K: Ord,
{
// FIXME(@porglezomp) Avoid allocating if we don't insert
let (map, dormant_map) = DormantMutRef::new(self);
let root_node = Self::ensure_is_owned(&mut map.root).borrow_mut();
match root_node.search_tree(&key) {
Found(handle) => Occupied(OccupiedEntry { handle, dormant_map, _marker: PhantomData }),
GoDown(handle) => {
Vacant(VacantEntry { key, handle, dormant_map, _marker: PhantomData })
}
match map.root {
None => Vacant(VacantEntry { key, handle: None, dormant_map, _marker: PhantomData }),
Some(ref mut root) => match root.borrow_mut().search_tree(&key) {
Found(handle) => {
Occupied(OccupiedEntry { handle, dormant_map, _marker: PhantomData })
}
GoDown(handle) => Vacant(VacantEntry {
key,
handle: Some(handle),
dormant_map,
_marker: PhantomData,
}),
},
}
}

Expand Down Expand Up @@ -2247,12 +2254,6 @@ impl<K, V> BTreeMap<K, V> {
pub const fn is_empty(&self) -> bool {
self.len() == 0
}

/// If the root node is the empty (non-allocated) root node, allocate our
/// own node. Is an associated function to avoid borrowing the entire BTreeMap.
fn ensure_is_owned(root: &mut Option<Root<K, V>>) -> &mut Root<K, V> {
root.get_or_insert_with(Root::new)
}
}

#[cfg(test)]
Expand Down
38 changes: 25 additions & 13 deletions library/alloc/src/collections/btree/map/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ impl<K: Debug + Ord, V: Debug> Debug for Entry<'_, K, V> {
#[stable(feature = "rust1", since = "1.0.0")]
pub struct VacantEntry<'a, K: 'a, V: 'a> {
pub(super) key: K,
pub(super) handle: Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>,
/// `None` for a (empty) map without root
pub(super) handle: Option<Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>>,
pub(super) dormant_map: DormantMutRef<'a, BTreeMap<K, V>>,

// Be invariant in `K` and `V`
Expand Down Expand Up @@ -312,22 +313,33 @@ impl<'a, K: Ord, V> VacantEntry<'a, K, V> {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn insert(self, value: V) -> &'a mut V {
let out_ptr = match self.handle.insert_recursing(self.key, value) {
(None, val_ptr) => {
// SAFETY: We have consumed self.handle and the handle returned.
let map = unsafe { self.dormant_map.awaken() };
map.length += 1;
val_ptr
}
(Some(ins), val_ptr) => {
drop(ins.left);
let out_ptr = match self.handle {
None => {
// SAFETY: We have consumed self.handle and the reference returned.
let map = unsafe { self.dormant_map.awaken() };
let root = map.root.as_mut().unwrap();
root.push_internal_level().push(ins.kv.0, ins.kv.1, ins.right);
map.length += 1;
let mut root = NodeRef::new_leaf();
let val_ptr = root.borrow_mut().push(self.key, value) as *mut V;
map.root = Some(root.forget_type());
map.length = 1;
val_ptr
}
Some(handle) => match handle.insert_recursing(self.key, value) {
(None, val_ptr) => {
// SAFETY: We have consumed self.handle and the handle returned.
let map = unsafe { self.dormant_map.awaken() };
map.length += 1;
val_ptr
}
(Some(ins), val_ptr) => {
drop(ins.left);
// SAFETY: We have consumed self.handle and the reference returned.
let map = unsafe { self.dormant_map.awaken() };
let root = map.root.as_mut().unwrap();
root.push_internal_level().push(ins.kv.0, ins.kv.1, ins.right);
map.length += 1;
val_ptr
}
},
};
// Now that we have finished growing the tree using borrowed references,
// dereference the pointer to a part of it, that we picked up along the way.
Expand Down
45 changes: 38 additions & 7 deletions library/alloc/src/collections/btree/map/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ impl<K, V> BTreeMap<K, V> {
K: Ord,
{
let iter = mem::take(self).into_iter();
let root = BTreeMap::ensure_is_owned(&mut self.root);
root.bulk_push(iter, &mut self.length);
if !iter.is_empty() {
self.root.insert(Root::new()).bulk_push(iter, &mut self.length);
}
}
}

Expand Down Expand Up @@ -914,7 +915,7 @@ mod test_drain_filter {
fn empty() {
let mut map: BTreeMap<i32, i32> = BTreeMap::new();
map.drain_filter(|_, _| unreachable!("there's nothing to decide on"));
assert!(map.is_empty());
assert_eq!(map.height(), None);
map.check();
}

Expand Down Expand Up @@ -1410,7 +1411,7 @@ fn test_clear() {
assert_eq!(map.len(), len);
map.clear();
map.check();
assert!(map.is_empty());
assert_eq!(map.height(), None);
}
}

Expand Down Expand Up @@ -1789,7 +1790,7 @@ fn test_occupied_entry_key() {
let mut a = BTreeMap::new();
let key = "hello there";
let value = "value goes here";
assert!(a.is_empty());
assert_eq!(a.height(), None);
a.insert(key, value);
assert_eq!(a.len(), 1);
assert_eq!(a[key], value);
Expand All @@ -1809,9 +1810,9 @@ fn test_vacant_entry_key() {
let key = "hello there";
let value = "value goes here";

assert!(a.is_empty());
assert_eq!(a.height(), None);
match a.entry(key) {
Occupied(_) => panic!(),
Occupied(_) => unreachable!(),
Vacant(e) => {
assert_eq!(key, *e.key());
e.insert(value);
Expand All @@ -1822,6 +1823,36 @@ fn test_vacant_entry_key() {
a.check();
}

#[test]
fn test_vacant_entry_no_insert() {
frank-king marked this conversation as resolved.
Show resolved Hide resolved
let mut a = BTreeMap::<&str, ()>::new();
let key = "hello there";

// Non-allocated
assert_eq!(a.height(), None);
match a.entry(key) {
Occupied(_) => unreachable!(),
Vacant(e) => assert_eq!(key, *e.key()),
}
// Ensures the tree has no root.
assert_eq!(a.height(), None);
a.check();

// Allocated but still empty
a.insert(key, ());
a.remove(&key);
assert_eq!(a.height(), Some(0));
assert!(a.is_empty());
match a.entry(key) {
Occupied(_) => unreachable!(),
Vacant(e) => assert_eq!(key, *e.key()),
}
// Ensures the allocated root is not changed.
assert_eq!(a.height(), Some(0));
assert!(a.is_empty());
a.check();
}

#[test]
fn test_first_last_entry() {
let mut a = BTreeMap::new();
Expand Down
9 changes: 5 additions & 4 deletions library/alloc/src/collections/btree/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ unsafe impl<K: Send, V: Send, Type> Send for NodeRef<marker::Owned, K, V, Type>
unsafe impl<K: Send, V: Send, Type> Send for NodeRef<marker::Dying, K, V, Type> {}

impl<K, V> NodeRef<marker::Owned, K, V, marker::Leaf> {
fn new_leaf() -> Self {
pub fn new_leaf() -> Self {
Self::from_new_leaf(LeafNode::new())
}

Expand Down Expand Up @@ -619,15 +619,16 @@ impl<K, V, Type> NodeRef<marker::Owned, K, V, Type> {
}

impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
/// Adds a key-value pair to the end of the node.
pub fn push(&mut self, key: K, val: V) {
/// Adds a key-value pair to the end of the node, and returns
/// the mutable reference of the inserted value.
pub fn push(&mut self, key: K, val: V) -> &mut V {
let len = self.len_mut();
let idx = usize::from(*len);
assert!(idx < CAPACITY);
*len += 1;
unsafe {
self.key_area_mut(idx).write(key);
self.val_area_mut(idx).write(val);
self.val_area_mut(idx).write(val)
}
}
}
Expand Down