Skip to content

Commit deeec63

Browse files
committed
Added an Iterator::fold implementation for QueryIter.
This implementation enables internal iteration for many Iterator methods that are implemented using Iterator::fold which greatly improves their performance. Unfortunately, Iterator::try_fold cannot be implemented instead until Try becomes stable.
1 parent e8c4ca7 commit deeec63

2 files changed

Lines changed: 44 additions & 0 deletions

File tree

benches/bench.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,23 @@ fn iterate_mut_100k(c: &mut Criterion) {
158158
});
159159
}
160160

161+
fn for_each_100k(c: &mut Criterion) {
162+
let mut world = World::new();
163+
for i in 0..100_000 {
164+
world.spawn((Position(-(i as f32)), Velocity(i as f32)));
165+
}
166+
c.bench_function("for_each 100k", |b| {
167+
b.iter(|| {
168+
world
169+
.query::<(&mut Position, &Velocity)>()
170+
.iter()
171+
.for_each(|(pos, vel)| {
172+
pos.0 += vel.0;
173+
});
174+
})
175+
});
176+
}
177+
161178
fn spawn_100_by_50(world: &mut World) {
162179
fn spawn_two<const N: usize>(world: &mut World, i: i32) {
163180
world.spawn((Position(-(i as f32)), Velocity(i as f32), [(); N]));
@@ -323,6 +340,7 @@ criterion_group!(
323340
exchange,
324341
iterate_100k,
325342
iterate_mut_100k,
343+
for_each_100k,
326344
iterate_uncached_100_by_50,
327345
iterate_uncached_1_of_100_by_50,
328346
iterate_cached_100_by_50,

src/query.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,32 @@ impl<'q, Q: Query> Iterator for QueryIter<'q, Q> {
836836
let n = self.len();
837837
(n, Some(n))
838838
}
839+
840+
// We're implementing this because some `Iterator`-consuming methods are
841+
// implemented using `Iterator::fold`. With an optmized version implemented
842+
// without actually calling `Iterator::next`, they all benefit from
843+
// improved performance. See `for_each_100k` bench.
844+
//
845+
// TODO: Once `Try` is stable, implement `Iterator::try_fold` instead. This
846+
// will make *all* `Iterator`-consuming methods rely on that
847+
// implementation.
848+
fn fold<B, F>(mut self, mut init: B, mut f: F) -> B
849+
where
850+
Self: Sized,
851+
F: FnMut(B, Self::Item) -> B,
852+
{
853+
loop {
854+
while let Some(components) = unsafe { self.iter.next(self.world.entities_meta()) } {
855+
init = f(init, components);
856+
}
857+
858+
if let Some(iter) = unsafe { self.archetypes.next(self.world) } {
859+
self.iter = iter;
860+
} else {
861+
break init;
862+
}
863+
}
864+
}
839865
}
840866

841867
impl<Q: Query> ExactSizeIterator for QueryIter<'_, Q> {

0 commit comments

Comments
 (0)