From 1c1e7ce8979a587c5aa5583785d700303fe69529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Tue, 4 May 2021 00:00:35 +0200 Subject: [PATCH 1/3] remove ParallelIterator --- benches/benches/bevy_tasks/iter.rs | 148 ------- crates/bevy_ecs/src/system/query.rs | 14 +- crates/bevy_tasks/src/iter/adapters.rs | 235 ------------ crates/bevy_tasks/src/iter/mod.rs | 512 ------------------------- crates/bevy_tasks/src/lib.rs | 4 - examples/README.md | 2 +- examples/ecs/parallel_query.rs | 10 +- 7 files changed, 18 insertions(+), 907 deletions(-) delete mode 100644 benches/benches/bevy_tasks/iter.rs delete mode 100644 crates/bevy_tasks/src/iter/adapters.rs delete mode 100644 crates/bevy_tasks/src/iter/mod.rs diff --git a/benches/benches/bevy_tasks/iter.rs b/benches/benches/bevy_tasks/iter.rs deleted file mode 100644 index aafa4b6f5b3db..0000000000000 --- a/benches/benches/bevy_tasks/iter.rs +++ /dev/null @@ -1,148 +0,0 @@ -use bevy::tasks::{ParallelIterator, TaskPoolBuilder}; -use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; - -struct ParChunks<'a, T>(std::slice::Chunks<'a, T>); -impl<'a, T> ParallelIterator> for ParChunks<'a, T> -where - T: 'a + Send + Sync, -{ - type Item = &'a T; - - fn next_batch(&mut self) -> Option> { - self.0.next().map(|s| s.iter()) - } -} - -struct ParChunksMut<'a, T>(std::slice::ChunksMut<'a, T>); -impl<'a, T> ParallelIterator> for ParChunksMut<'a, T> -where - T: 'a + Send + Sync, -{ - type Item = &'a mut T; - - fn next_batch(&mut self) -> Option> { - self.0.next().map(|s| s.iter_mut()) - } -} - -fn bench_overhead(c: &mut Criterion) { - fn noop(_: &mut usize) {} - - let mut v = (0..10000).collect::>(); - c.bench_function("overhead_iter", |b| { - b.iter(|| { - v.iter_mut().for_each(noop); - }) - }); - - let mut v = (0..10000).collect::>(); - let mut group = c.benchmark_group("overhead_par_iter"); - for thread_count in &[1, 2, 4, 8, 16, 32] { - let pool = TaskPoolBuilder::new().num_threads(*thread_count).build(); - group.bench_with_input( - BenchmarkId::new("threads", thread_count), - thread_count, - |b, _| { - b.iter(|| { - ParChunksMut(v.chunks_mut(100)).for_each(&pool, noop); - }) - }, - ); - } - group.finish(); -} - -fn bench_for_each(c: &mut Criterion) { - fn busy_work(n: usize) { - let mut i = n; - while i > 0 { - i = black_box(i - 1); - } - } - - let mut v = (0..10000).collect::>(); - c.bench_function("for_each_iter", |b| { - b.iter(|| { - v.iter_mut().for_each(|x| { - busy_work(10000); - *x *= *x; - }); - }) - }); - - let mut v = (0..10000).collect::>(); - let mut group = c.benchmark_group("for_each_par_iter"); - for thread_count in &[1, 2, 4, 8, 16, 32] { - let pool = TaskPoolBuilder::new().num_threads(*thread_count).build(); - group.bench_with_input( - BenchmarkId::new("threads", thread_count), - thread_count, - |b, _| { - b.iter(|| { - ParChunksMut(v.chunks_mut(100)).for_each(&pool, |x| { - busy_work(10000); - *x *= *x; - }); - }) - }, - ); - } - group.finish(); -} - -fn bench_many_maps(c: &mut Criterion) { - fn busy_doubles(mut x: usize, n: usize) -> usize { - for _ in 0..n { - x = black_box(x.wrapping_mul(2)); - } - x - } - - let v = (0..10000).collect::>(); - c.bench_function("many_maps_iter", |b| { - b.iter(|| { - v.iter() - .map(|x| busy_doubles(*x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .for_each(drop); - }) - }); - - let v = (0..10000).collect::>(); - let mut group = c.benchmark_group("many_maps_par_iter"); - for thread_count in &[1, 2, 4, 8, 16, 32] { - let pool = TaskPoolBuilder::new().num_threads(*thread_count).build(); - group.bench_with_input( - BenchmarkId::new("threads", thread_count), - thread_count, - |b, _| { - b.iter(|| { - ParChunks(v.chunks(100)) - .map(|x| busy_doubles(*x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .map(|x| busy_doubles(x, 1000)) - .for_each(&pool, drop); - }) - }, - ); - } - group.finish(); -} - -criterion_group!(benches, bench_overhead, bench_for_each, bench_many_maps); -criterion_main!(benches); diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index a2f2e94be9a5a..149f7ed0da621 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -219,7 +219,14 @@ where }; } - /// Runs `f` on each query result in parallel using the given task pool. + /// Runs `f` on each query result in parallel using the given task pool. This is + /// the parallel equivalent of [`Self::for_each`] + /// + /// Note that the overhead of ParallelIterator is high relative to some + /// workloads. In particular, if the batch size is too small or task being + /// run in parallel is inexpensive, *a `par_for_each` could take longer + /// than a normal [`Self::for_each`]*. Therefore, you should profile your code before + /// using ParallelIterator. /// /// This can only be called for read-only queries, see [`Self::par_for_each_mut`] for /// write-queries. @@ -246,7 +253,10 @@ where }; } - /// Runs `f` on each query result in parallel using the given task pool. + /// Runs `f` on each query result in parallel using the given task pool. This is + /// the parallel equivalent of [`Self::for_each_mut`] + /// + /// See [`Self::par_for_each`] for more information on performance. #[inline] pub fn par_for_each_mut( &mut self, diff --git a/crates/bevy_tasks/src/iter/adapters.rs b/crates/bevy_tasks/src/iter/adapters.rs deleted file mode 100644 index 047f6fb8ad5f0..0000000000000 --- a/crates/bevy_tasks/src/iter/adapters.rs +++ /dev/null @@ -1,235 +0,0 @@ -use crate::iter::ParallelIterator; - -#[derive(Debug)] -pub struct Chain { - pub(crate) left: T, - pub(crate) right: U, - pub(crate) left_in_progress: bool, -} - -impl ParallelIterator for Chain -where - B: Iterator + Send, - T: ParallelIterator, - U: ParallelIterator, -{ - type Item = T::Item; - - fn next_batch(&mut self) -> Option { - if self.left_in_progress { - match self.left.next_batch() { - b @ Some(_) => return b, - None => self.left_in_progress = false, - } - } - self.right.next_batch() - } -} - -#[derive(Debug)] -pub struct Map { - pub(crate) iter: P, - pub(crate) f: F, -} - -impl ParallelIterator> for Map -where - B: Iterator + Send, - U: ParallelIterator, - F: FnMut(U::Item) -> T + Send + Clone, -{ - type Item = T; - - fn next_batch(&mut self) -> Option> { - self.iter.next_batch().map(|b| b.map(self.f.clone())) - } -} - -#[derive(Debug)] -pub struct Filter { - pub(crate) iter: P, - pub(crate) predicate: F, -} - -impl ParallelIterator> for Filter -where - B: Iterator + Send, - P: ParallelIterator, - F: FnMut(&P::Item) -> bool + Send + Clone, -{ - type Item = P::Item; - - fn next_batch(&mut self) -> Option> { - self.iter - .next_batch() - .map(|b| b.filter(self.predicate.clone())) - } -} - -#[derive(Debug)] -pub struct FilterMap { - pub(crate) iter: P, - pub(crate) f: F, -} - -impl ParallelIterator> for FilterMap -where - B: Iterator + Send, - P: ParallelIterator, - F: FnMut(P::Item) -> Option + Send + Clone, -{ - type Item = R; - - fn next_batch(&mut self) -> Option> { - self.iter.next_batch().map(|b| b.filter_map(self.f.clone())) - } -} - -#[derive(Debug)] -pub struct FlatMap { - pub(crate) iter: P, - pub(crate) f: F, -} - -impl ParallelIterator> for FlatMap -where - B: Iterator + Send, - P: ParallelIterator, - F: FnMut(P::Item) -> U + Send + Clone, - U: IntoIterator, - U::IntoIter: Send, -{ - type Item = U::Item; - - // This extends each batch using the flat map. The other option is - // to turn each IntoIter into its own batch. - fn next_batch(&mut self) -> Option> { - self.iter.next_batch().map(|b| b.flat_map(self.f.clone())) - } -} - -#[derive(Debug)] -pub struct Flatten

{ - pub(crate) iter: P, -} - -impl ParallelIterator> for Flatten

-where - B: Iterator + Send, - P: ParallelIterator, - B::Item: IntoIterator, - ::IntoIter: Send, -{ - type Item = ::Item; - - // This extends each batch using the flatten. The other option is to - // turn each IntoIter into its own batch. - fn next_batch(&mut self) -> Option> { - self.iter.next_batch().map(|b| b.flatten()) - } -} - -#[derive(Debug)] -pub struct Fuse

{ - pub(crate) iter: Option

, -} - -impl ParallelIterator for Fuse

-where - B: Iterator + Send, - P: ParallelIterator, -{ - type Item = P::Item; - - fn next_batch(&mut self) -> Option { - match &mut self.iter { - Some(iter) => match iter.next_batch() { - b @ Some(_) => b, - None => { - self.iter = None; - None - } - }, - None => None, - } - } -} - -#[derive(Debug)] -pub struct Inspect { - pub(crate) iter: P, - pub(crate) f: F, -} - -impl ParallelIterator> for Inspect -where - B: Iterator + Send, - P: ParallelIterator, - F: FnMut(&P::Item) + Send + Clone, -{ - type Item = P::Item; - - fn next_batch(&mut self) -> Option> { - self.iter.next_batch().map(|b| b.inspect(self.f.clone())) - } -} - -#[derive(Debug)] -pub struct Copied

{ - pub(crate) iter: P, -} - -impl<'a, B, P, T> ParallelIterator> for Copied

-where - B: Iterator + Send, - P: ParallelIterator, - T: 'a + Copy, -{ - type Item = T; - - fn next_batch(&mut self) -> Option> { - self.iter.next_batch().map(|b| b.copied()) - } -} - -#[derive(Debug)] -pub struct Cloned

{ - pub(crate) iter: P, -} - -impl<'a, B, P, T> ParallelIterator> for Cloned

-where - B: Iterator + Send, - P: ParallelIterator, - T: 'a + Copy, -{ - type Item = T; - - fn next_batch(&mut self) -> Option> { - self.iter.next_batch().map(|b| b.cloned()) - } -} - -#[derive(Debug)] -pub struct Cycle

{ - pub(crate) iter: P, - pub(crate) curr: Option

, -} - -impl ParallelIterator for Cycle

-where - B: Iterator + Send, - P: ParallelIterator + Clone, -{ - type Item = P::Item; - - fn next_batch(&mut self) -> Option { - match self.curr.as_mut().and_then(|c| c.next_batch()) { - batch @ Some(_) => batch, - None => { - self.curr = Some(self.iter.clone()); - self.next_batch() - } - } - } -} diff --git a/crates/bevy_tasks/src/iter/mod.rs b/crates/bevy_tasks/src/iter/mod.rs deleted file mode 100644 index 91147fceb1acf..0000000000000 --- a/crates/bevy_tasks/src/iter/mod.rs +++ /dev/null @@ -1,512 +0,0 @@ -use crate::TaskPool; - -mod adapters; -pub use adapters::*; - -/// ParallelIterator closely emulates the std::iter::Iterator -/// interface. However, it uses bevy_task to compute batches in parallel. -/// -/// Note that the overhead of ParallelIterator is high relative to some -/// workloads. In particular, if the batch size is too small or task being -/// run in parallel is inexpensive, *a ParallelIterator could take longer -/// than a normal Iterator*. Therefore, you should profile your code before -/// using ParallelIterator. -pub trait ParallelIterator -where - B: Iterator + Send, - Self: Sized + Send, -{ - type Item; - - /// Returns the next batch of items for processing. - /// - /// Each batch is an iterator with items of the same type as the - /// ParallelIterator. Returns `None` when there are no batches left. - fn next_batch(&mut self) -> Option; - - /// Returns the bounds on the remaining number of items in the - /// parallel iterator. - /// - /// See [`Iterator::size_hint()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.size_hint) - fn size_hint(&self) -> (usize, Option) { - (0, None) - } - - /// Consumes the parallel iterator and returns the number of items. - /// - /// See [`Iterator::count()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.count) - fn count(mut self, pool: &TaskPool) -> usize { - pool.scope(|s| { - while let Some(batch) = self.next_batch() { - s.spawn(async move { batch.count() }) - } - }) - .iter() - .sum() - } - - /// Consumes the parallel iterator and returns the last item. - /// - /// See [`Iterator::last()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.last) - fn last(mut self, _pool: &TaskPool) -> Option { - let mut last_item = None; - while let Some(batch) = self.next_batch() { - last_item = batch.last(); - } - last_item - } - - /// Consumes the parallel iterator and returns the nth item. - /// - /// See [`Iterator::nth()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.nth) - // TODO: Optimize with size_hint on each batch - fn nth(mut self, _pool: &TaskPool, n: usize) -> Option { - let mut i = 0; - while let Some(batch) = self.next_batch() { - for item in batch { - if i == n { - return Some(item); - } - i += 1; - } - } - None - } - - /// Takes two parallel iterators and returns a parallel iterators over - /// both in sequence. - /// - /// See [`Iterator::chain()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.chain) - // TODO: Use IntoParallelIterator for U - fn chain(self, other: U) -> Chain - where - U: ParallelIterator, - { - Chain { - left: self, - right: other, - left_in_progress: true, - } - } - - /// Takes a closure and creates a parallel iterator which calls that - /// closure on each item. - /// - /// See [`Iterator::map()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map) - fn map(self, f: F) -> Map - where - F: FnMut(Self::Item) -> T + Send + Clone, - { - Map { iter: self, f } - } - - /// Calls a closure on each item of a parallel iterator. - /// - /// See [`Iterator::for_each()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.for_each) - fn for_each(mut self, pool: &TaskPool, f: F) - where - F: FnMut(Self::Item) + Send + Clone + Sync, - { - pool.scope(|s| { - while let Some(batch) = self.next_batch() { - let newf = f.clone(); - s.spawn(async move { - batch.for_each(newf); - }); - } - }); - } - - /// Creates a parallel iterator which uses a closure to determine - /// if an element should be yielded. - /// - /// See [`Iterator::filter()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter) - fn filter(self, predicate: F) -> Filter - where - F: FnMut(&Self::Item) -> bool, - { - Filter { - iter: self, - predicate, - } - } - - /// Creates a parallel iterator that both filters and maps. - /// - /// See [`Iterator::filter_map()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map) - fn filter_map(self, f: F) -> FilterMap - where - F: FnMut(Self::Item) -> Option, - { - FilterMap { iter: self, f } - } - - /// Creates a parallel iterator that works like map, but flattens - /// nested structure. - /// - /// See [`Iterator::flat_map()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.flat_map) - fn flat_map(self, f: F) -> FlatMap - where - F: FnMut(Self::Item) -> U, - U: IntoIterator, - { - FlatMap { iter: self, f } - } - - /// Creates a parallel iterator that flattens nested structure. - /// - /// See [`Iterator::flatten()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.flatten) - fn flatten(self) -> Flatten - where - Self::Item: IntoIterator, - { - Flatten { iter: self } - } - - /// Creates a parallel iterator which ends after the first None. - /// - /// See [`Iterator::fuse()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fuse) - fn fuse(self) -> Fuse { - Fuse { iter: Some(self) } - } - - /// Does something with each item of a parallel iterator, passing - /// the value on. - /// - /// See [`Iterator::inspect()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.inspect) - fn inspect(self, f: F) -> Inspect - where - F: FnMut(&Self::Item), - { - Inspect { iter: self, f } - } - - /// Borrows a parallel iterator, rather than consuming it. - /// - /// See [`Iterator::by_ref()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.by_ref) - fn by_ref(&mut self) -> &mut Self { - self - } - - /// Transforms a parallel iterator into a collection. - /// - /// See [`Iterator::collect()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect) - // TODO: Investigate optimizations for less copying - fn collect(mut self, pool: &TaskPool) -> C - where - C: std::iter::FromIterator, - Self::Item: Send + 'static, - { - pool.scope(|s| { - while let Some(batch) = self.next_batch() { - s.spawn(async move { batch.collect::>() }); - } - }) - .into_iter() - .flatten() - .collect() - } - - /// Consumes a parallel iterator, creating two collections from it. - /// - /// See [`Iterator::partition()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.partition) - // TODO: Investigate optimizations for less copying - fn partition(mut self, pool: &TaskPool, f: F) -> (C, C) - where - C: Default + Extend + Send, - F: FnMut(&Self::Item) -> bool + Send + Sync + Clone, - Self::Item: Send + 'static, - { - let (mut a, mut b) = <(C, C)>::default(); - pool.scope(|s| { - while let Some(batch) = self.next_batch() { - let newf = f.clone(); - s.spawn(async move { batch.partition::, F>(newf) }) - } - }) - .into_iter() - .for_each(|(c, d)| { - a.extend(c); - b.extend(d); - }); - (a, b) - } - - /// Repeatedly applies a function to items of each batch of a parallel - /// iterator, producing a Vec of final values. - /// - /// *Note that this folds each batch independently and returns a Vec of - /// results (in batch order).* - /// - /// See [`Iterator::fold()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold) - fn fold(mut self, pool: &TaskPool, init: C, f: F) -> Vec - where - F: FnMut(C, Self::Item) -> C + Send + Sync + Clone, - C: Clone + Send + Sync + 'static, - { - pool.scope(|s| { - while let Some(batch) = self.next_batch() { - let newf = f.clone(); - let newi = init.clone(); - s.spawn(async move { batch.fold(newi, newf) }); - } - }) - } - - /// Tests if every element of the parallel iterator matches a predicate. - /// - /// *Note that all is **not** short circuiting.* - /// - /// See [`Iterator::all()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.all) - fn all(mut self, pool: &TaskPool, f: F) -> bool - where - F: FnMut(Self::Item) -> bool + Send + Sync + Clone, - { - pool.scope(|s| { - while let Some(mut batch) = self.next_batch() { - let newf = f.clone(); - s.spawn(async move { batch.all(newf) }); - } - }) - .into_iter() - .all(std::convert::identity) - } - - /// Tests if any element of the parallel iterator matches a predicate. - /// - /// *Note that any is **not** short circuiting.* - /// - /// See [`Iterator::any()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.any) - fn any(mut self, pool: &TaskPool, f: F) -> bool - where - F: FnMut(Self::Item) -> bool + Send + Sync + Clone, - { - pool.scope(|s| { - while let Some(mut batch) = self.next_batch() { - let newf = f.clone(); - s.spawn(async move { batch.any(newf) }); - } - }) - .into_iter() - .any(std::convert::identity) - } - - /// Searches for an element in a parallel iterator, returning its index. - /// - /// *Note that position consumes the whole iterator.* - /// - /// See [`Iterator::position()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.position) - // TODO: Investigate optimizations for less copying - fn position(mut self, pool: &TaskPool, f: F) -> Option - where - F: FnMut(Self::Item) -> bool + Send + Sync + Clone, - { - let poses = pool.scope(|s| { - while let Some(batch) = self.next_batch() { - let mut newf = f.clone(); - s.spawn(async move { - let mut len = 0; - let mut pos = None; - for item in batch { - if pos.is_none() && newf(item) { - pos = Some(len); - } - len += 1; - } - (len, pos) - }); - } - }); - let mut start = 0; - for (len, pos) in poses { - if let Some(pos) = pos { - return Some(start + pos); - } - start += len; - } - None - } - - /// Returns the maximum item of a parallel iterator. - /// - /// See [`Iterator::max()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.max) - fn max(mut self, pool: &TaskPool) -> Option - where - Self::Item: Ord + Send + 'static, - { - pool.scope(|s| { - while let Some(batch) = self.next_batch() { - s.spawn(async move { batch.max() }); - } - }) - .into_iter() - .flatten() - .max() - } - - /// Returns the minimum item of a parallel iterator. - /// - /// See [`Iterator::min()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.min) - fn min(mut self, pool: &TaskPool) -> Option - where - Self::Item: Ord + Send + 'static, - { - pool.scope(|s| { - while let Some(batch) = self.next_batch() { - s.spawn(async move { batch.min() }); - } - }) - .into_iter() - .flatten() - .min() - } - - /// Returns the item that gives the maximum value from the specified function. - /// - /// See [`Iterator::max_by_key()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.max_by_key) - fn max_by_key(mut self, pool: &TaskPool, f: F) -> Option - where - R: Ord, - F: FnMut(&Self::Item) -> R + Send + Sync + Clone, - Self::Item: Send + 'static, - { - pool.scope(|s| { - while let Some(batch) = self.next_batch() { - let newf = f.clone(); - s.spawn(async move { batch.max_by_key(newf) }); - } - }) - .into_iter() - .flatten() - .max_by_key(f) - } - - /// Returns the item that gives the maximum value with respect to the specified comparison - /// function. - /// - /// See [`Iterator::max_by()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.max_by) - fn max_by(mut self, pool: &TaskPool, f: F) -> Option - where - F: FnMut(&Self::Item, &Self::Item) -> std::cmp::Ordering + Send + Sync + Clone, - Self::Item: Send + 'static, - { - pool.scope(|s| { - while let Some(batch) = self.next_batch() { - let newf = f.clone(); - s.spawn(async move { batch.max_by(newf) }); - } - }) - .into_iter() - .flatten() - .max_by(f) - } - - /// Returns the item that gives the minimum value from the specified function. - /// - /// See [`Iterator::min_by_key()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.min_by_key) - fn min_by_key(mut self, pool: &TaskPool, f: F) -> Option - where - R: Ord, - F: FnMut(&Self::Item) -> R + Send + Sync + Clone, - Self::Item: Send + 'static, - { - pool.scope(|s| { - while let Some(batch) = self.next_batch() { - let newf = f.clone(); - s.spawn(async move { batch.min_by_key(newf) }); - } - }) - .into_iter() - .flatten() - .min_by_key(f) - } - - /// Returns the item that gives the minimum value with respect to the specified comparison - /// function. - /// - /// See [`Iterator::min_by()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.min_by) - fn min_by(mut self, pool: &TaskPool, f: F) -> Option - where - F: FnMut(&Self::Item, &Self::Item) -> std::cmp::Ordering + Send + Sync + Clone, - Self::Item: Send + 'static, - { - pool.scope(|s| { - while let Some(batch) = self.next_batch() { - let newf = f.clone(); - s.spawn(async move { batch.min_by(newf) }); - } - }) - .into_iter() - .flatten() - .min_by(f) - } - - /// Creates a parallel iterator which copies all of its items. - /// - /// See [`Iterator::copied()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.copied) - fn copied<'a, T>(self) -> Copied - where - Self: ParallelIterator, - T: 'a + Copy, - { - Copied { iter: self } - } - - /// Creates a parallel iterator which clones all of its items. - /// - /// See [`Iterator::cloned()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.cloned) - fn cloned<'a, T>(self) -> Cloned - where - Self: ParallelIterator, - T: 'a + Copy, - { - Cloned { iter: self } - } - - /// Repeats a parallel iterator endlessly. - /// - /// See [`Iterator::cycle()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.cycle) - fn cycle(self) -> Cycle - where - Self: Clone, - { - Cycle { - iter: self, - curr: None, - } - } - - /// Sums the items of a parallel iterator. - /// - /// See [`Iterator::sum()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.sum) - fn sum(mut self, pool: &TaskPool) -> R - where - S: std::iter::Sum + Send + 'static, - R: std::iter::Sum, - { - pool.scope(|s| { - while let Some(batch) = self.next_batch() { - s.spawn(async move { batch.sum() }); - } - }) - .into_iter() - .sum() - } - - /// Multiplies all the items of a parallel iterator. - /// - /// See [`Iterator::product()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.product) - fn product(mut self, pool: &TaskPool) -> R - where - S: std::iter::Product + Send + 'static, - R: std::iter::Product, - { - pool.scope(|s| { - while let Some(batch) = self.next_batch() { - s.spawn(async move { batch.product() }); - } - }) - .into_iter() - .product() - } -} diff --git a/crates/bevy_tasks/src/lib.rs b/crates/bevy_tasks/src/lib.rs index 08d7dfc42b988..c690242e78777 100644 --- a/crates/bevy_tasks/src/lib.rs +++ b/crates/bevy_tasks/src/lib.rs @@ -20,13 +20,9 @@ pub use usages::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool}; mod countdown_event; pub use countdown_event::CountdownEvent; -mod iter; -pub use iter::ParallelIterator; - pub mod prelude { #[doc(hidden)] pub use crate::{ - iter::ParallelIterator, slice::{ParallelSlice, ParallelSliceMut}, usages::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool}, }; diff --git a/examples/README.md b/examples/README.md index 096afc63e4193..d9184be86a6f3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -153,7 +153,7 @@ Example | File | Description `event` | [`ecs/event.rs`](./ecs/event.rs) | Illustrates event creation, activation, and reception `fixed_timestep` | [`ecs/fixed_timestep.rs`](./ecs/fixed_timestep.rs) | Shows how to create systems that run every fixed timestep, rather than every tick `hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities -`parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator` +`parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `par_for_each_mut` `query_bundle` | [`ecs/query_bundle.rs`](./ecs/query_bundle.rs) | Shows how to query entities that contain components in a `Bundle` `removal_detection` | [`ecs/removal_detection.rs`](./ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame. `startup_system` | [`ecs/startup_system.rs`](./ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up) diff --git a/examples/ecs/parallel_query.rs b/examples/ecs/parallel_query.rs index 6d4ed06a4bc43..7e96f9df1c000 100644 --- a/examples/ecs/parallel_query.rs +++ b/examples/ecs/parallel_query.rs @@ -29,11 +29,11 @@ fn move_system(pool: Res, mut sprites: Query<(&mut Transform, & // Compute the new location of each sprite in parallel on the // ComputeTaskPool using batches of 32 sprites // - // This example is only for demonstrative purposes. Using a - // ParallelIterator for an inexpensive operation like addition on only 128 + // This example is only for demonstrative purposes. Using + // `par_for_each_mut` for an inexpensive operation like addition on only 128 // elements will not typically be faster than just using a normal Iterator. - // See the ParallelIterator documentation for more information on when - // to use or not use ParallelIterator over a normal Iterator. + // See `par_for_each_mut` documentation for more information on when + // to use or not use `par_for_each_mut` over a normal Iterator. sprites.par_for_each_mut(&pool, 32, |(mut transform, velocity)| { transform.translation += velocity.0.extend(0.0); }); @@ -54,7 +54,7 @@ fn bounce_system( let top = height / 2.0; sprites // Batch size of 32 is chosen to limit the overhead of - // ParallelIterator, since negating a vector is very inexpensive. + // `par_for_each_mut`, since negating a vector is very inexpensive. .par_for_each_mut(&pool, 32, |(transform, mut v)| { if !(left < transform.translation.x && transform.translation.x < right From a1285b1e072b9261d1c5f441e2f6fed284b262fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Tue, 4 May 2021 00:18:15 +0200 Subject: [PATCH 2/3] missed one... in a comment I added... Co-authored-by: MinerSebas <66798382+MinerSebas@users.noreply.github.com> --- crates/bevy_ecs/src/system/query.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 149f7ed0da621..e040301fd885b 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -222,7 +222,7 @@ where /// Runs `f` on each query result in parallel using the given task pool. This is /// the parallel equivalent of [`Self::for_each`] /// - /// Note that the overhead of ParallelIterator is high relative to some + /// Note that the overhead of `par_for_each` is high relative to some /// workloads. In particular, if the batch size is too small or task being /// run in parallel is inexpensive, *a `par_for_each` could take longer /// than a normal [`Self::for_each`]*. Therefore, you should profile your code before From 67eef38ac2e236fb71d4e72433c5b8fe5411ac17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Tue, 4 May 2021 00:21:01 +0200 Subject: [PATCH 3/3] another missed --- crates/bevy_ecs/src/system/query.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index e040301fd885b..f0406ef1b7af8 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -226,7 +226,7 @@ where /// workloads. In particular, if the batch size is too small or task being /// run in parallel is inexpensive, *a `par_for_each` could take longer /// than a normal [`Self::for_each`]*. Therefore, you should profile your code before - /// using ParallelIterator. + /// using `par_for_each`. /// /// This can only be called for read-only queries, see [`Self::par_for_each_mut`] for /// write-queries.