Skip to content

Add intrusive iterators to BTree. #19796

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

Closed
wants to merge 1 commit into from
Closed
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
93 changes: 93 additions & 0 deletions src/libcollections/btree/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,7 @@ impl<K, V, E, T: Traverse<E> + DoubleEndedIterator<TraversalItem<K, V, E>>>
// making these arbitrary sub-range iterators. However the logic to construct these paths
// efficiently is fairly involved, so this is a FIXME. The sub-range iterators also wouldn't be
// able to accurately predict size, so those iterators can't implement ExactSizeIterator.
#[inline]
fn next(&mut self) -> Option<(K, V)> {
loop {
// We want the smallest element, so try to get the top of the left stack
Expand Down Expand Up @@ -1030,6 +1031,7 @@ impl<K, V, E, T: Traverse<E> + DoubleEndedIterator<TraversalItem<K, V, E>>>
}

impl<'a, K, V> Iterator<(&'a K, &'a V)> for Entries<'a, K, V> {
#[inline]
fn next(&mut self) -> Option<(&'a K, &'a V)> { self.inner.next() }
fn size_hint(&self) -> (uint, Option<uint>) { self.inner.size_hint() }
}
Expand All @@ -1040,20 +1042,24 @@ impl<'a, K, V> ExactSizeIterator<(&'a K, &'a V)> for Entries<'a, K, V> {}


impl<'a, K, V> Iterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> {
#[inline]
fn next(&mut self) -> Option<(&'a K, &'a mut V)> { self.inner.next() }
fn size_hint(&self) -> (uint, Option<uint>) { self.inner.size_hint() }
}
impl<'a, K, V> DoubleEndedIterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> {
#[inline]
fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> { self.inner.next_back() }
}
impl<'a, K, V> ExactSizeIterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> {}


impl<K, V> Iterator<(K, V)> for MoveEntries<K, V> {
#[inline]
fn next(&mut self) -> Option<(K, V)> { self.inner.next() }
fn size_hint(&self) -> (uint, Option<uint>) { self.inner.size_hint() }
}
impl<K, V> DoubleEndedIterator<(K, V)> for MoveEntries<K, V> {
#[inline]
fn next_back(&mut self) -> Option<(K, V)> { self.inner.next_back() }
}
impl<K, V> ExactSizeIterator<(K, V)> for MoveEntries<K, V> {}
Expand Down Expand Up @@ -1130,6 +1136,25 @@ impl<K, V> BTreeMap<K, V> {
}
}

/// An intrusive version of `.iter()`. The closure will be called once with
/// every key/value pair in the tree.
///
/// This is faster than calling `.iter()`, but is far less composable.
#[inline]
#[experimental = "relies on unboxed closures"]
pub fn intrusive_iter<F: FnMut(&K, &V)>(&self, mut f: F) {
fn intrusive_iter_impl<K, V, F: FnMut(&K, &V)>(node: &Node<K, V>, f: &mut F) {
for ti in node.iter() {
match ti {
Elem(k, v) => (*f)(k, v),
Edge(e) => intrusive_iter_impl(e, f),
}
}
}

intrusive_iter_impl(&self.root, &mut f);
}

/// Gets a mutable iterator over the entries of the map.
#[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn iter_mut<'a>(&'a mut self) -> MutEntries<'a, K, V> {
Expand All @@ -1144,6 +1169,25 @@ impl<K, V> BTreeMap<K, V> {
}
}

/// An intrusive version of `.iter_mut()`. The closure will be called once
/// with every key/value pair in the tree.
///
/// This is faster than calling `.iter_mut()`, but is far less composable.
#[inline]
#[experimental = "relies on unboxed closures"]
pub fn intrusive_iter_mut<F: FnMut(&K, &mut V)>(&mut self, mut f: F) {
fn intrusive_iter_mut_impl<K, V, F: FnMut(&K, &mut V)>(node: &mut Node<K, V>, f: &mut F) {
for ti in node.iter_mut() {
match ti {
Elem(k, v) => (*f)(k, v),
Edge(e) => intrusive_iter_mut_impl(e, f),
}
}
}

intrusive_iter_mut_impl(&mut self.root, &mut f);
}

/// Gets an owning iterator over the entries of the map.
#[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn into_iter(self) -> MoveEntries<K, V> {
Expand All @@ -1158,6 +1202,25 @@ impl<K, V> BTreeMap<K, V> {
}
}

