Skip to content

Commit def348d

Browse files
author
Clark Gaebel
committed
Add intrusive iterators to BTree.
Unfortunately, tree structures are intrinsically slower to iterate over externally than internally. This can be demonstrated in benchmarks. In fact, it's so bad at external iteration that calling `.find` on each element in succession is currently slightly faster. This patch implements a faster intrusive way to iterate over BTrees. This is about 5x faster, but you lose all iterator composition infrastructure. This is a tradeoff that is acceptable in some applications. Relevant benchmarks: ``` test btree::map::bench::intrusive_iter_1000 ... bench: 2658 ns/iter (+/- 602) test btree::map::bench::intrusive_iter_100000 ... bench: 346353 ns/iter (+/- 189565) test btree::map::bench::intrusive_iter_20 ... bench: 55 ns/iter (+/- 16) test btree::map::bench::iter_1000 ... bench: 15892 ns/iter (+/- 3717) test btree::map::bench::iter_100000 ... bench: 1383714 ns/iter (+/- 444706) test btree::map::bench::iter_20 ... bench: 366 ns/iter (+/- 104) ``` r? @gankro @huonw @aturon how does this fit into 1.0 stabilization plans. Is marking this as #[experimental] enough?
1 parent ffc1118 commit def348d

File tree

3 files changed

+104
-1
lines changed

3 files changed

+104
-1
lines changed

src/libcollections/btree/map.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,7 @@ impl<K, V, E, T: Traverse<E> + DoubleEndedIterator<TraversalItem<K, V, E>>>
937937
// making these arbitrary sub-range iterators. However the logic to construct these paths
938938
// efficiently is fairly involved, so this is a FIXME. The sub-range iterators also wouldn't be
939939
// able to accurately predict size, so those iterators can't implement ExactSizeIterator.
940+
#[inline]
940941
fn next(&mut self) -> Option<(K, V)> {
941942
loop {
942943
// We want the smallest element, so try to get the top of the left stack
@@ -1030,6 +1031,7 @@ impl<K, V, E, T: Traverse<E> + DoubleEndedIterator<TraversalItem<K, V, E>>>
10301031
}
10311032

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

10411043

10421044
impl<'a, K, V> Iterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> {
1045+
#[inline]
10431046
fn next(&mut self) -> Option<(&'a K, &'a mut V)> { self.inner.next() }
10441047
fn size_hint(&self) -> (uint, Option<uint>) { self.inner.size_hint() }
10451048
}
10461049
impl<'a, K, V> DoubleEndedIterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> {
1050+
#[inline]
10471051
fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> { self.inner.next_back() }
10481052
}
10491053
impl<'a, K, V> ExactSizeIterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> {}
10501054

10511055

10521056
impl<K, V> Iterator<(K, V)> for MoveEntries<K, V> {
1057+
#[inline]
10531058
fn next(&mut self) -> Option<(K, V)> { self.inner.next() }
10541059
fn size_hint(&self) -> (uint, Option<uint>) { self.inner.size_hint() }
10551060
}
10561061
impl<K, V> DoubleEndedIterator<(K, V)> for MoveEntries<K, V> {
1062+
#[inline]
10571063
fn next_back(&mut self) -> Option<(K, V)> { self.inner.next_back() }
10581064
}
10591065
impl<K, V> ExactSizeIterator<(K, V)> for MoveEntries<K, V> {}
@@ -1130,6 +1136,25 @@ impl<K, V> BTreeMap<K, V> {
11301136
}
11311137
}
11321138

1139+
/// An intrusive version of `.iter()`. The closure will be called once with
1140+
/// every key/value pair in the tree.
1141+
///
1142+
/// This is faster than calling `.iter()`, but is far less composable.
1143+
#[inline]
1144+
#[experimental = "relies on unboxed closures"]
1145+
pub fn intrusive_iter<F: FnMut(&K, &V)>(&self, mut f: F) {
1146+
fn intrusive_iter_impl<K, V, F: FnMut(&K, &V)>(node: &Node<K, V>, f: &mut F) {
1147+
for ti in node.iter() {
1148+
match ti {
1149+
Elem(k, v) => (*f)(k, v),
1150+
Edge(e) => intrusive_iter_impl(e, f),
1151+
}
1152+
}
1153+
}
1154+
1155+
intrusive_iter_impl(&self.root, &mut f);
1156+
}
1157+
11331158
/// Gets a mutable iterator over the entries of the map.
11341159
#[unstable = "matches collection reform specification, waiting for dust to settle"]
11351160
pub fn iter_mut<'a>(&'a mut self) -> MutEntries<'a, K, V> {
@@ -1144,6 +1169,25 @@ impl<K, V> BTreeMap<K, V> {
11441169
}
11451170
}
11461171

