| @@ -4,4 +4,11 @@ version = "0.1.0" | |||||
| authors = ["Bergmann89 <info@bergmann89.de>"] | authors = ["Bergmann89 <info@bergmann89.de>"] | ||||
| edition = "2018" | edition = "2018" | ||||
| [features] | |||||
| default = [ "tokio-executor" ] | |||||
| tokio-executor = [ "futures", "num_cpus", "tokio" ] | |||||
| [dependencies] | [dependencies] | ||||
| futures = { version = "0.3", optional = true } | |||||
| num_cpus = { version = "1.13", optional = true } | |||||
| tokio = { version = "0.3", features = [ "macros", "rt-multi-thread" ], optional = true } | |||||
| @@ -1,11 +1,11 @@ | |||||
| use crate::{Executor, DefaultExecutor}; | use crate::{Executor, DefaultExecutor}; | ||||
| pub trait Driver<D>: Sized | |||||
| pub trait Driver<'a, D>: Sized | |||||
| where D: Send, | where D: Send, | ||||
| { | { | ||||
| fn exec_with<E>(self, executor: E) -> E::Result | fn exec_with<E>(self, executor: E) -> E::Result | ||||
| where E: Executor<D>; | |||||
| where E: Executor<'a, D>; | |||||
| fn exec(self) -> <DefaultExecutor as Executor<D>>::Result | |||||
| fn exec(self) -> <DefaultExecutor as Executor<'a, D>>::Result | |||||
| { self.exec_with(DefaultExecutor::default()) } | { self.exec_with(DefaultExecutor::default()) } | ||||
| } | } | ||||
| @@ -2,22 +2,22 @@ use super::{ | |||||
| Consumer, IndexedConsumer, Producer, IndexedProducer, Reducer, ProducerCallback, IndexedProducerCallback, | Consumer, IndexedConsumer, Producer, IndexedProducer, Reducer, ProducerCallback, IndexedProducerCallback, | ||||
| }; | }; | ||||
| pub trait Executor<D> | |||||
| pub trait Executor<'a, D> | |||||
| where D: Send, | where D: Send, | ||||
| { | { | ||||
| type Result: Send; | type Result: Send; | ||||
| fn exec<P, C, R>(self, producer: P, consumer: C) -> Self::Result | fn exec<P, C, R>(self, producer: P, consumer: C) -> Self::Result | ||||
| where | where | ||||
| P: Producer, | |||||
| C: Consumer<P::Item, Result = D, Reducer = R>, | |||||
| R: Reducer<D>; | |||||
| P: Producer + 'a, | |||||
| C: Consumer<P::Item, Result = D, Reducer = R> + 'a, | |||||
| R: Reducer<D> + Send; | |||||
| fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result | fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result | ||||
| where | where | ||||
| P: IndexedProducer, | |||||
| C: IndexedConsumer<P::Item, Result = D, Reducer = R>, | |||||
| R: Reducer<D>; | |||||
| P: IndexedProducer + 'a, | |||||
| C: IndexedConsumer<P::Item, Result = D, Reducer = R> + 'a, | |||||
| R: Reducer<D> + Send; | |||||
| } | } | ||||
| pub struct ExecutorCallback<E, C> { | pub struct ExecutorCallback<E, C> { | ||||
| @@ -34,35 +34,35 @@ impl<E, C> ExecutorCallback<E, C> { | |||||
| } | } | ||||
| } | } | ||||
| impl<E, D, C, I, R> ProducerCallback<I> for ExecutorCallback<E, C> | |||||
| impl<'a, E, D, C, I, R> ProducerCallback<'a, I> for ExecutorCallback<E, C> | |||||
| where | where | ||||
| E: Executor<D>, | |||||
| E: Executor<'a, D>, | |||||
| D: Send, | D: Send, | ||||
| C: Consumer<I, Result = D, Reducer = R>, | |||||
| R: Reducer<D>, | |||||
| C: Consumer<I, Result = D, Reducer = R> + 'a, | |||||
| R: Reducer<D> + Send, | |||||
| { | { | ||||
| type Output = E::Result; | type Output = E::Result; | ||||
| fn callback<P>(self, producer: P) -> Self::Output | fn callback<P>(self, producer: P) -> Self::Output | ||||
| where | where | ||||
| P: Producer<Item = I> | |||||
| P: Producer<Item = I> + 'a | |||||
| { | { | ||||
| self.executor.exec(producer, self.consumer) | self.executor.exec(producer, self.consumer) | ||||
| } | } | ||||
| } | } | ||||
| impl<E, D, C, I, R> IndexedProducerCallback<I> for ExecutorCallback<E, C> | |||||
| impl<'a, E, D, C, I, R> IndexedProducerCallback<'a, I> for ExecutorCallback<E, C> | |||||
| where | where | ||||
| E: Executor<D>, | |||||
| E: Executor<'a, D>, | |||||
| D: Send, | D: Send, | ||||
| C: IndexedConsumer<I, Result = D, Reducer = R>, | |||||
| R: Reducer<D>, | |||||
| C: IndexedConsumer<I, Result = D, Reducer = R> + 'a, | |||||
| R: Reducer<D> + Send, | |||||
| { | { | ||||
| type Output = E::Result; | type Output = E::Result; | ||||
| fn callback<P>(self, producer: P) -> Self::Output | fn callback<P>(self, producer: P) -> Self::Output | ||||
| where | where | ||||
| P: IndexedProducer<Item = I> | |||||
| P: IndexedProducer<Item = I> + 'a | |||||
| { | { | ||||
| self.executor.exec_indexed(producer, self.consumer) | self.executor.exec_indexed(producer, self.consumer) | ||||
| } | } | ||||
| @@ -8,9 +8,9 @@ use super::ParallelIterator; | |||||
| /// | /// | ||||
| /// [`ParallelIterator`]: trait.ParallelIterator.html | /// [`ParallelIterator`]: trait.ParallelIterator.html | ||||
| /// [`std::iter::IntoIterator`]: https://doc.rust-lang.org/std/iter/trait.IntoIterator.html | /// [`std::iter::IntoIterator`]: https://doc.rust-lang.org/std/iter/trait.IntoIterator.html | ||||
| pub trait IntoParallelIterator { | |||||
| pub trait IntoParallelIterator<'a> { | |||||
| /// The parallel iterator type that will be created. | /// The parallel iterator type that will be created. | ||||
| type Iter: ParallelIterator<Item = Self::Item>; | |||||
| type Iter: ParallelIterator<'a, Item = Self::Item>; | |||||
| /// The type of item that the parallel iterator will produce. | /// The type of item that the parallel iterator will produce. | ||||
| type Item: Send; | type Item: Send; | ||||
| @@ -53,13 +53,13 @@ pub trait IntoParallelIterator { | |||||
| /// | /// | ||||
| /// [`ParallelIterator`]: trait.ParallelIterator.html | /// [`ParallelIterator`]: trait.ParallelIterator.html | ||||
| /// [`IntoParallelIterator`]: trait.IntoParallelIterator.html | /// [`IntoParallelIterator`]: trait.IntoParallelIterator.html | ||||
| pub trait IntoParallelRefIterator<'data> { | |||||
| pub trait IntoParallelRefIterator<'a> { | |||||
| /// The type of the parallel iterator that will be returned. | /// The type of the parallel iterator that will be returned. | ||||
| type Iter: ParallelIterator<Item = Self::Item>; | |||||
| type Iter: ParallelIterator<'a, Item = Self::Item>; | |||||
| /// The type of item that the parallel iterator will produce. | /// The type of item that the parallel iterator will produce. | ||||
| /// This will typically be an `&'data T` reference type. | |||||
| type Item: Send + 'data; | |||||
| /// This will typically be an `&'a T` reference type. | |||||
| type Item: Send + 'a; | |||||
| /// Converts `self` into a parallel iterator. | /// Converts `self` into a parallel iterator. | ||||
| /// | /// | ||||
| @@ -76,7 +76,7 @@ pub trait IntoParallelRefIterator<'data> { | |||||
| /// assert!(v.par_iter().zip(&v) | /// assert!(v.par_iter().zip(&v) | ||||
| /// .all(|(a, b)| std::ptr::eq(a, b))); | /// .all(|(a, b)| std::ptr::eq(a, b))); | ||||
| /// ``` | /// ``` | ||||
| fn par_iter(&'data self) -> Self::Iter; | |||||
| fn par_iter(&'a self) -> Self::Iter; | |||||
| } | } | ||||
| /// `IntoParallelRefMutIterator` implements the conversion to a | /// `IntoParallelRefMutIterator` implements the conversion to a | ||||
| @@ -92,13 +92,13 @@ pub trait IntoParallelRefIterator<'data> { | |||||
| /// | /// | ||||
| /// [`ParallelIterator`]: trait.ParallelIterator.html | /// [`ParallelIterator`]: trait.ParallelIterator.html | ||||
| /// [`IntoParallelIterator`]: trait.IntoParallelIterator.html | /// [`IntoParallelIterator`]: trait.IntoParallelIterator.html | ||||
| pub trait IntoParallelRefMutIterator<'data> { | |||||
| pub trait IntoParallelRefMutIterator<'a> { | |||||
| /// The type of iterator that will be created. | /// The type of iterator that will be created. | ||||
| type Iter: ParallelIterator<Item = Self::Item>; | |||||
| type Iter: ParallelIterator<'a, Item = Self::Item>; | |||||
| /// The type of item that will be produced; this is typically an | /// The type of item that will be produced; this is typically an | ||||
| /// `&'data mut T` reference. | |||||
| type Item: Send + 'data; | |||||
| /// `&'a mut T` reference. | |||||
| type Item: Send + 'a; | |||||
| /// Creates the parallel iterator from `self`. | /// Creates the parallel iterator from `self`. | ||||
| /// | /// | ||||
| @@ -111,10 +111,12 @@ pub trait IntoParallelRefMutIterator<'data> { | |||||
| /// v.par_iter_mut().enumerate().for_each(|(i, x)| *x = i); | /// v.par_iter_mut().enumerate().for_each(|(i, x)| *x = i); | ||||
| /// assert_eq!(v, [0, 1, 2, 3, 4]); | /// assert_eq!(v, [0, 1, 2, 3, 4]); | ||||
| /// ``` | /// ``` | ||||
| fn par_iter_mut(&'data mut self) -> Self::Iter; | |||||
| fn par_iter_mut(&'a mut self) -> Self::Iter; | |||||
| } | } | ||||
| impl<T: ParallelIterator> IntoParallelIterator for T { | |||||
| impl<'a, T> IntoParallelIterator<'a> for T | |||||
| where T: ParallelIterator<'a> | |||||
| { | |||||
| type Iter = T; | type Iter = T; | ||||
| type Item = T::Item; | type Item = T::Item; | ||||
| @@ -123,26 +125,28 @@ impl<T: ParallelIterator> IntoParallelIterator for T { | |||||
| } | } | ||||
| } | } | ||||
| impl<'data, I: 'data + ?Sized> IntoParallelRefIterator<'data> for I | |||||
| impl<'a, I> IntoParallelRefIterator<'a> for I | |||||
| where | where | ||||
| &'data I: IntoParallelIterator, | |||||
| I: 'a + ?Sized, | |||||
| &'a I: IntoParallelIterator<'a>, | |||||
| { | { | ||||
| type Iter = <&'data I as IntoParallelIterator>::Iter; | |||||
| type Item = <&'data I as IntoParallelIterator>::Item; | |||||
| type Iter = <&'a I as IntoParallelIterator<'a>>::Iter; | |||||
| type Item = <&'a I as IntoParallelIterator<'a>>::Item; | |||||
| fn par_iter(&'data self) -> Self::Iter { | |||||
| fn par_iter(&'a self) -> Self::Iter { | |||||
| self.into_par_iter() | self.into_par_iter() | ||||
| } | } | ||||
| } | } | ||||
| impl<'data, I: 'data + ?Sized> IntoParallelRefMutIterator<'data> for I | |||||
| impl<'a, I> IntoParallelRefMutIterator<'a> for I | |||||
| where | where | ||||
| &'data mut I: IntoParallelIterator, | |||||
| I: 'a + ?Sized, | |||||
| &'a mut I: IntoParallelIterator<'a>, | |||||
| { | { | ||||
| type Iter = <&'data mut I as IntoParallelIterator>::Iter; | |||||
| type Item = <&'data mut I as IntoParallelIterator>::Item; | |||||
| type Iter = <&'a mut I as IntoParallelIterator<'a>>::Iter; | |||||
| type Item = <&'a mut I as IntoParallelIterator<'a>>::Item; | |||||
| fn par_iter_mut(&'data mut self) -> Self::Iter { | |||||
| fn par_iter_mut(&'a mut self) -> Self::Iter { | |||||
| self.into_par_iter() | self.into_par_iter() | ||||
| } | } | ||||
| } | } | ||||
| @@ -16,7 +16,7 @@ use crate::inner::{for_each::ForEach, map::Map}; | |||||
| /// | /// | ||||
| /// [iter]: index.html | /// [iter]: index.html | ||||
| /// [`IndexedParallelIterator`]: trait.IndexedParallelIterator.html | /// [`IndexedParallelIterator`]: trait.IndexedParallelIterator.html | ||||
| pub trait ParallelIterator: Sized + Send { | |||||
| pub trait ParallelIterator<'a>: Sized + Send { | |||||
| /// The type of item that this parallel iterator produces. | /// The type of item that this parallel iterator produces. | ||||
| /// For example, if you use the [`for_each`] method, this is the type of | /// For example, if you use the [`for_each`] method, this is the type of | ||||
| /// item that your closure will be invoked with. | /// item that your closure will be invoked with. | ||||
| @@ -38,10 +38,10 @@ pub trait ParallelIterator: Sized + Send { | |||||
| /// [README]: README.md | /// [README]: README.md | ||||
| fn drive<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | fn drive<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | ||||
| where | where | ||||
| E: Executor<D>, | |||||
| C: Consumer<Self::Item, Result = D, Reducer = R>, | |||||
| E: Executor<'a, D>, | |||||
| C: Consumer<Self::Item, Result = D, Reducer = R> + 'a, | |||||
| D: Send, | D: Send, | ||||
| R: Reducer<D>; | |||||
| R: Reducer<D> + Send; | |||||
| /// Internal method used to define the behavior of this parallel | /// Internal method used to define the behavior of this parallel | ||||
| /// iterator. You should not need to call this directly. | /// iterator. You should not need to call this directly. | ||||
| @@ -60,7 +60,7 @@ pub trait ParallelIterator: Sized + Send { | |||||
| /// [README]: README.md | /// [README]: README.md | ||||
| fn with_producer<CB>(self, callback: CB) -> CB::Output | fn with_producer<CB>(self, callback: CB) -> CB::Output | ||||
| where | where | ||||
| CB: ProducerCallback<Self::Item>; | |||||
| CB: ProducerCallback<'a, Self::Item>; | |||||
| /// Internal method used to define the behavior of this parallel | /// Internal method used to define the behavior of this parallel | ||||
| /// iterator. You should not need to call this directly. | /// iterator. You should not need to call this directly. | ||||
| @@ -124,7 +124,7 @@ pub trait ParallelIterator: Sized + Send { | |||||
| /// those points. | /// those points. | ||||
| /// | /// | ||||
| /// **Note:** Not implemented for `u64`, `i64`, `u128`, or `i128` ranges | /// **Note:** Not implemented for `u64`, `i64`, `u128`, or `i128` ranges | ||||
| pub trait IndexedParallelIterator: ParallelIterator { | |||||
| pub trait IndexedParallelIterator<'a>: ParallelIterator<'a> { | |||||
| /// Internal method used to define the behavior of this parallel | /// Internal method used to define the behavior of this parallel | ||||
| /// iterator. You should not need to call this directly. | /// iterator. You should not need to call this directly. | ||||
| /// | /// | ||||
| @@ -141,7 +141,7 @@ pub trait IndexedParallelIterator: ParallelIterator { | |||||
| /// [README]: README.md | /// [README]: README.md | ||||
| fn drive_indexed<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | fn drive_indexed<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | ||||
| where | where | ||||
| E: Executor<D>, | |||||
| E: Executor<'a, D>, | |||||
| C: IndexedConsumer<Self::Item, Result = D, Reducer = R>, | C: IndexedConsumer<Self::Item, Result = D, Reducer = R>, | ||||
| D: Send, | D: Send, | ||||
| R: Reducer<D>; | R: Reducer<D>; | ||||
| @@ -163,7 +163,7 @@ pub trait IndexedParallelIterator: ParallelIterator { | |||||
| /// [README]: README.md | /// [README]: README.md | ||||
| fn with_producer_indexed<CB>(self, callback: CB) -> CB::Output | fn with_producer_indexed<CB>(self, callback: CB) -> CB::Output | ||||
| where | where | ||||
| CB: IndexedProducerCallback<Self::Item>; | |||||
| CB: IndexedProducerCallback<'a, Self::Item>; | |||||
| /// Produces an exact count of how many items this iterator will | /// Produces an exact count of how many items this iterator will | ||||
| /// produce, presuming no panic occurs. | /// produce, presuming no panic occurs. | ||||
| @@ -19,6 +19,11 @@ pub trait Producer: Send + Sized { | |||||
| /// are possible. | /// are possible. | ||||
| fn into_iter(self) -> Self::IntoIter; | fn into_iter(self) -> Self::IntoIter; | ||||
| /// Number of splits/threads this iterator will use to proceed. | |||||
| fn splits(&self) -> Option<usize> { | |||||
| None | |||||
| } | |||||
| /// Split midway into a new producer if possible, otherwise return `None`. | /// Split midway into a new producer if possible, otherwise return `None`. | ||||
| fn split(self) -> (Self, Option<Self>); | fn split(self) -> (Self, Option<Self>); | ||||
| @@ -58,6 +63,7 @@ pub trait Producer: Send + Sized { | |||||
| /// | /// | ||||
| /// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md | /// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md | ||||
| /// [20671]: https://github.com/rust-lang/rust/issues/20671 | /// [20671]: https://github.com/rust-lang/rust/issues/20671 | ||||
| #[allow(clippy::len_without_is_empty)] | |||||
| pub trait IndexedProducer: Send + Sized { | pub trait IndexedProducer: Send + Sized { | ||||
| /// The type of item that will be produced by this producer once | /// The type of item that will be produced by this producer once | ||||
| /// it is converted into an iterator. | /// it is converted into an iterator. | ||||
| @@ -70,6 +76,15 @@ pub trait IndexedProducer: Send + Sized { | |||||
| /// are possible. | /// are possible. | ||||
| fn into_iter(self) -> Self::IntoIter; | fn into_iter(self) -> Self::IntoIter; | ||||
| /// Number of splits/threads this iterator will use to proceed. | |||||
| fn splits(&self) -> Option<usize> { | |||||
| None | |||||
| } | |||||
| /// Produces an exact count of how many items this producer will | |||||
| /// emit, presuming no panic occurs. | |||||
| fn len(&self) -> usize; | |||||
| /// The minimum number of items that we will process | /// The minimum number of items that we will process | ||||
| /// sequentially. Defaults to 1, which means that we will split | /// sequentially. Defaults to 1, which means that we will split | ||||
| /// all the way down to a single item. This can be raised higher | /// all the way down to a single item. This can be raised higher | ||||
| @@ -80,8 +95,8 @@ pub trait IndexedProducer: Send + Sized { | |||||
| /// needed. | /// needed. | ||||
| /// | /// | ||||
| /// [`with_min_len`]: ../trait.IndexedParallelIterator.html#method.with_min_len | /// [`with_min_len`]: ../trait.IndexedParallelIterator.html#method.with_min_len | ||||
| fn min_len(&self) -> usize { | |||||
| 1 | |||||
| fn min_len(&self) -> Option<usize> { | |||||
| None | |||||
| } | } | ||||
| /// The maximum number of items that we will process | /// The maximum number of items that we will process | ||||
| @@ -93,8 +108,8 @@ pub trait IndexedProducer: Send + Sized { | |||||
| /// overhead, so this should not be needed. | /// overhead, so this should not be needed. | ||||
| /// | /// | ||||
| /// [`with_max_len`]: ../trait.IndexedParallelIterator.html#method.with_max_len | /// [`with_max_len`]: ../trait.IndexedParallelIterator.html#method.with_max_len | ||||
| fn max_len(&self) -> usize { | |||||
| usize::MAX | |||||
| fn max_len(&self) -> Option<usize> { | |||||
| None | |||||
| } | } | ||||
| /// Split into two producers; one produces items `0..index`, the | /// Split into two producers; one produces items `0..index`, the | ||||
| @@ -119,7 +134,7 @@ pub trait IndexedProducer: Send + Sized { | |||||
| /// | /// | ||||
| /// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md#producer-callback | /// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md#producer-callback | ||||
| /// [FnOnce]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html | /// [FnOnce]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html | ||||
| pub trait ProducerCallback<I> { | |||||
| pub trait ProducerCallback<'p, I> { | |||||
| /// The type of value returned by this callback. Analogous to | /// The type of value returned by this callback. Analogous to | ||||
| /// [`Output` from the `FnOnce` trait][Output]. | /// [`Output` from the `FnOnce` trait][Output]. | ||||
| /// | /// | ||||
| @@ -131,7 +146,7 @@ pub trait ProducerCallback<I> { | |||||
| /// `P`, and hence implementors must be defined for any producer. | /// `P`, and hence implementors must be defined for any producer. | ||||
| fn callback<P>(self, producer: P) -> Self::Output | fn callback<P>(self, producer: P) -> Self::Output | ||||
| where | where | ||||
| P: Producer<Item = I>; | |||||
| P: Producer<Item = I> + 'p; | |||||
| } | } | ||||
| /// The `IndexedProducerCallback` trait is a kind of generic closure, | /// The `IndexedProducerCallback` trait is a kind of generic closure, | ||||
| @@ -140,7 +155,7 @@ pub trait ProducerCallback<I> { | |||||
| /// | /// | ||||
| /// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md#producer-callback | /// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md#producer-callback | ||||
| /// [FnOnce]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html | /// [FnOnce]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html | ||||
| pub trait IndexedProducerCallback<I> { | |||||
| pub trait IndexedProducerCallback<'p, I> { | |||||
| /// The type of value returned by this callback. Analogous to | /// The type of value returned by this callback. Analogous to | ||||
| /// [`Output` from the `FnOnce` trait][Output]. | /// [`Output` from the `FnOnce` trait][Output]. | ||||
| /// | /// | ||||
| @@ -152,5 +167,5 @@ pub trait IndexedProducerCallback<I> { | |||||
| /// `P`, and hence implementors must be defined for any producer. | /// `P`, and hence implementors must be defined for any producer. | ||||
| fn callback<P>(self, producer: P) -> Self::Output | fn callback<P>(self, producer: P) -> Self::Output | ||||
| where | where | ||||
| P: IndexedProducer<Item = I>; | |||||
| P: IndexedProducer<Item = I> + 'p; | |||||
| } | } | ||||
| @@ -1,5 +1,13 @@ | |||||
| mod sequential; | mod sequential; | ||||
| #[cfg(feature = "tokio-executor")] | |||||
| mod tokio; | |||||
| pub use sequential::Sequential as SequentialExecutor; | pub use sequential::Sequential as SequentialExecutor; | ||||
| #[cfg(feature = "tokio-executor")] | |||||
| pub use self::tokio::Tokio as TokioExecutor; | |||||
| #[cfg(feature = "tokio-executor")] | |||||
| pub type DefaultExecutor = TokioExecutor; | |||||
| #[cfg(not(feature = "tokio-executor"))] | |||||
| pub type DefaultExecutor = SequentialExecutor; | pub type DefaultExecutor = SequentialExecutor; | ||||
| @@ -6,15 +6,15 @@ use crate::core::{ | |||||
| #[derive(Default)] | #[derive(Default)] | ||||
| pub struct Sequential; | pub struct Sequential; | ||||
| impl<D> Executor<D> for Sequential | |||||
| impl<'a, D> Executor<'a, D> for Sequential | |||||
| where D: Send, | where D: Send, | ||||
| { | { | ||||
| type Result = D; | type Result = D; | ||||
| fn exec<P, C, R>(self, producer: P, consumer: C) -> Self::Result | fn exec<P, C, R>(self, producer: P, consumer: C) -> Self::Result | ||||
| where | where | ||||
| P: Producer, | |||||
| C: Consumer<P::Item, Result = D, Reducer = R>, | |||||
| P: Producer + 'a, | |||||
| C: Consumer<P::Item, Result = D, Reducer = R> + 'a, | |||||
| R: Reducer<D>, | R: Reducer<D>, | ||||
| { | { | ||||
| if consumer.is_full() { | if consumer.is_full() { | ||||
| @@ -0,0 +1,201 @@ | |||||
| use std::mem::transmute; | |||||
| use std::cmp; | |||||
| use futures::{future::{Future, BoxFuture, FutureExt}, join}; | |||||
| use tokio::task::{spawn}; | |||||
| use crate::core::{ | |||||
| Consumer, Executor, Folder, IndexedConsumer, IndexedProducer, Producer, Reducer, | |||||
| }; | |||||
| pub struct Tokio { | |||||
| splits: usize, | |||||
| } | |||||
| impl Tokio { | |||||
| pub fn new(splits: usize) -> Self { | |||||
| Self { splits } | |||||
| } | |||||
| } | |||||
| impl Default for Tokio { | |||||
| fn default() -> Self { | |||||
| Self { | |||||
| splits: 2 * num_cpus::get() | |||||
| } | |||||
| } | |||||
| } | |||||
| impl<'a, D> Executor<'a, D> for Tokio | |||||
| where D: Send, | |||||
| { | |||||
| type Result = BoxFuture<'a, D>; | |||||
| fn exec<P, C, R>(self, producer: P, consumer: C) -> Self::Result | |||||
| where | |||||
| P: Producer + 'a, | |||||
| C: Consumer<P::Item, Result = D, Reducer = R> + 'a, | |||||
| R: Reducer<D> + Send, | |||||
| { | |||||
| let splits = producer.splits().unwrap_or(self.splits); | |||||
| let splitter = Splitter::new(splits); | |||||
| exec(splitter, producer, consumer) | |||||
| } | |||||
| fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result | |||||
| where | |||||
| P: IndexedProducer + 'a, | |||||
| C: IndexedConsumer<P::Item, Result = D, Reducer = R> + 'a, | |||||
| R: Reducer<D> + Send, | |||||
| { | |||||
| let splits = producer.splits().unwrap_or(self.splits); | |||||
| let splitter = IndexedSplitter::new(splits, producer.len(), producer.min_len(), producer.max_len()); | |||||
| exec_indexed(splitter, producer, consumer) | |||||
| } | |||||
| } | |||||
| fn exec<'a, P, C>(mut splitter: Splitter, producer: P, consumer: C) -> BoxFuture<'a, C::Result> | |||||
| where | |||||
| P: Producer + 'a, | |||||
| C: Consumer<P::Item> + 'a, | |||||
| C::Reducer: Send, | |||||
| { | |||||
| async move { | |||||
| if consumer.is_full() { | |||||
| consumer.into_folder().complete() | |||||
| } else if splitter.try_split() { | |||||
| match producer.split() { | |||||
| (left_producer, Some(right_producer)) => { | |||||
| let ((left_consumer, reducer), right_consumer) = | |||||
| (consumer.split_off_left(), consumer); | |||||
| let left = run_as_task(exec(splitter, left_producer, left_consumer)); | |||||
| let right = run_as_task(exec(splitter, right_producer, right_consumer)); | |||||
| let (left_result, right_result) = join!(left, right); | |||||
| reducer.reduce(left_result, right_result) | |||||
| } | |||||
| (producer, None) => producer.fold_with(consumer.into_folder()).complete(), | |||||
| } | |||||
| } else { | |||||
| producer.fold_with(consumer.into_folder()).complete() | |||||
| } | |||||
| } | |||||
| .boxed() | |||||
| } | |||||
| fn exec_indexed<'a, P, C>(mut splitter: IndexedSplitter, producer: P, consumer: C) -> BoxFuture<'a, C::Result> | |||||
| where | |||||
| P: IndexedProducer + 'a, | |||||
| C: IndexedConsumer<P::Item> + 'a, | |||||
| C::Reducer: Send, | |||||
| { | |||||
| async move { | |||||
| if consumer.is_full() { | |||||
| consumer.into_folder().complete() | |||||
| } else { | |||||
| let len = producer.len(); | |||||
| if splitter.try_split(len) { | |||||
| let mid = len / 2; | |||||
| let (left_producer, right_producer) = producer.split_at(mid); | |||||
| let (left_consumer, right_consumer, reducer) = consumer.split_at(mid); | |||||
| let left = run_as_task(exec_indexed(splitter, left_producer, left_consumer)); | |||||
| let right = run_as_task(exec_indexed(splitter, right_producer, right_consumer)); | |||||
| let (left_result, right_result) = join!(left, right); | |||||
| reducer.reduce(left_result, right_result) | |||||
| } else { | |||||
| producer.fold_with(consumer.into_folder()).complete() | |||||
| } | |||||
| } | |||||
| } | |||||
| .boxed() | |||||
| } | |||||
| async fn run_as_task<'a, T, F>(f: F) -> T | |||||
| where | |||||
| T: Send + 'a, | |||||
| F: Future<Output = T> + Send + 'a, | |||||
| { | |||||
| struct Pointer<T>(*mut T); | |||||
| unsafe impl<T> Send for Pointer<T> {} | |||||
| let mut result = None; | |||||
| let r = Pointer(&mut result as *mut _); | |||||
| let task: BoxFuture<'a, ()> = async move { | |||||
| unsafe { | |||||
| *r.0 = Some(f.await); | |||||
| } | |||||
| } | |||||
| .boxed(); | |||||
| let task: BoxFuture<'static, ()> = unsafe { transmute(task) }; | |||||
| spawn(task).await.expect("Error in tokio executor"); | |||||
| result.unwrap() | |||||
| } | |||||
| #[derive(Clone, Copy)] | |||||
| struct Splitter { | |||||
| splits: usize, | |||||
| } | |||||
| impl Splitter { | |||||
| #[inline] | |||||
| fn new(splits: usize) -> Self { | |||||
| Self { | |||||
| splits, | |||||
| } | |||||
| } | |||||
| #[inline] | |||||
| fn try_split(&mut self) -> bool { | |||||
| if self.splits > 0 { | |||||
| self.splits /= 2; | |||||
| true | |||||
| } else { | |||||
| false | |||||
| } | |||||
| } | |||||
| } | |||||
| #[derive(Clone, Copy)] | |||||
| struct IndexedSplitter { | |||||
| inner: Splitter, | |||||
| min: usize, | |||||
| } | |||||
| impl IndexedSplitter { | |||||
| #[inline] | |||||
| fn new(splits: usize, len: usize, min: Option<usize>, max: Option<usize>) -> Self { | |||||
| let min = min.unwrap_or_default(); | |||||
| let mut ret = Self { | |||||
| inner: Splitter::new(splits), | |||||
| min: cmp::max(min, 1), | |||||
| }; | |||||
| if let Some(max) = max { | |||||
| let min_splits = len / cmp::max(max, 1); | |||||
| if min_splits > ret.inner.splits { | |||||
| ret.inner.splits = min_splits; | |||||
| } | |||||
| } | |||||
| ret | |||||
| } | |||||
| #[inline] | |||||
| fn try_split(&mut self, len: usize) -> bool { | |||||
| len / 2 >= self.min && self.inner.try_split() | |||||
| } | |||||
| } | |||||
| @@ -17,13 +17,13 @@ impl<X, O> ForEach<X, O> | |||||
| } | } | ||||
| } | } | ||||
| impl<X, O> Driver<()> for ForEach<X, O> | |||||
| impl<'a, X, O> Driver<'a, ()> for ForEach<X, O> | |||||
| where | where | ||||
| X: ParallelIterator, | |||||
| O: Fn(X::Item) + Clone + Send, | |||||
| X: ParallelIterator<'a>, | |||||
| O: Fn(X::Item) + Clone + Send + 'a, | |||||
| { | { | ||||
| fn exec_with<E>(self, executor: E) -> E::Result | fn exec_with<E>(self, executor: E) -> E::Result | ||||
| where E: Executor<()> | |||||
| where E: Executor<'a, ()> | |||||
| { | { | ||||
| let iterator = self.iterator; | let iterator = self.iterator; | ||||
| let operation = self.operation; | let operation = self.operation; | ||||
| @@ -89,15 +89,15 @@ mod tests { | |||||
| use super::*; | use super::*; | ||||
| use crate::*; | use crate::*; | ||||
| #[test] | |||||
| fn test_for_each() { | |||||
| #[tokio::test] | |||||
| async fn test_for_each() { | |||||
| let x = (0..10usize) | let x = (0..10usize) | ||||
| .into_par_iter() | .into_par_iter() | ||||
| .map(Some) | .map(Some) | ||||
| .for_each(|j| { | .for_each(|j| { | ||||
| println!("{:?}", j); | println!("{:?}", j); | ||||
| }) | }) | ||||
| .exec(); | |||||
| .exec().await; | |||||
| dbg!(x); | dbg!(x); | ||||
| } | } | ||||
| @@ -16,20 +16,20 @@ impl<X, O> Map<X, O> { | |||||
| } | } | ||||
| } | } | ||||
| impl<X, O, T> ParallelIterator for Map<X, O> | |||||
| impl<'a, X, O, T> ParallelIterator<'a> for Map<X, O> | |||||
| where | where | ||||
| X: ParallelIterator, | |||||
| O: Fn(X::Item) -> T + Sync + Send + Copy, | |||||
| X: ParallelIterator<'a>, | |||||
| O: Fn(X::Item) -> T + Sync + Send + Copy + 'a, | |||||
| T: Send, | T: Send, | ||||
| { | { | ||||
| type Item = O::Output; | type Item = O::Output; | ||||
| fn drive<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | fn drive<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | ||||
| where | where | ||||
| E: Executor<D>, | |||||
| C: Consumer<Self::Item, Result = D, Reducer = R>, | |||||
| E: Executor<'a, D>, | |||||
| C: Consumer<Self::Item, Result = D, Reducer = R> + 'a, | |||||
| D: Send, | D: Send, | ||||
| R: Reducer<D> | |||||
| R: Reducer<D> + Send, | |||||
| { | { | ||||
| let consumer = MapConsumer::new(consumer, self.operation); | let consumer = MapConsumer::new(consumer, self.operation); | ||||
| @@ -38,7 +38,7 @@ where | |||||
| fn with_producer<CB>(self, callback: CB) -> CB::Output | fn with_producer<CB>(self, callback: CB) -> CB::Output | ||||
| where | where | ||||
| CB: ProducerCallback<Self::Item>, | |||||
| CB: ProducerCallback<'a, Self::Item>, | |||||
| { | { | ||||
| self.base.with_producer(MapCallback { | self.base.with_producer(MapCallback { | ||||
| callback, | callback, | ||||
| @@ -51,15 +51,15 @@ where | |||||
| } | } | ||||
| } | } | ||||
| impl<X, O, T> IndexedParallelIterator for Map<X, O> | |||||
| impl<'a, X, O, T> IndexedParallelIterator<'a> for Map<X, O> | |||||
| where | where | ||||
| X: IndexedParallelIterator, | |||||
| O: Fn(X::Item) -> T + Sync + Send + Copy, | |||||
| X: IndexedParallelIterator<'a>, | |||||
| O: Fn(X::Item) -> T + Sync + Send + Copy + 'a, | |||||
| T: Send, | T: Send, | ||||
| { | { | ||||
| fn drive_indexed<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | fn drive_indexed<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | ||||
| where | where | ||||
| E: Executor<D>, | |||||
| E: Executor<'a, D>, | |||||
| C: IndexedConsumer<Self::Item, Result = D, Reducer = R>, | C: IndexedConsumer<Self::Item, Result = D, Reducer = R>, | ||||
| D: Send, | D: Send, | ||||
| R: Reducer<D> | R: Reducer<D> | ||||
| @@ -71,7 +71,7 @@ where | |||||
| fn with_producer_indexed<CB>(self, callback: CB) -> CB::Output | fn with_producer_indexed<CB>(self, callback: CB) -> CB::Output | ||||
| where | where | ||||
| CB: IndexedProducerCallback<Self::Item>, | |||||
| CB: IndexedProducerCallback<'a, Self::Item>, | |||||
| { | { | ||||
| self.base.with_producer_indexed(MapCallback { | self.base.with_producer_indexed(MapCallback { | ||||
| callback, | callback, | ||||
| @@ -91,17 +91,17 @@ struct MapCallback<CB, O> { | |||||
| operation: O, | operation: O, | ||||
| } | } | ||||
| impl<I, O, T, CB> ProducerCallback<I> for MapCallback<CB, O> | |||||
| impl<'a, I, O, T, CB> ProducerCallback<'a, I> for MapCallback<CB, O> | |||||
| where | where | ||||
| CB: ProducerCallback<T>, | |||||
| O: Fn(I) -> T + Sync + Send + Copy, | |||||
| CB: ProducerCallback<'a, T>, | |||||
| O: Fn(I) -> T + Sync + Send + Copy + 'a, | |||||
| T: Send, | T: Send, | ||||
| { | { | ||||
| type Output = CB::Output; | type Output = CB::Output; | ||||
| fn callback<P>(self, base: P) -> CB::Output | fn callback<P>(self, base: P) -> CB::Output | ||||
| where | where | ||||
| P: Producer<Item = I>, | |||||
| P: Producer<Item = I> + 'a, | |||||
| { | { | ||||
| let producer = MapProducer { | let producer = MapProducer { | ||||
| base, | base, | ||||
| @@ -112,17 +112,17 @@ where | |||||
| } | } | ||||
| } | } | ||||
| impl<I, O, T, CB> IndexedProducerCallback<I> for MapCallback<CB, O> | |||||
| impl<'a, I, O, T, CB> IndexedProducerCallback<'a, I> for MapCallback<CB, O> | |||||
| where | where | ||||
| CB: IndexedProducerCallback<T>, | |||||
| O: Fn(I) -> T + Sync + Send + Copy, | |||||
| CB: IndexedProducerCallback<'a, T>, | |||||
| O: Fn(I) -> T + Sync + Send + Copy + 'a, | |||||
| T: Send, | T: Send, | ||||
| { | { | ||||
| type Output = CB::Output; | type Output = CB::Output; | ||||
| fn callback<P>(self, base: P) -> CB::Output | fn callback<P>(self, base: P) -> CB::Output | ||||
| where | where | ||||
| P: IndexedProducer<Item = I>, | |||||
| P: IndexedProducer<Item = I> + 'a, | |||||
| { | { | ||||
| let producer = MapProducer { | let producer = MapProducer { | ||||
| base, | base, | ||||
| @@ -196,11 +196,19 @@ where | |||||
| self.base.into_iter().map(self.operation) | self.base.into_iter().map(self.operation) | ||||
| } | } | ||||
| fn min_len(&self) -> usize { | |||||
| fn splits(&self) -> Option<usize> { | |||||
| self.base.splits() | |||||
| } | |||||
| fn len(&self) -> usize { | |||||
| self.base.len() | |||||
| } | |||||
| fn min_len(&self) -> Option<usize> { | |||||
| self.base.min_len() | self.base.min_len() | ||||
| } | } | ||||
| fn max_len(&self) -> usize { | |||||
| fn max_len(&self) -> Option<usize> { | |||||
| self.base.max_len() | self.base.max_len() | ||||
| } | } | ||||
| @@ -10,7 +10,7 @@ struct IterProducer { | |||||
| range: Range<usize>, | range: Range<usize>, | ||||
| } | } | ||||
| impl IntoParallelIterator for Range<usize> { | |||||
| impl<'a> IntoParallelIterator<'a> for Range<usize> { | |||||
| type Iter = Iter; | type Iter = Iter; | ||||
| type Item = usize; | type Item = usize; | ||||
| @@ -19,15 +19,15 @@ impl IntoParallelIterator for Range<usize> { | |||||
| } | } | ||||
| } | } | ||||
| impl ParallelIterator for Iter { | |||||
| impl<'a> ParallelIterator<'a> for Iter { | |||||
| type Item = usize; | type Item = usize; | ||||
| fn drive<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | fn drive<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | ||||
| where | where | ||||
| E: Executor<D>, | |||||
| C: Consumer<Self::Item, Result = D, Reducer = R>, | |||||
| E: Executor<'a, D>, | |||||
| C: Consumer<Self::Item, Result = D, Reducer = R> + 'a, | |||||
| D: Send, | D: Send, | ||||
| R: Reducer<D> | |||||
| R: Reducer<D> + Send, | |||||
| { | { | ||||
| self.with_producer(ExecutorCallback::new(executor, consumer)) | self.with_producer(ExecutorCallback::new(executor, consumer)) | ||||
| } | } | ||||
| @@ -38,7 +38,7 @@ impl ParallelIterator for Iter { | |||||
| fn with_producer<CB>(self, callback: CB) -> CB::Output | fn with_producer<CB>(self, callback: CB) -> CB::Output | ||||
| where | where | ||||
| CB: ProducerCallback<Self::Item>, | |||||
| CB: ProducerCallback<'a, Self::Item>, | |||||
| { | { | ||||
| callback.callback(IterProducer { range: self.range }) | callback.callback(IterProducer { range: self.range }) | ||||
| } | } | ||||