/// An intrusive version of `.into_iter()`. The closure will be called once
/// with every key/value pair in the tree.
///
/// This is faster than calling `.into_iter()`, but is far less composable.
#[inline]
#[experimental = "relies on unboxed closures"]
pub fn intrusive_into_iter<F: FnMut(K, V)>(self, mut f: F) {
fn intrusive_into_iter_impl<K, V, F: FnMut(K, V)>(node: Node<K, V>, f: &mut F) {
for ti in node.into_iter() {
match ti {
Elem(k, v) => (*f)(k, v),
Edge(e) => intrusive_into_iter_impl(e, f),
}
}
}

intrusive_into_iter_impl(self.root, &mut f);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you believe that the recursive version will be faster/more efficient than an explicit stack?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do, but only because the benchmarks as written agree.

}

/// Gets an iterator over the keys of the map.
///
/// # Examples
Expand Down Expand Up @@ -1580,4 +1643,34 @@ mod bench {
pub fn iter_100000(b: &mut Bencher) {
bench_iter(b, 100000);
}

fn bench_intrusive_iter(b: &mut Bencher, size: uint) {
let mut map = BTreeMap::<uint, uint>::new();
let mut rng = weak_rng();

for _ in range(0, size) {
map.insert(rng.gen(), rng.gen());
}

b.iter(|| {
map.intrusive_iter(|&mut: k, v| {
black_box(k); black_box(v);
});
});
}

#[bench]
pub fn intrusive_iter_20(b: &mut Bencher) {
bench_intrusive_iter(b, 20);
}

#[bench]
pub fn intrusive_iter_1000(b: &mut Bencher) {
bench_intrusive_iter(b, 1000);
}

#[bench]
pub fn intrusive_iter_100000(b: &mut Bencher) {
bench_intrusive_iter(b, 100000);
}
}
10 changes: 10 additions & 0 deletions src/libcollections/btree/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1334,10 +1334,14 @@ struct ElemsAndEdges<Elems, Edges>(Elems, Edges);
impl<K, V, E, Elems: DoubleEndedIterator<(K, V)>, Edges: DoubleEndedIterator<E>>
TraversalImpl<K, V, E> for ElemsAndEdges<Elems, Edges> {

#[inline]
fn next_kv(&mut self) -> Option<(K, V)> { self.0.next() }
#[inline]
fn next_kv_back(&mut self) -> Option<(K, V)> { self.0.next_back() }

#[inline]
fn next_edge(&mut self) -> Option<E> { self.1.next() }
#[inline]
fn next_edge_back(&mut self) -> Option<E> { self.1.next_back() }
}

Expand All @@ -1354,26 +1358,30 @@ struct MoveTraversalImpl<K, V> {
}

impl<K, V> TraversalImpl<K, V, Node<K, V>> for MoveTraversalImpl<K, V> {
#[inline]
fn next_kv(&mut self) -> Option<(K, V)> {
match (self.keys.next(), self.vals.next()) {
(Some(k), Some(v)) => Some((k, v)),
_ => None
}
}

#[inline]
fn next_kv_back(&mut self) -> Option<(K, V)> {
match (self.keys.next_back(), self.vals.next_back()) {
(Some(k), Some(v)) => Some((k, v)),
_ => None
}
}

#[inline]
fn next_edge(&mut self) -> Option<Node<K, V>> {
// Necessary for correctness, but in a private module
debug_assert!(!self.is_leaf);
self.edges.next()
}

#[inline]
fn next_edge_back(&mut self) -> Option<Node<K, V>> {
// Necessary for correctness, but in a private module
debug_assert!(!self.is_leaf);
Expand Down Expand Up @@ -1427,6 +1435,7 @@ pub type MoveTraversal<K, V> = AbsTraversal<MoveTraversalImpl<K, V>>;
impl<K, V, E, Impl: TraversalImpl<K, V, E>>
Iterator<TraversalItem<K, V, E>> for AbsTraversal<Impl> {

#[inline]
fn next(&mut self) -> Option<TraversalItem<K, V, E>> {
let head_is_edge = self.head_is_edge;
self.head_is_edge = !head_is_edge;
Expand All @@ -1442,6 +1451,7 @@ impl<K, V, E, Impl: TraversalImpl<K, V, E>>
impl<K, V, E, Impl: TraversalImpl<K, V, E>>
DoubleEndedIterator<TraversalItem<K, V, E>> for AbsTraversal<Impl> {

#[inline]
fn next_back(&mut self) -> Option<TraversalItem<K, V, E>> {
let tail_is_edge = self.tail_is_edge;
self.tail_is_edge = !tail_is_edge;
Expand Down
2 changes: 1 addition & 1 deletion src/libcollections/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#![allow(unknown_features)]
#![feature(macro_rules, default_type_params, phase, globs)]
#![feature(unsafe_destructor, import_shadowing, slicing_syntax)]
#![feature(tuple_indexing, unboxed_closures)]
#![feature(unboxed_closures)]
#![no_std]

#[phase(plugin, link)] extern crate core;
Expand Down