1172+
/// An intrusive version of `.iter_mut()`. The closure will be called once
1173+
/// with every key/value pair in the tree.
1174+
///
1175+
/// This is faster than calling `.iter_mut()`, but is far less composable.
1176+
#[inline]
1177+
#[experimental = "relies on unboxed closures"]
1178+
pub fn intrusive_iter_mut<F: FnMut(&K, &mut V)>(&mut self, mut f: F) {
1179+
fn intrusive_iter_mut_impl<K, V, F: FnMut(&K, &mut V)>(node: &mut Node<K, V>, f: &mut F) {
1180+
for ti in node.iter_mut() {
1181+
match ti {
1182+
Elem(k, v) => (*f)(k, v),
1183+
Edge(e) => intrusive_iter_mut_impl(e, f),
1184+
}
1185+
}
1186+
}
1187+
1188+
intrusive_iter_mut_impl(&mut self.root, &mut f);
1189+
}
1190+
11471191
/// Gets an owning iterator over the entries of the map.
11481192
#[unstable = "matches collection reform specification, waiting for dust to settle"]
11491193
pub fn into_iter(self) -> MoveEntries<K, V> {
@@ -1158,6 +1202,25 @@ impl<K, V> BTreeMap<K, V> {
11581202
}
11591203
}
11601204

1205+
/// An intrusive version of `.into_iter()`. The closure will be called once
1206+
/// with every key/value pair in the tree.
1207+
///
1208+
/// This is faster than calling `.into_iter()`, but is far less composable.
1209+
#[inline]
1210+
#[experimental = "relies on unboxed closures"]
1211+
pub fn intrusive_into_iter<F: FnMut(K, V)>(self, mut f: F) {
1212+
fn intrusive_into_iter_impl<K, V, F: FnMut(K, V)>(node: Node<K, V>, f: &mut F) {
1213+
for ti in node.into_iter() {
1214+
match ti {
1215+
Elem(k, v) => (*f)(k, v),
1216+
Edge(e) => intrusive_into_iter_impl(e, f),
1217+
}
1218+
}
1219+
}
1220+
1221+
intrusive_into_iter_impl(self.root, &mut f);
1222+
}
1223+
11611224
/// Gets an iterator over the keys of the map.
11621225
///
11631226
/// # Examples
@@ -1580,4 +1643,34 @@ mod bench {
15801643
pub fn iter_100000(b: &mut Bencher) {
15811644
bench_iter(b, 100000);
15821645
}
1646+
1647+
fn bench_intrusive_iter(b: &mut Bencher, size: uint) {
1648+
let mut map = BTreeMap::<uint, uint>::new();
1649+
let mut rng = weak_rng();
1650+
1651+
for _ in range(0, size) {
1652+
map.insert(rng.gen(), rng.gen());
1653+
}
1654+
1655+
b.iter(|| {
1656+
map.intrusive_iter(|&mut: k, v| {
1657+
black_box(k); black_box(v);
1658+
});
1659+
});
1660+
}
1661+
1662+
#[bench]
1663+
pub fn intrusive_iter_20(b: &mut Bencher) {
1664+
bench_intrusive_iter(b, 20);
1665+
}
1666+
1667+
#[bench]
1668+
pub fn intrusive_iter_1000(b: &mut Bencher) {
1669+
bench_intrusive_iter(b, 1000);
1670+
}
1671+
1672+
#[bench]
1673+
pub fn intrusive_iter_100000(b: &mut Bencher) {
1674+
bench_intrusive_iter(b, 100000);
1675+
}
15831676
}

src/libcollections/btree/node.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,10 +1334,14 @@ struct ElemsAndEdges<Elems, Edges>(Elems, Edges);
13341334
impl<K, V, E, Elems: DoubleEndedIterator<(K, V)>, Edges: DoubleEndedIterator<E>>
13351335
TraversalImpl<K, V, E> for ElemsAndEdges<Elems, Edges> {
13361336

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

1342+
#[inline]
13401343
fn next_edge(&mut self) -> Option<E> { self.1.next() }
1344+
#[inline]
13411345
fn next_edge_back(&mut self) -> Option<E> { self.1.next_back() }
13421346
}
13431347

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

13561360
impl<K, V> TraversalImpl<K, V, Node<K, V>> for MoveTraversalImpl<K, V> {
1361+
#[inline]
13571362
fn next_kv(&mut self) -> Option<(K, V)> {
13581363
match (self.keys.next(), self.vals.next()) {
13591364
(Some(k), Some(v)) => Some((k, v)),
13601365
_ => None
13611366
}
13621367
}
13631368

1369+
#[inline]
13641370
fn next_kv_back(&mut self) -> Option<(K, V)> {
13651371
match (self.keys.next_back(), self.vals.next_back()) {
13661372
(Some(k), Some(v)) => Some((k, v)),
13671373
_ => None
13681374
}
13691375
}
13701376

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

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

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

1454+
#[inline]
14451455
fn next_back(&mut self) -> Option<TraversalItem<K, V, E>> {
14461456
let tail_is_edge = self.tail_is_edge;
14471457
self.tail_is_edge = !tail_is_edge;

src/libcollections/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
#![allow(unknown_features)]
2525
#![feature(macro_rules, default_type_params, phase, globs)]
2626
#![feature(unsafe_destructor, import_shadowing, slicing_syntax)]
27-
#![feature(tuple_indexing, unboxed_closures)]
27+
#![feature(unboxed_closures)]
2828
#![no_std]
2929

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

0 commit comments

Comments
 (0)