diff --git a/.scripts/run_benchmark.sh b/.scripts/run_benchmark.sh index 743fa5a8..1a3ce1dc 100755 --- a/.scripts/run_benchmark.sh +++ b/.scripts/run_benchmark.sh @@ -1,10 +1,10 @@ -original_bench=find +original_bench=find_iter_into_par bench=$1 sed -i "s/$original_bench/$bench/g" Cargo.toml rm -f benches/results/$bench.txt -cargo bench >> benches/results/$bench.txt +cargo bench --all-features >> benches/results/$bench.txt sed -i "s/$bench/$original_bench/g" Cargo.toml diff --git a/.scripts/run_benchmarks.sh b/.scripts/run_benchmarks.sh index 27000814..b8837623 100755 --- a/.scripts/run_benchmarks.sh +++ b/.scripts/run_benchmarks.sh @@ -1,36 +1,38 @@ -# allBenches=(collect_filter sum) - allBenches=( + chain_collect_map + chain3_collect_map + chain4_collect_map collect_filter collect_filtermap collect_flatmap collect_iter_into_par collect_long_chain - collect_map - collect_map_filter collect_map_filter_hash_set - collect_result + collect_map_filter + collect_map count_filtermap count_flatmap - count_map count_map_filter + count_map drain_vec_collect_map_filter - find find_any find_flatmap find_iter_into_par find_map_filter + find mut_for_each_iter mut_for_each_slice - reduce reduce_iter_into_par reduce_long_chain - reduce_map reduce_map_filter - sum + reduce_map + reduce + result_collect_map + result_reduce_map sum_filtermap sum_flatmap sum_map_filter + sum vec_deque_collect_map_filter vec_deque_collect_map_filter_owned ) diff --git a/Cargo.toml b/Cargo.toml index e2e5bd57..79eb1b69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "orx-parallel" -version = "3.2.0" +version = "3.3.0" edition = "2024" authors = ["orxfun "] readme = "README.md" @@ -11,33 +11,47 @@ keywords = ["parallel", "concurrency", "performance", "thread", "iterator"] categories = ["concurrency", "algorithms"] [dependencies] -orx-pseudo-default = { version = "2.1.0", default-features = false } orx-pinned-vec = { version = "3.17.0", default-features = false } -orx-fixed-vec = { version = "3.18.0", default-features = false } -orx-split-vec = { version = "3.18.0", default-features = false } -orx-pinned-concurrent-col = { version = "2.14.0", default-features = false } -orx-concurrent-bag = { version = "3.0.0", default-features = false } -orx-concurrent-ordered-bag = { version = "3.0.0", default-features = false } +orx-fixed-vec = { version = "3.19.0", default-features = false } +orx-split-vec = { version = "3.19.0", default-features = false } +orx-concurrent-iter = { version = "3.1.0", default-features = false } +orx-concurrent-bag = { version = "3.1.0", default-features = false } +orx-concurrent-ordered-bag = { version = "3.1.0", default-features = false } orx-iterable = { version = "1.3.0", default-features = false } +orx-pinned-concurrent-col = { version = "2.15.0", default-features = false } orx-priority-queue = { version = "1.7.0", default-features = false } -orx-concurrent-iter = { version = "3.1.0", default-features = false } -rayon = { version = "1.10.0", optional = true } +orx-pseudo-default = { version = "2.1.0", default-features = false } + +# optional: generic iterator +rayon = { version = "1.11.0", optional = true, default-features = false } + +# optional: thread pool +pond = { version = "0.3.1", optional = true, default-features = false } +poolite = { version = "0.7.1", optional = true, default-features = false } +rayon-core = { version = "1.13.0", optional = true, default-features = false } +scoped-pool = { version = "1.0.0", optional = true, default-features = false } +scoped_threadpool = { version = "0.1.9", optional = true, default-features = false } +yastl = { version = "0.1.2", optional = true, default-features = false } [dev-dependencies] -chrono = "0.4.39" -clap = { version = "4.5.36", features = ["derive"] } -criterion = "0.5.1" +chrono = "0.4.42" +clap = { version = "4.5.47", features = ["derive"] } +criterion = "0.7.0" orx-concurrent-option = { version = "1.5.0", default-features = false } -orx-concurrent-vec = "3.6.0" -rand = "0.9" +orx-concurrent-vec = "3.8.0" +rand = "0.9.2" rand_chacha = "0.9" -rayon = "1.10.0" +rayon = "1.11.0" test-case = "3.3.1" [[bench]] -name = "find" +name = "find_iter_into_par" harness = false +[package.metadata.docs.rs] +all-features = true + [features] -default = [] +default = ["std"] +std = [] generic_iterator = ["rayon"] diff --git a/README.md b/README.md index ef05108c..1158daf0 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ * [Fallible Parallel Iterators](#fallible-parallel-iterators) * [Using Mutable Variables](#using-mutable-variables) * [Configurations](#configurations) -* [Underlying Approach and Parallel Runners](#underlying-approach-and-parallel-runners) +* [Runner: Pools and Executors](#runner-pools-and-executors) * [Contributing](#contributing) ## Parallel Computation by Iterators @@ -420,16 +420,101 @@ This is guaranteed by the fact that both consuming computation calls and configu Additionally, maximum number of threads that can be used by parallel computations can be globally bounded by the environment variable `ORX_PARALLEL_MAX_NUM_THREADS`. Please see the corresponding [example](https://github.com/orxfun/orx-parallel/blob/main/examples/max_num_threads_config.rs) for details. -## Underlying Approach and Parallel Runners +## Runner: Pools and Executors This crate defines parallel computation by combining two basic components. -* Pulling **inputs** in parallel is achieved through [`ConcurrentIter`](https://crates.io/crates/orx-concurrent-iter). Concurrent iterator implementations are lock-free, efficient and support pull-by-chunks optimization to reduce the parallelization overhead. A thread can pull any number of inputs from the concurrent iterator every time it becomes idle. This provides the means to dynamically decide on the chunk sizes. -* Writing **outputs** in parallel is handled using thread-safe containers such as [`ConcurrentBag`](https://crates.io/crates/orx-concurrent-bag) and [`ConcurrentOrderedBag`](https://crates.io/crates/orx-concurrent-ordered-bag). Similarly, these are lock-free collections that aim for high performance collection of results. +**Pulling inputs** +* Pulling inputs in parallel is achieved through [`ConcurrentIter`](https://crates.io/crates/orx-concurrent-iter). Concurrent iterator implementations are lock-free, efficient and support pull-by-chunks optimization to reduce the parallelization overhead. A thread can pull any number of inputs from the concurrent iterator every time it becomes idle. This provides the means to dynamically decide on the chunk sizes. +* Furthermore, this allows to reduce the overhead of defining creating tasks. To illustrate, provided that the computation will be handled by `n` threads, a closure holding a reference to the input concurrent iterator is defined to represent the computation. This same closure is passed to `n` threads; i.e., `n` spawn calls are made. Each of these threads keep pulling elements from the input until the computation is completed, without requiring to define another task. -Finally, [`ParallelRunner`](https://docs.rs/orx-parallel/latest/orx_parallel/runner/trait.ParallelRunner.html) trait manages parallelization of the given computation with desired configuration. The objective of the parallel runner is to optimize the chunk sizes to solve the tradeoff between impact of heterogeneity of individual computations and overhead of parallelization. +**Writing outputs** +* When we collect results, writing outputs is handled using lock-free containers such as [`ConcurrentBag`](https://crates.io/crates/orx-concurrent-bag) and [`ConcurrentOrderedBag`](https://crates.io/crates/orx-concurrent-ordered-bag) which aim for high performance collection of results. -Since it is a trait, parallel runner is customizable. It is possible to implement and use your *own runner* by calling [`with_runner`](https://docs.rs/orx-parallel/latest/orx_parallel/trait.ParIter.html#tymethod.with_runner) transformation method on the parallel iterator. Default parallel runner targets to be efficient in general. When we have a use case with special characteristics, we can implement a `ParallelRunner` optimized for this scenario and use with the parallel iterators. +There are two main decisions to be taken while executing these components: +* how many threads do we use? +* what is the chunk size; i.e., how many input items does a thread pull each time? + +A [`ParallelRunner`](https://docs.rs/orx-parallel/latest/orx_parallel/trait.ParallelRunner) is a combination of a `ParThreadPool` and a `ParallelExecutor` that are responsible for these decisions, respectively. + +### ParThreadPool: number of threads + +[`ParThreadPool`](https://docs.rs/orx-parallel/latest/orx_parallel/trait.ParThreadPool) trait generalizes thread pools that can be used for parallel computations. This allows the parallel computation to be generic over thread pools. + +When not explicitly set, [`DefaultPool`](https://docs.rs/orx-parallel/latest/orx_parallel/type.DefaultPool) is used: +* When **std** feature is enabled, default pool is the [`StdDefaultPool`](https://docs.rs/orx-parallel/latest/orx_parallel/struct.StdDefaultPool). In other words, all available native threads can be used by the parallel computation. This number can globally bounded by "ORX_PARALLEL_MAX_NUM_THREADS" environment variable when set. +* When working in a **no-std** environment, default pool is the [`SequentialPool`](https://docs.rs/orx-parallel/latest/orx_parallel/struct.SequentialPool). As the name suggests, this pool executes the parallel computation sequentially on the main thread. It can be considered as a placeholder to be overwritten by `with_pool` or `with_runner` methods to achieve parallelism. + +*Note that thread pool defines the resource, or upper bound. This upper bound can further be bounded by the [`num_threads`](https://docs.rs/orx-parallel/latest/orx_parallel/trait.ParIter.html#tymethod.num_threads) configuration. Finally, parallel executor might choose not to use all available threads if it decides that the computation is small enough.* + +To overwrite the defaults and explicitly set the thread pool to be used for the computation, [`with_pool`](https://docs.rs/orx-parallel/latest/orx_parallel/trait.ParIter.html#tymethod.with_pool) or [`with_runner`](https://docs.rs/orx-parallel/latest/orx_parallel/trait.ParIter.html#tymethod.with_runner) methods are used. + +```rust +use orx_parallel::*; + +let inputs: Vec<_> = (0..42).collect(); + +// uses the DefaultPool +// assuming "std" enabled, StdDefaultPool will be used; i.e., native threads +let sum = inputs.par().sum(); + +// equivalent to: +let sum2 = inputs.par().with_pool(StdDefaultPool::default()).sum(); +assert_eq!(sum, sum2); + +#[cfg(feature = "scoped_threadpool")] +{ + let mut pool = scoped_threadpool::Pool::new(8); + // uses the scoped_threadpool::Pool created with 8 threads + let sum2 = inputs.par().with_pool(&mut pool).sum(); + assert_eq!(sum, sum2); +} + +#[cfg(feature = "rayon-core")] +{ + let pool = rayon_core::ThreadPoolBuilder::new() + .num_threads(8) + .build() + .unwrap(); + // uses the rayon-core::ThreadPool created with 8 threads + let sum2 = inputs.par().with_pool(&pool).sum(); + assert_eq!(sum, sum2); +} + +#[cfg(feature = "yastl")] +{ + let pool = YastlPool::new(8); + // uses the yastl::Pool created with 8 threads + let sum2 = inputs.par().with_pool(&pool).sum(); + assert_eq!(sum, sum2); +} +``` + +`ParThreadPool` implementations of several thread pools are provided in this crate as optional features (see [features](#features) section). Provided that the pool supports scoped computations, it is trivial to implement this trait in most cases (see [implementations](https://github.com/orxfun/orx-parallel/tree/main/src/runner/implementations) for examples). + +In most of the cases, *rayon-core*, *scoped_threadpool* and *scoped_pool* perform better than others, and get close to native threads performance with `StdDefaultPool`. + +Since parallel computations are generic over the thread pools, performances can be conveniently compared for specific use cases. Such an example benchmark can be found in [collect_filter_map](https://github.com/orxfun/orx-parallel/blob/main/benches/collect_filter_map.rs) file. To have quick tests, you may also use the example [benchmark_pools](https://github.com/orxfun/orx-parallel/blob/main/examples/benchmark_pools.rs). + +### ParallelExecutor: chunk size + +Once thread pool provides the computation resources, it is [`ParallelExecutor`](https://docs.rs/orx-parallel/latest/orx_parallel/trait.ParallelExecutor)'s task to distribute work to available threads. As mentioned above, all threads receive exactly the same closure. This closure continues to pull elements from the input concurrent iterator and operate on the inputs until all elements are processed. + +The critical decision that parallel executor makes is the chunk size. Depending on the state of the computation, it can dynamically decide on number of elements to pull from the input iterator. The tradeoff it tries to solve is as follows: + +* the larger the chunk size, + * the smaller the parallelization overhead; but also + * the larger the risk of imbalance in cases of heterogeneity. + +## Features + +* **std**: This is a **no-std** crate while *std* is included as a default feature. Please use `--no-default-features` flag for no-std use cases. **std** feature enables `StdDefaultPool` as the default thread provider which uses native threads. +* **rayon-core**: This feature enables using `rayon_core::ThreadPool` for parallel computations. +* **scoped_threadpool**: This feature enables using `scoped_threadpool::Pool`. +* **scoped-pool**: This feature enables using `scoped-pool::Pool`. +* **yastl**: This feature enables using `yastl::Pool`. +* **pond**: This feature enables using `pond::Pool`. +* **poolite**: This feature enables using `poolite::Pool`. ## Contributing @@ -439,7 +524,8 @@ Please open an [issue](https://github.com/orxfun/orx-parallel/issues/new) or cre * if you notice an error, * have a question or think something could be improved, -* have an input collection or generator that needs to be parallelized, or +* have an input collection or generator that needs to be parallelized, +* want to use a particular thread pool with parallel iterators, * having trouble representing a particular parallel computation with parallel iterators, * or anything else:) diff --git a/benches/chain3_collect_map.rs b/benches/chain3_collect_map.rs index 2d1acf24..1c46573c 100644 --- a/benches/chain3_collect_map.rs +++ b/benches/chain3_collect_map.rs @@ -1,9 +1,10 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use orx_split_vec::SplitVec; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/chain4_collect_map.rs b/benches/chain4_collect_map.rs index 6cdce5e1..af4bb5c3 100644 --- a/benches/chain4_collect_map.rs +++ b/benches/chain4_collect_map.rs @@ -1,9 +1,10 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use orx_split_vec::SplitVec; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/chain_collect_map.rs b/benches/chain_collect_map.rs index 2e7d9d9f..b1196048 100644 --- a/benches/chain_collect_map.rs +++ b/benches/chain_collect_map.rs @@ -1,9 +1,10 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use orx_split_vec::SplitVec; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/collect_filter.rs b/benches/collect_filter.rs index 28ad18e4..07befaca 100644 --- a/benches/collect_filter.rs +++ b/benches/collect_filter.rs @@ -1,9 +1,10 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use orx_split_vec::SplitVec; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; @@ -83,6 +84,11 @@ fn orx_into_split_vec(inputs: &[Output]) -> SplitVec<&Output> { inputs.into_par().filter(filter).collect() } +#[allow(dead_code)] +fn orx_into_vec_with(inputs: &[Output], pool: P) -> Vec<&Output> { + inputs.into_par().with_pool(pool).filter(filter).collect() +} + fn run(c: &mut Criterion) { let treatments = [65_536 * 2]; @@ -113,6 +119,56 @@ fn run(c: &mut Criterion) { assert_eq!(&expected, &orx_into_split_vec(&input)); b.iter(|| orx_into_split_vec(black_box(&input))) }); + + #[cfg(feature = "rayon-core")] + group.bench_with_input( + BenchmarkId::new("orx-vec (rayon-core::ThreadPool)", n), + n, + |b, _| { + let pool = rayon_core::ThreadPoolBuilder::new() + .num_threads(32) + .build() + .unwrap(); + assert_eq!(&expected, &orx_into_vec_with(&input, &pool)); + b.iter(|| orx_into_vec_with(black_box(&input), &pool)) + }, + ); + + #[cfg(feature = "scoped-pool")] + group.bench_with_input( + BenchmarkId::new("orx-vec (scoped-pool::Pool)", n), + n, + |b, _| { + let pool = scoped_pool::Pool::new(32); + assert_eq!(&expected, &orx_into_vec_with(&input, &pool)); + b.iter(|| orx_into_vec_with(black_box(&input), &pool)) + }, + ); + + #[cfg(feature = "scoped_threadpool")] + group.bench_with_input( + BenchmarkId::new("orx-vec (scoped_threadpool::Pool)", n), + n, + |b, _| { + let pool = || scoped_threadpool::Pool::new(32); + assert_eq!(&expected, &orx_into_vec_with(&input, pool())); + b.iter(|| orx_into_vec_with(black_box(&input), pool())) + }, + ); + + #[cfg(feature = "yastl")] + group.bench_with_input(BenchmarkId::new("orx-vec (yastl::Pool)", n), n, |b, _| { + let pool = YastlPool::new(32); + assert_eq!(&expected, &orx_into_vec_with(&input, &pool)); + b.iter(|| orx_into_vec_with(black_box(&input), &pool)) + }); + + #[cfg(feature = "pond")] + group.bench_with_input(BenchmarkId::new("orx-vec (pond::Pool)", n), n, |b, _| { + let pool = || PondPool::new_threads_unbounded(32); + assert_eq!(&expected, &orx_into_vec_with(&input, pool())); + b.iter(|| orx_into_vec_with(black_box(&input), pool())) + }); } group.finish(); diff --git a/benches/collect_filtermap.rs b/benches/collect_filtermap.rs index 2d5620c5..7bacf98d 100644 --- a/benches/collect_filtermap.rs +++ b/benches/collect_filtermap.rs @@ -1,9 +1,10 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use orx_split_vec::SplitVec; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/collect_flatmap.rs b/benches/collect_flatmap.rs index b7410731..4a38b5e8 100644 --- a/benches/collect_flatmap.rs +++ b/benches/collect_flatmap.rs @@ -1,9 +1,10 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use orx_split_vec::SplitVec; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/collect_iter_into_par.rs b/benches/collect_iter_into_par.rs index 9c86f9a0..b47498c1 100644 --- a/benches/collect_iter_into_par.rs +++ b/benches/collect_iter_into_par.rs @@ -1,9 +1,10 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use orx_split_vec::SplitVec; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::ParallelBridge; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/collect_long_chain.rs b/benches/collect_long_chain.rs index 0b50311e..7607fc66 100644 --- a/benches/collect_long_chain.rs +++ b/benches/collect_long_chain.rs @@ -1,9 +1,10 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use orx_split_vec::SplitVec; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const SEED: u64 = 5426; const FIB_UPPER_BOUND: u32 = 29; diff --git a/benches/collect_map.rs b/benches/collect_map.rs index 26ca0897..a54a89e8 100644 --- a/benches/collect_map.rs +++ b/benches/collect_map.rs @@ -1,9 +1,10 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use orx_split_vec::SplitVec; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/collect_map_filter.rs b/benches/collect_map_filter.rs index d8b908ed..9f91cd84 100644 --- a/benches/collect_map_filter.rs +++ b/benches/collect_map_filter.rs @@ -1,9 +1,10 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use orx_split_vec::SplitVec; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; @@ -82,6 +83,16 @@ fn orx_into_split_vec(inputs: &[usize]) -> SplitVec { inputs.into_par().map(map).filter(filter).collect() } +#[allow(dead_code)] +fn orx_into_vec_with(inputs: &[usize], pool: P) -> Vec { + inputs + .into_par() + .with_pool(pool) + .map(map) + .filter(filter) + .collect() +} + fn run(c: &mut Criterion) { let treatments = [65_536 * 2]; @@ -112,6 +123,64 @@ fn run(c: &mut Criterion) { assert_eq!(&expected, &orx_into_split_vec(&input)); b.iter(|| orx_into_split_vec(black_box(&input))) }); + + #[cfg(feature = "rayon-core")] + group.bench_with_input( + BenchmarkId::new("orx-into-vec (rayon-core::ThreadPool)", n), + n, + |b, _| { + let pool = rayon_core::ThreadPoolBuilder::new() + .num_threads(32) + .build() + .unwrap(); + assert_eq!(&expected, &orx_into_vec_with(&input, &pool)); + b.iter(|| orx_into_vec_with(black_box(&input), &pool)) + }, + ); + + #[cfg(feature = "scoped-pool")] + group.bench_with_input( + BenchmarkId::new("orx-into-vec (scoped-pool::Pool)", n), + n, + |b, _| { + let pool = scoped_pool::Pool::new(32); + assert_eq!(&expected, &orx_into_vec_with(&input, &pool)); + b.iter(|| orx_into_vec_with(black_box(&input), &pool)) + }, + ); + + #[cfg(feature = "scoped_threadpool")] + group.bench_with_input( + BenchmarkId::new("orx-into-vec (scoped_threadpool::Pool)", n), + n, + |b, _| { + let pool = || scoped_threadpool::Pool::new(32); + assert_eq!(&expected, &orx_into_vec_with(&input, pool())); + b.iter(|| orx_into_vec_with(black_box(&input), pool())) + }, + ); + + #[cfg(feature = "yastl")] + group.bench_with_input( + BenchmarkId::new("orx-into-vec (yastl::Pool)", n), + n, + |b, _| { + let pool = YastlPool::new(32); + assert_eq!(&expected, &orx_into_vec_with(&input, &pool)); + b.iter(|| orx_into_vec_with(black_box(&input), &pool)) + }, + ); + + #[cfg(feature = "pond")] + group.bench_with_input( + BenchmarkId::new("orx-into-vec (pond::Pool)", n), + n, + |b, _| { + let pool = || PondPool::new_threads_unbounded(32); + assert_eq!(&expected, &orx_into_vec_with(&input, pool())); + b.iter(|| orx_into_vec_with(black_box(&input), pool())) + }, + ); } group.finish(); diff --git a/benches/collect_map_filter_hash_set.rs b/benches/collect_map_filter_hash_set.rs index 4f30c36c..5e3472f9 100644 --- a/benches/collect_map_filter_hash_set.rs +++ b/benches/collect_map_filter_hash_set.rs @@ -1,9 +1,10 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; use std::collections::HashSet; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/count_filtermap.rs b/benches/count_filtermap.rs index 17d6e949..a73e4d91 100644 --- a/benches/count_filtermap.rs +++ b/benches/count_filtermap.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/count_flatmap.rs b/benches/count_flatmap.rs index 32a66ebe..8a54fb35 100644 --- a/benches/count_flatmap.rs +++ b/benches/count_flatmap.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = true; diff --git a/benches/count_map.rs b/benches/count_map.rs index 81813863..78d2da47 100644 --- a/benches/count_map.rs +++ b/benches/count_map.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/count_map_filter.rs b/benches/count_map_filter.rs index 28607ce0..6e00a37d 100644 --- a/benches/count_map_filter.rs +++ b/benches/count_map_filter.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = true; diff --git a/benches/drain_vec_collect_map_filter.rs b/benches/drain_vec_collect_map_filter.rs index 4570325c..d47ff31c 100644 --- a/benches/drain_vec_collect_map_filter.rs +++ b/benches/drain_vec_collect_map_filter.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use orx_split_vec::SplitVec; use rand::prelude::*; use rand_chacha::ChaCha8Rng; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/find.rs b/benches/find.rs index 03097b79..53d87c10 100644 --- a/benches/find.rs +++ b/benches/find.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/find_any.rs b/benches/find_any.rs index 03e0dd23..16773b2a 100644 --- a/benches/find_any.rs +++ b/benches/find_any.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/find_flatmap.rs b/benches/find_flatmap.rs index a556f598..8923ab2a 100644 --- a/benches/find_flatmap.rs +++ b/benches/find_flatmap.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/find_iter_into_par.rs b/benches/find_iter_into_par.rs index 221efdef..e5d43f82 100644 --- a/benches/find_iter_into_par.rs +++ b/benches/find_iter_into_par.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::ParallelBridge; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/find_map_filter.rs b/benches/find_map_filter.rs index 892b34e6..1af9888d 100644 --- a/benches/find_map_filter.rs +++ b/benches/find_map_filter.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/mut_for_each_slice.rs b/benches/mut_for_each_slice.rs index c151d7bc..30e8549b 100644 --- a/benches/mut_for_each_slice.rs +++ b/benches/mut_for_each_slice.rs @@ -1,4 +1,5 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; +use std::hint::black_box; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] struct Data { diff --git a/benches/reduce.rs b/benches/reduce.rs index 281bd8ad..d3e8ae5d 100644 --- a/benches/reduce.rs +++ b/benches/reduce.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/reduce_iter_into_par.rs b/benches/reduce_iter_into_par.rs index 7ec709a5..817c80b5 100644 --- a/benches/reduce_iter_into_par.rs +++ b/benches/reduce_iter_into_par.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::ParallelBridge; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; @@ -92,6 +93,17 @@ fn orx(inputs: &[usize]) -> Option { .reduce(reduce) } +#[allow(dead_code)] +fn orx_with(inputs: &[usize], pool: P) -> Option { + inputs + .into_iter() + .iter_into_par() + .with_pool(pool) + .map(map) + .filter(filter) + .reduce(reduce) +} + fn run(c: &mut Criterion) { let treatments = [65_536 * 2]; @@ -115,6 +127,52 @@ fn run(c: &mut Criterion) { assert_eq!(&expected, &orx(&input)); b.iter(|| orx(black_box(&input))) }); + + #[cfg(feature = "rayon-core")] + group.bench_with_input( + BenchmarkId::new("orx (rayon-core::ThreadPool)", n), + n, + |b, _| { + let pool = rayon_core::ThreadPoolBuilder::new() + .num_threads(32) + .build() + .unwrap(); + assert_eq!(&expected, &orx_with(&input, &pool)); + b.iter(|| orx_with(black_box(&input), &pool)) + }, + ); + + #[cfg(feature = "scoped-pool")] + group.bench_with_input(BenchmarkId::new("orx (scoped-pool::Pool)", n), n, |b, _| { + let pool = scoped_pool::Pool::new(32); + assert_eq!(&expected, &orx_with(&input, &pool)); + b.iter(|| orx_with(black_box(&input), &pool)) + }); + + #[cfg(feature = "scoped_threadpool")] + group.bench_with_input( + BenchmarkId::new("orx (scoped_threadpool::Pool)", n), + n, + |b, _| { + let pool = || scoped_threadpool::Pool::new(32); + assert_eq!(&expected, &orx_with(&input, pool())); + b.iter(|| orx_with(black_box(&input), pool())) + }, + ); + + #[cfg(feature = "yastl")] + group.bench_with_input(BenchmarkId::new("orx (yastl::Pool)", n), n, |b, _| { + let pool = YastlPool::new(32); + assert_eq!(&expected, &orx_with(&input, &pool)); + b.iter(|| orx_with(black_box(&input), &pool)) + }); + + #[cfg(feature = "pond")] + group.bench_with_input(BenchmarkId::new("orx (pond::Pool)", n), n, |b, _| { + let pool = || PondPool::new_threads_unbounded(32); + assert_eq!(&expected, &orx_with(&input, pool())); + b.iter(|| orx_with(black_box(&input), pool())) + }); } group.finish(); diff --git a/benches/reduce_long_chain.rs b/benches/reduce_long_chain.rs index fa23968f..2c8415be 100644 --- a/benches/reduce_long_chain.rs +++ b/benches/reduce_long_chain.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const SEED: u64 = 5426; const FIB_UPPER_BOUND: u32 = 29; diff --git a/benches/reduce_map.rs b/benches/reduce_map.rs index e66312d5..9eff300f 100644 --- a/benches/reduce_map.rs +++ b/benches/reduce_map.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/reduce_map_filter.rs b/benches/reduce_map_filter.rs index 532d6936..287a7161 100644 --- a/benches/reduce_map_filter.rs +++ b/benches/reduce_map_filter.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/result_collect_map.rs b/benches/result_collect_map.rs index 040d0ced..833c6eb5 100644 --- a/benches/result_collect_map.rs +++ b/benches/result_collect_map.rs @@ -1,6 +1,7 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use rand::prelude::*; use rand_chacha::ChaCha8Rng; +use std::hint::black_box; use std::num::ParseIntError; type ERR = ParseIntError; diff --git a/benches/result_reduce_map.rs b/benches/result_reduce_map.rs index 6a1c7711..6dd82e91 100644 --- a/benches/result_reduce_map.rs +++ b/benches/result_reduce_map.rs @@ -1,7 +1,8 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_concurrent_option::{ConcurrentOption, IntoOption}; use rand::prelude::*; use rand_chacha::ChaCha8Rng; +use std::hint::black_box; use std::num::ParseIntError; type ERR = ParseIntError; diff --git a/benches/sum.rs b/benches/sum.rs index 0ed6fb3b..db455750 100644 --- a/benches/sum.rs +++ b/benches/sum.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const SEED: u64 = 9562; const FIB_UPPER_BOUND: u32 = 201; diff --git a/benches/sum_filtermap.rs b/benches/sum_filtermap.rs index ec77fd35..f4fb35e9 100644 --- a/benches/sum_filtermap.rs +++ b/benches/sum_filtermap.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/sum_flatmap.rs b/benches/sum_flatmap.rs index 469abcac..0e3f7478 100644 --- a/benches/sum_flatmap.rs +++ b/benches/sum_flatmap.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/sum_map_filter.rs b/benches/sum_map_filter.rs index 1d2cf7af..cccda74f 100644 --- a/benches/sum_map_filter.rs +++ b/benches/sum_map_filter.rs @@ -1,8 +1,9 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; +use std::hint::black_box; const SEED: u64 = 5426; const FIB_UPPER_BOUND: u32 = 201; diff --git a/benches/vec_deque_collect_map_filter.rs b/benches/vec_deque_collect_map_filter.rs index accb8740..9ff9b51b 100644 --- a/benches/vec_deque_collect_map_filter.rs +++ b/benches/vec_deque_collect_map_filter.rs @@ -1,10 +1,11 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use orx_split_vec::SplitVec; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; use std::collections::VecDeque; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/benches/vec_deque_collect_map_filter_owned.rs b/benches/vec_deque_collect_map_filter_owned.rs index 14235339..42e496ce 100644 --- a/benches/vec_deque_collect_map_filter_owned.rs +++ b/benches/vec_deque_collect_map_filter_owned.rs @@ -1,10 +1,11 @@ -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use orx_parallel::*; use orx_split_vec::SplitVec; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rayon::iter::IntoParallelIterator; use std::collections::VecDeque; +use std::hint::black_box; const TEST_LARGE_OUTPUT: bool = false; diff --git a/examples/benchmark_pools.rs b/examples/benchmark_pools.rs new file mode 100644 index 00000000..15e5349a --- /dev/null +++ b/examples/benchmark_pools.rs @@ -0,0 +1,264 @@ +// cargo run --all-features --release --example benchmark_pools +// to run with all options: +// +// output: +// +// Args { pool_type: All, num_threads: 16, len: 100000, num_repetitions: 1000 } +// Std => 15.912437916s +// Sequential => 46.194610858s +// Pond => 42.560279289s +// Poolite => 21.422590826s +// RayonCore => 16.227641997s +// ScopedPool => 15.958834105s +// ScopedThreadPool => 17.228307255s +// Yastl => 43.914882593s + +// cargo run --all-features --release --example benchmark_pools -- --pool-type scoped-pool +// to run only using scoped-pool +// +// output: +// +// Args { pool_type: ScopedPool, num_threads: 16, len: 100000, num_repetitions: 1000 } +// ScopedPool => 16.640308686s + +// cargo run --all-features --release --example benchmark_pools -- --pool-type rayon-core --len 1000 --num-repetitions 10000 +// to run only using rayon-core ThreadPool, with 10000 repetitions for input size of 1000 +// +// output: +// +// Args { pool_type: RayonCore, num_threads: 16, len: 1000, num_repetitions: 10000 } +// RayonCore => 6.950370104s + +mod utils; + +fn main() { + #[cfg(feature = "std")] + #[cfg(feature = "pond")] + #[cfg(feature = "poolite")] + #[cfg(feature = "rayon-core")] + #[cfg(feature = "scoped-pool")] + #[cfg(feature = "scoped_threadpool")] + #[cfg(feature = "yastl")] + { + use clap::Parser; + use orx_parallel::runner::ParallelRunner; + use orx_parallel::*; + use std::hint::black_box; + use std::num::NonZeroUsize; + use std::time::SystemTime; + + #[derive(Parser, Debug)] + struct Args { + /// Type of the thread pool to be used for computations. + #[arg(long, default_value_t, value_enum)] + pool_type: PoolType, + /// Number of threads. + #[arg(long, default_value_t = NonZeroUsize::new(16).unwrap())] + num_threads: NonZeroUsize, + /// Number of items in the input iterator. + #[arg(long, default_value_t = 100000)] + len: usize, + /// Number of repetitions to measure time; total time will be reported. + #[arg(long, default_value_t = 100)] + num_repetitions: usize, + } + + #[derive(clap::ValueEnum, Clone, Copy, Default, Debug)] + enum PoolType { + Std, + Sequential, + Pond, + Poolite, + RayonCore, + ScopedPool, + ScopedThreadPool, + Yastl, + #[default] + All, + } + + impl PoolType { + fn run_single(self, nt: usize, reps: usize, input: &[usize], expected: &[String]) { + let now = SystemTime::now(); + let result = match self { + Self::Std => run_std(nt, reps, input), + Self::Sequential => run_sequential(nt, reps, input), + Self::Pond => run_pond(nt, reps, input), + Self::Poolite => run_poolite(nt, reps, input), + Self::RayonCore => run_rayon_core(nt, reps, input), + Self::ScopedPool => run_scoped_pool(nt, reps, input), + Self::ScopedThreadPool => run_scoped_threadpool(nt, reps, input), + Self::Yastl => run_yastl(nt, reps, input), + Self::All => panic!("all is handled by run_all"), + }; + let elapsed = now.elapsed().unwrap(); + println!("{self:?} => {elapsed:?}"); + assert_eq!(expected, result); + } + + fn run_all(nt: usize, reps: usize, input: &[usize], expected: &[String]) { + Self::Std.run_single(nt, reps, input, expected); + Self::Sequential.run_single(nt, reps, input, expected); + Self::Pond.run_single(nt, reps, input, expected); + Self::Poolite.run_single(nt, reps, input, expected); + Self::RayonCore.run_single(nt, reps, input, expected); + Self::ScopedPool.run_single(nt, reps, input, expected); + Self::ScopedThreadPool.run_single(nt, reps, input, expected); + Self::Yastl.run_single(nt, reps, input, expected); + } + + fn run(self, nt: usize, reps: usize, input: &[usize], expected: &[String]) { + match self { + Self::All => Self::run_all(nt, reps, input, expected), + _ => self.run_single(nt, reps, input, expected), + } + } + } + + fn run_with_runner( + mut runner: R, + num_threads: usize, + num_repetitions: usize, + input: &[usize], + ) -> Vec { + let mut dummy = vec![]; + let mut result = vec![]; + for i in 0..num_repetitions { + result = black_box( + input + .par() + .num_threads(num_threads) + .with_runner(&mut runner) + .flat_map(|x| { + [ + *x, + fibonacci(x % 10), + fibonacci(x % 21), + fibonacci(x % 17), + fibonacci(x % 33), + fibonacci(x % 21), + ] + }) + .map(|x| 3 * x) + .filter(|x| !(100..150).contains(x)) + .map(|x| x.to_string()) + .collect(), + ); + if i < num_repetitions.min(result.len()) { + dummy.push(result[i].clone()) + }; + } + for i in 0..dummy.len() { + assert_eq!(&dummy[i], &result[i]); + } + result + } + + fn fibonacci(n: usize) -> usize { + let mut a = 0; + let mut b = 1; + for _ in 0..n { + let c = a + b; + a = b; + b = c; + } + a + } + + fn run_std(num_threads: usize, num_repetitions: usize, input: &[usize]) -> Vec { + let mut runner = DefaultRunner::default(); // StdRunner + run_with_runner(&mut runner, num_threads, num_repetitions, input) + } + + fn run_sequential( + num_threads: usize, + num_repetitions: usize, + input: &[usize], + ) -> Vec { + let mut runner = RunnerWithPool::from(SequentialPool); + run_with_runner(&mut runner, num_threads, num_repetitions, input) + } + + fn run_pond(num_threads: usize, num_repetitions: usize, input: &[usize]) -> Vec { + let mut pool = PondPool::new_threads_unbounded(num_threads); + let mut runner = RunnerWithPool::from(&mut pool); + run_with_runner(&mut runner, num_threads, num_repetitions, input) + } + + fn run_poolite(num_threads: usize, num_repetitions: usize, input: &[usize]) -> Vec { + let pool = poolite::Pool::with_builder( + poolite::Builder::new().min(num_threads).max(num_threads), + ) + .unwrap(); + let mut runner = RunnerWithPool::from(&pool); + run_with_runner(&mut runner, num_threads, num_repetitions, input) + } + + fn run_rayon_core( + num_threads: usize, + num_repetitions: usize, + input: &[usize], + ) -> Vec { + let pool = rayon_core::ThreadPoolBuilder::new() + .num_threads(num_threads) + .build() + .unwrap(); + let mut runner = RunnerWithPool::from(&pool); + run_with_runner(&mut runner, num_threads, num_repetitions, input) + } + + fn run_scoped_pool( + num_threads: usize, + num_repetitions: usize, + input: &[usize], + ) -> Vec { + let pool = scoped_pool::Pool::new(num_threads); + let mut runner = RunnerWithPool::from(&pool); + run_with_runner(&mut runner, num_threads, num_repetitions, input) + } + + fn run_scoped_threadpool( + num_threads: usize, + num_repetitions: usize, + input: &[usize], + ) -> Vec { + let mut pool = scoped_threadpool::Pool::new(num_threads as u32); + let mut runner = RunnerWithPool::from(&mut pool); + run_with_runner(&mut runner, num_threads, num_repetitions, input) + } + + fn run_yastl(num_threads: usize, num_repetitions: usize, input: &[usize]) -> Vec { + let pool = YastlPool::new(num_threads); + let mut runner = RunnerWithPool::from(&pool); + run_with_runner(&mut runner, num_threads, num_repetitions, input) + } + + let args = Args::parse(); + println!("{args:?}"); + + let input: Vec<_> = (0..args.len as usize).collect::>(); + let expected: Vec<_> = input + .iter() + .flat_map(|x| { + [ + *x, + fibonacci(x % 10), + fibonacci(x % 21), + fibonacci(x % 17), + fibonacci(x % 33), + fibonacci(x % 21), + ] + }) + .map(|x| 3 * x) + .filter(|x| !(100..150).contains(x)) + .map(|x| x.to_string()) + .collect(); + + args.pool_type.run( + args.num_threads.into(), + args.num_repetitions, + &input, + &expected, + ); + } +} diff --git a/examples/max_num_threads_config.rs b/examples/max_num_threads_config.rs index 0225636e..9f618b86 100644 --- a/examples/max_num_threads_config.rs +++ b/examples/max_num_threads_config.rs @@ -50,7 +50,7 @@ fn main() { } } - let n = 1 << 30; + let n = 1 << 31; let input = 0..n; // default -> might use all threads diff --git a/examples/using_metrics.rs b/examples/using_metrics.rs index 2f13d41a..8601ea2d 100644 --- a/examples/using_metrics.rs +++ b/examples/using_metrics.rs @@ -43,8 +43,10 @@ impl ComputationMetrics { } } +unsafe impl Sync for ComputationMetrics {} + impl ComputationMetrics { - unsafe fn create_for_thread<'a>(&mut self, thread_idx: usize) -> ThreadMetricsWriter<'a> { + unsafe fn create_for_thread<'a>(&self, thread_idx: usize) -> ThreadMetricsWriter<'a> { // SAFETY: here we create a mutable variable to the thread_idx-th metrics // * If we call this method multiple times with the same index, // we create multiple mutable references to the same ThreadMetrics, @@ -72,6 +74,8 @@ fn main() { .par() // SAFETY: we do not call `create_for_thread` externally; // it is safe if it is called only by the parallel computation. + // Since we unsafely implement Sync for ComputationMetrics, + // we must ensure that ComputationMetrics is not used elsewhere. .using(|t| unsafe { metrics.create_for_thread(t) }) .map(|m: &mut ThreadMetricsWriter<'_>, i| { // collect some useful metrics diff --git a/src/collect_into/collect.rs b/src/collect_into/collect.rs new file mode 100644 index 00000000..a33c810d --- /dev/null +++ b/src/collect_into/collect.rs @@ -0,0 +1,176 @@ +use crate::Params; +use crate::executor::parallel_compute as prc; +use crate::generic_values::runner_results::{ + Fallibility, Infallible, ParallelCollect, ParallelCollectArbitrary, Stop, +}; +use crate::runner::{NumSpawned, ParallelRunner}; +use crate::{IterationOrder, generic_values::Values}; +use orx_concurrent_iter::ConcurrentIter; +use orx_fixed_vec::IntoConcurrentPinnedVec; + +pub fn map_collect_into( + orchestrator: R, + params: Params, + iter: I, + map1: M1, + pinned_vec: P, +) -> (NumSpawned, P) +where + R: ParallelRunner, + I: ConcurrentIter, + M1: Fn(I::Item) -> O + Sync, + O: Send, + P: IntoConcurrentPinnedVec, +{ + match (params.is_sequential(), params.iteration_order) { + (true, _) => ( + NumSpawned::zero(), + map_collect_into_seq(iter, map1, pinned_vec), + ), + #[cfg(test)] + (false, IterationOrder::Arbitrary) => { + prc::collect_arbitrary::m(orchestrator, params, iter, map1, pinned_vec) + } + (false, _) => prc::collect_ordered::m(orchestrator, params, iter, map1, pinned_vec), + } +} + +fn map_collect_into_seq(iter: I, map1: M1, mut pinned_vec: P) -> P +where + I: ConcurrentIter, + M1: Fn(I::Item) -> O + Sync, + O: Send, + P: IntoConcurrentPinnedVec, +{ + let iter = iter.into_seq_iter(); + for i in iter { + pinned_vec.push(map1(i)); + } + pinned_vec +} + +pub fn xap_collect_into( + orchestrator: R, + params: Params, + iter: I, + xap1: X1, + pinned_vec: P, +) -> (NumSpawned, P) +where + R: ParallelRunner, + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + X1: Fn(I::Item) -> Vo + Sync, + P: IntoConcurrentPinnedVec, +{ + match (params.is_sequential(), params.iteration_order) { + (true, _) => ( + NumSpawned::zero(), + xap_collect_into_seq(iter, xap1, pinned_vec), + ), + (false, IterationOrder::Arbitrary) => { + let (num_threads, result) = + prc::collect_arbitrary::x(orchestrator, params, iter, xap1, pinned_vec); + let pinned_vec = match result { + ParallelCollectArbitrary::AllOrUntilWhileCollected { pinned_vec } => pinned_vec, + }; + (num_threads, pinned_vec) + } + (false, IterationOrder::Ordered) => { + let (num_threads, result) = + prc::collect_ordered::x(orchestrator, params, iter, xap1, pinned_vec); + let pinned_vec = match result { + ParallelCollect::AllCollected { pinned_vec } => pinned_vec, + ParallelCollect::StoppedByWhileCondition { + pinned_vec, + stopped_idx: _, + } => pinned_vec, + }; + (num_threads, pinned_vec) + } + } +} + +fn xap_collect_into_seq(iter: I, xap1: X1, mut pinned_vec: P) -> P +where + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + X1: Fn(I::Item) -> Vo + Sync, + P: IntoConcurrentPinnedVec, +{ + let iter = iter.into_seq_iter(); + for i in iter { + let vt = xap1(i); + let done = vt.push_to_pinned_vec(&mut pinned_vec); + if Vo::sequential_push_to_stop(done).is_some() { + break; + } + } + + pinned_vec +} + +pub fn xap_try_collect_into( + orchestrator: R, + params: Params, + iter: I, + xap1: X1, + pinned_vec: P, +) -> ( + NumSpawned, + Result::Error>, +) +where + R: ParallelRunner, + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + X1: Fn(I::Item) -> Vo + Sync, + P: IntoConcurrentPinnedVec, +{ + match (params.is_sequential(), params.iteration_order) { + (true, _) => ( + NumSpawned::zero(), + xap_try_collect_into_seq(iter, xap1, pinned_vec), + ), + (false, IterationOrder::Arbitrary) => { + let (nt, result) = + prc::collect_arbitrary::x(orchestrator, params, iter, xap1, pinned_vec); + (nt, result.into_result()) + } + (false, IterationOrder::Ordered) => { + let (nt, result) = + prc::collect_ordered::x(orchestrator, params, iter, xap1, pinned_vec); + (nt, result.into_result()) + } + } +} + +fn xap_try_collect_into_seq( + iter: I, + xap1: X1, + mut pinned_vec: P, +) -> Result::Error> +where + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + X1: Fn(I::Item) -> Vo + Sync, + P: IntoConcurrentPinnedVec, +{ + let iter = iter.into_seq_iter(); + for i in iter { + let vt = xap1(i); + let done = vt.push_to_pinned_vec(&mut pinned_vec); + if let Some(stop) = Vo::sequential_push_to_stop(done) { + match stop { + Stop::DueToWhile => return Ok(pinned_vec), + Stop::DueToError { error } => return Err(error), + } + } + } + + Ok(pinned_vec) +} diff --git a/src/collect_into/fixed_vec.rs b/src/collect_into/fixed_vec.rs index e248ef90..8b3905d3 100644 --- a/src/collect_into/fixed_vec.rs +++ b/src/collect_into/fixed_vec.rs @@ -1,8 +1,9 @@ use super::par_collect_into::ParCollectIntoCore; -use crate::computations::{M, X}; -use crate::generic_values::Values; +use crate::Params; use crate::generic_values::runner_results::{Fallibility, Infallible}; +use crate::generic_values::{TransformableValues, Values}; use crate::runner::ParallelRunner; +use alloc::vec::Vec; use orx_concurrent_iter::ConcurrentIter; use orx_fixed_vec::FixedVec; #[cfg(test)] @@ -19,7 +20,7 @@ where vec.into() } - fn m_collect_into(self, m: M) -> Self + fn m_collect_into(self, orchestrator: R, params: Params, iter: I, map1: M1) -> Self where R: ParallelRunner, I: ConcurrentIter, @@ -27,34 +28,42 @@ where O: Send, { let vec = Vec::from(self); - FixedVec::from(vec.m_collect_into::(m)) + FixedVec::from(vec.m_collect_into(orchestrator, params, iter, map1)) } - fn x_collect_into(self, x: X) -> Self + fn x_collect_into( + self, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, + ) -> Self where R: ParallelRunner, I: ConcurrentIter, - Vo: Values, - M1: Fn(I::Item) -> Vo + Sync, + Vo: TransformableValues, + X1: Fn(I::Item) -> Vo + Sync, { let vec = Vec::from(self); - FixedVec::from(vec.x_collect_into::(x)) + FixedVec::from(vec.x_collect_into(orchestrator, params, iter, xap1)) } - fn x_try_collect_into( + fn x_try_collect_into( self, - x: X, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, ) -> Result::Error> where R: ParallelRunner, I: ConcurrentIter, + X1: Fn(I::Item) -> Vo + Sync, Vo: Values, - M1: Fn(I::Item) -> Vo + Sync, - Self: Sized, { let vec = Vec::from(self); - let result = vec.x_try_collect_into::(x); - result.map(FixedVec::from) + vec.x_try_collect_into(orchestrator, params, iter, xap1) + .map(FixedVec::from) } // test diff --git a/src/collect_into/mod.rs b/src/collect_into/mod.rs index 9ba4ccb5..b121faf7 100644 --- a/src/collect_into/mod.rs +++ b/src/collect_into/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod collect; mod fixed_vec; mod par_collect_into; mod split_vec; diff --git a/src/collect_into/par_collect_into.rs b/src/collect_into/par_collect_into.rs index d140fed5..58d8562a 100644 --- a/src/collect_into/par_collect_into.rs +++ b/src/collect_into/par_collect_into.rs @@ -1,6 +1,6 @@ -use crate::computations::{M, X}; -use crate::generic_values::Values; +use crate::Params; use crate::generic_values::runner_results::{Fallibility, Infallible}; +use crate::generic_values::{TransformableValues, Values}; use crate::runner::ParallelRunner; use crate::using::UParCollectIntoCore; use orx_concurrent_iter::ConcurrentIter; @@ -12,27 +12,36 @@ pub trait ParCollectIntoCore: Collection { fn empty(iter_len: Option) -> Self; - fn m_collect_into(self, m: M) -> Self + fn m_collect_into(self, orchestrator: R, params: Params, iter: I, map1: M1) -> Self where R: ParallelRunner, I: ConcurrentIter, M1: Fn(I::Item) -> O + Sync; - fn x_collect_into(self, x: X) -> Self + fn x_collect_into( + self, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, + ) -> Self where R: ParallelRunner, I: ConcurrentIter, - Vo: Values, - M1: Fn(I::Item) -> Vo + Sync; + Vo: TransformableValues, + X1: Fn(I::Item) -> Vo + Sync; - fn x_try_collect_into( + fn x_try_collect_into( self, - x: X, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, ) -> Result::Error> where R: ParallelRunner, I: ConcurrentIter, - M1: Fn(I::Item) -> Vo + Sync, + X1: Fn(I::Item) -> Vo + Sync, Vo: Values, Self: Sized; diff --git a/src/collect_into/split_vec.rs b/src/collect_into/split_vec.rs index 898c9506..7d42b760 100644 --- a/src/collect_into/split_vec.rs +++ b/src/collect_into/split_vec.rs @@ -1,11 +1,10 @@ +use super::collect::{map_collect_into, xap_collect_into, xap_try_collect_into}; use super::par_collect_into::ParCollectIntoCore; -use crate::generic_values::Values; +use crate::Params; +use crate::collect_into::utils::split_vec_reserve; use crate::generic_values::runner_results::{Fallibility, Infallible}; -use crate::{ - collect_into::utils::split_vec_reserve, - computations::{M, X}, - runner::ParallelRunner, -}; +use crate::generic_values::{TransformableValues, Values}; +use crate::runner::ParallelRunner; use orx_concurrent_iter::ConcurrentIter; #[cfg(test)] use orx_pinned_vec::PinnedVec; @@ -21,47 +20,62 @@ where fn empty(iter_len: Option) -> Self { let mut vec = Self::pseudo_default(); - split_vec_reserve(&mut vec, iter_len); + split_vec_reserve(&mut vec, false, iter_len); vec } - fn m_collect_into(mut self, m: M) -> Self + fn m_collect_into( + mut self, + orchestrator: R, + params: Params, + iter: I, + map1: M1, + ) -> Self where R: ParallelRunner, I: ConcurrentIter, M1: Fn(I::Item) -> O + Sync, O: Send, { - split_vec_reserve(&mut self, m.par_len()); - let (_num_spawned, pinned_vec) = m.collect_into::(self); + split_vec_reserve(&mut self, params.is_sequential(), iter.try_get_len()); + let (_, pinned_vec) = map_collect_into(orchestrator, params, iter, map1, self); pinned_vec } - fn x_collect_into(mut self, x: X) -> Self + fn x_collect_into( + mut self, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, + ) -> Self where R: ParallelRunner, I: ConcurrentIter, - Vo: Values, - M1: Fn(I::Item) -> Vo + Sync, + Vo: TransformableValues, + X1: Fn(I::Item) -> Vo + Sync, { - split_vec_reserve(&mut self, x.par_len()); - let (_num_spawned, pinned_vec) = x.collect_into::(self); + split_vec_reserve(&mut self, params.is_sequential(), iter.try_get_len()); + let (_num_spawned, pinned_vec) = xap_collect_into(orchestrator, params, iter, xap1, self); pinned_vec } - fn x_try_collect_into( + fn x_try_collect_into( mut self, - x: X, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, ) -> Result::Error> where R: ParallelRunner, I: ConcurrentIter, + X1: Fn(I::Item) -> Vo + Sync, Vo: Values, - M1: Fn(I::Item) -> Vo + Sync, Self: Sized, { - split_vec_reserve(&mut self, x.par_len()); - let (_num_spawned, result) = x.try_collect_into::(self); + split_vec_reserve(&mut self, params.is_sequential(), iter.try_get_len()); + let (_num_spawned, result) = xap_try_collect_into(orchestrator, params, iter, xap1, self); result } diff --git a/src/collect_into/utils.rs b/src/collect_into/utils.rs index 356468c8..b569f600 100644 --- a/src/collect_into/utils.rs +++ b/src/collect_into/utils.rs @@ -1,3 +1,4 @@ +use alloc::vec::Vec; use orx_pinned_vec::PinnedVec; use orx_split_vec::{GrowthWithConstantTimeAccess, SplitVec}; @@ -20,8 +21,14 @@ where pub fn split_vec_reserve( split_vec: &mut SplitVec, - len_to_extend: Option, + is_sequential: bool, + iter_len: Option, ) { + let len_to_extend = match (is_sequential, iter_len) { + (true, _) => None, // not required to concurrent reserve when seq + (false, x) => x, + }; + match len_to_extend { None => { let capacity_bound = split_vec.capacity_bound(); diff --git a/src/collect_into/vec.rs b/src/collect_into/vec.rs index c5a98628..880d1f0c 100644 --- a/src/collect_into/vec.rs +++ b/src/collect_into/vec.rs @@ -1,9 +1,11 @@ use super::par_collect_into::ParCollectIntoCore; +use crate::Params; +use crate::collect_into::collect::map_collect_into; use crate::collect_into::utils::extend_vec_from_split; -use crate::computations::{M, X}; -use crate::generic_values::Values; use crate::generic_values::runner_results::{Fallibility, Infallible}; +use crate::generic_values::{TransformableValues, Values}; use crate::runner::ParallelRunner; +use alloc::vec::Vec; use orx_concurrent_iter::ConcurrentIter; use orx_fixed_vec::FixedVec; use orx_split_vec::SplitVec; @@ -21,53 +23,68 @@ where } } - fn m_collect_into(mut self, m: M) -> Self + fn m_collect_into( + mut self, + orchestrator: R, + params: Params, + iter: I, + map1: M1, + ) -> Self where R: ParallelRunner, I: ConcurrentIter, M1: Fn(I::Item) -> O + Sync, O: Send, { - match m.par_len() { + match iter.try_get_len() { None => { let split_vec = SplitVec::with_doubling_growth_and_max_concurrent_capacity(); - let split_vec = split_vec.m_collect_into::(m); + let split_vec = split_vec.m_collect_into(orchestrator, params, iter, map1); extend_vec_from_split(self, split_vec) } Some(len) => { self.reserve(len); let fixed_vec = FixedVec::from(self); - let (_num_spawned, fixed_vec) = m.collect_into::(fixed_vec); + let (_, fixed_vec) = map_collect_into(orchestrator, params, iter, map1, fixed_vec); Vec::from(fixed_vec) } } } - fn x_collect_into(self, x: X) -> Self + fn x_collect_into( + self, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, + ) -> Self where R: ParallelRunner, I: ConcurrentIter, - Vo: Values, - M1: Fn(I::Item) -> Vo + Sync, + Vo: TransformableValues, + X1: Fn(I::Item) -> Vo + Sync, { let split_vec = SplitVec::with_doubling_growth_and_max_concurrent_capacity(); - let split_vec = split_vec.x_collect_into::(x); + let split_vec = split_vec.x_collect_into(orchestrator, params, iter, xap1); extend_vec_from_split(self, split_vec) } - fn x_try_collect_into( + fn x_try_collect_into( self, - x: X, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, ) -> Result::Error> where R: ParallelRunner, I: ConcurrentIter, + X1: Fn(I::Item) -> Vo + Sync, Vo: Values, - M1: Fn(I::Item) -> Vo + Sync, Self: Sized, { let split_vec = SplitVec::with_doubling_growth_and_max_concurrent_capacity(); - let result = split_vec.x_try_collect_into::(x); + let result = split_vec.x_try_collect_into(orchestrator, params, iter, xap1); result.map(|split_vec| extend_vec_from_split(self, split_vec)) } diff --git a/src/computational_variants/fallible_option.rs b/src/computational_variants/fallible_option.rs index 5cec0876..7fb1a155 100644 --- a/src/computational_variants/fallible_option.rs +++ b/src/computational_variants/fallible_option.rs @@ -1,10 +1,12 @@ use crate::{ - ChunkSize, DefaultRunner, IterationOrder, NumThreads, ParCollectInto, ParIterResult, - ParallelRunner, + ChunkSize, IterationOrder, NumThreads, ParCollectInto, ParIterResult, par_iter_option::{ParIterOption, ResultIntoOption}, + runner::{DefaultRunner, ParallelRunner}, }; -use std::marker::PhantomData; +use core::marker::PhantomData; +/// A parallel iterator for which the computation either completely succeeds, +/// or fails and **early exits** with None. pub struct ParOption where R: ParallelRunner, @@ -48,8 +50,11 @@ where Self::new(self.par.iteration_order(order)) } - fn with_runner(self) -> impl ParIterOption { - ParOption::new(self.par.with_runner()) + fn with_runner( + self, + orchestrator: Q, + ) -> impl ParIterOption { + ParOption::new(self.par.with_runner(orchestrator)) } // computation transformations diff --git a/src/computational_variants/fallible_result/map_result.rs b/src/computational_variants/fallible_result/map_result.rs index 1b6e3518..2ed891a0 100644 --- a/src/computational_variants/fallible_result/map_result.rs +++ b/src/computational_variants/fallible_result/map_result.rs @@ -1,11 +1,13 @@ use crate::computational_variants::ParMap; -use crate::computations::X; +use crate::executor::parallel_compute as prc; use crate::par_iter_result::{IntoResult, ParIterResult}; use crate::runner::{DefaultRunner, ParallelRunner}; use crate::{IterationOrder, ParCollectInto, ParIter}; +use core::marker::PhantomData; use orx_concurrent_iter::ConcurrentIter; -use std::marker::PhantomData; +/// A parallel iterator for which the computation either completely succeeds, +/// or fails and **early exits** with an error. pub struct ParMapResult where R: ParallelRunner, @@ -66,10 +68,11 @@ where fn with_runner( self, + orchestrator: Q, ) -> impl ParIterResult { - let (params, iter, m1) = self.par.destruct(); + let (_, params, iter, m1) = self.par.destruct(); ParMapResult { - par: ParMap::new(params, iter, m1), + par: ParMap::new(orchestrator, params, iter, m1), phantom: PhantomData, } } @@ -82,10 +85,9 @@ where Self::Item: Send, Self::Err: Send, { - let (params, iter, m1) = self.par.destruct(); + let (orchestrator, params, iter, m1) = self.par.destruct(); let x1 = |i: I::Item| m1(i).into_result(); - let x = X::new(params, iter, x1); - output.x_try_collect_into::(x) + output.x_try_collect_into(orchestrator, params, iter, x1) } // reduce @@ -96,10 +98,9 @@ where Self::Err: Send, Reduce: Fn(Self::Item, Self::Item) -> Self::Item + Sync, { - let (params, iter, m1) = self.par.destruct(); + let (orchestrator, params, iter, m1) = self.par.destruct(); let x1 = |i: I::Item| m1(i).into_result(); - let x = X::new(params, iter, x1); - x.try_reduce::(reduce).1 + prc::reduce::x(orchestrator, params, iter, x1, reduce).1 } // early exit @@ -109,12 +110,17 @@ where Self::Item: Send, Self::Err: Send, { - let (params, iter, m1) = self.par.destruct(); + let (orchestrator, params, iter, m1) = self.par.destruct(); let x1 = |i: I::Item| m1(i).into_result(); - let x = X::new(params, iter, x1); match params.iteration_order { - IterationOrder::Ordered => x.try_next::().1, - IterationOrder::Arbitrary => x.try_next_any::().1, + IterationOrder::Ordered => { + let (_, result) = prc::next::x(orchestrator, params, iter, x1); + result.map(|x| x.map(|y| y.1)) + } + IterationOrder::Arbitrary => { + let (_, result) = prc::next_any::x(orchestrator, params, iter, x1); + result + } } } } diff --git a/src/computational_variants/fallible_result/par_result.rs b/src/computational_variants/fallible_result/par_result.rs index 3af9357a..2a6296f7 100644 --- a/src/computational_variants/fallible_result/par_result.rs +++ b/src/computational_variants/fallible_result/par_result.rs @@ -1,11 +1,13 @@ use crate::computational_variants::Par; -use crate::computations::X; +use crate::executor::parallel_compute as prc; use crate::par_iter_result::{IntoResult, ParIterResult}; use crate::runner::{DefaultRunner, ParallelRunner}; use crate::{IterationOrder, ParCollectInto, ParIter}; +use core::marker::PhantomData; use orx_concurrent_iter::ConcurrentIter; -use std::marker::PhantomData; +/// A parallel iterator for which the computation either completely succeeds, +/// or fails and **early exits** with an error. pub struct ParResult where R: ParallelRunner, @@ -63,10 +65,11 @@ where fn with_runner( self, + orchestrator: Q, ) -> impl ParIterResult { - let (params, iter) = self.par.destruct(); + let (_, params, iter) = self.par.destruct(); ParResult { - par: Par::new(params, iter), + par: Par::new(orchestrator, params, iter), phantom: PhantomData, } } @@ -79,10 +82,9 @@ where Self::Item: Send, Self::Err: Send, { - let (params, iter) = self.par.destruct(); + let (orchestrator, params, iter) = self.par.destruct(); let x1 = |i: I::Item| i.into_result(); - let x = X::new(params, iter, x1); - output.x_try_collect_into::(x) + output.x_try_collect_into(orchestrator, params, iter, x1) } // reduce @@ -93,10 +95,9 @@ where Self::Err: Send, Reduce: Fn(Self::Item, Self::Item) -> Self::Item + Sync, { - let (params, iter) = self.par.destruct(); + let (orchestrator, params, iter) = self.par.destruct(); let x1 = |i: I::Item| i.into_result(); - let x = X::new(params, iter, x1); - x.try_reduce::(reduce).1 + prc::reduce::x(orchestrator, params, iter, x1, reduce).1 } // early exit @@ -106,12 +107,17 @@ where Self::Item: Send, Self::Err: Send, { - let (params, iter) = self.par.destruct(); + let (orchestrator, params, iter) = self.par.destruct(); let x1 = |i: I::Item| i.into_result(); - let x = X::new(params, iter, x1); match params.iteration_order { - IterationOrder::Ordered => x.try_next::().1, - IterationOrder::Arbitrary => x.try_next_any::().1, + IterationOrder::Ordered => { + let (_, result) = prc::next::x(orchestrator, params, iter, x1); + result.map(|x| x.map(|y| y.1)) + } + IterationOrder::Arbitrary => { + let (_, result) = prc::next_any::x(orchestrator, params, iter, x1); + result + } } } } diff --git a/src/computational_variants/fallible_result/xap_result.rs b/src/computational_variants/fallible_result/xap_result.rs index 9ec7d152..a88a7fa8 100644 --- a/src/computational_variants/fallible_result/xap_result.rs +++ b/src/computational_variants/fallible_result/xap_result.rs @@ -1,48 +1,60 @@ use crate::computational_variants::ParXap; -use crate::computations::X; +use crate::executor::parallel_compute as prc; use crate::generic_values::TransformableValues; use crate::generic_values::runner_results::Infallible; use crate::par_iter_result::{IntoResult, ParIterResult}; use crate::runner::{DefaultRunner, ParallelRunner}; -use crate::{IterationOrder, ParCollectInto, ParIter}; +use crate::{IterationOrder, ParCollectInto, Params}; +use core::marker::PhantomData; use orx_concurrent_iter::ConcurrentIter; -use std::marker::PhantomData; -pub struct ParXapResult +/// A parallel iterator for which the computation either completely succeeds, +/// or fails and **early exits** with an error. +pub struct ParXapResult where R: ParallelRunner, I: ConcurrentIter, - Vo: TransformableValues, + Vo: TransformableValues, Vo::Item: IntoResult, - M1: Fn(I::Item) -> Vo + Sync, + X1: Fn(I::Item) -> Vo + Sync, { - par: ParXap, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, phantom: PhantomData<(T, E)>, } -impl ParXapResult +impl ParXapResult where R: ParallelRunner, I: ConcurrentIter, - Vo: TransformableValues, + Vo: TransformableValues, Vo::Item: IntoResult, - M1: Fn(I::Item) -> Vo + Sync, + X1: Fn(I::Item) -> Vo + Sync, { - pub(crate) fn new(par: ParXap) -> Self { + pub(crate) fn new(orchestrator: R, params: Params, iter: I, xap1: X1) -> Self { Self { - par, + orchestrator, + params, + iter, + xap1, phantom: PhantomData, } } + + fn destruct(self) -> (R, Params, I, X1) { + (self.orchestrator, self.params, self.iter, self.xap1) + } } -impl ParIterResult for ParXapResult +impl ParIterResult for ParXapResult where R: ParallelRunner, I: ConcurrentIter, Vo: TransformableValues, Vo::Item: IntoResult, - M1: Fn(I::Item) -> Vo + Sync, + X1: Fn(I::Item) -> Vo + Sync, { type Item = T; @@ -50,33 +62,30 @@ where type RegularItem = Vo::Item; - type RegularParIter = ParXap; + type RegularParIter = ParXap; fn con_iter_len(&self) -> Option { - self.par.con_iter().try_get_len() + self.iter.try_get_len() } fn into_regular_par(self) -> Self::RegularParIter { - self.par + let (orchestrator, params, iter, x1) = self.destruct(); + ParXap::new(orchestrator, params, iter, x1) } fn from_regular_par(regular_par: Self::RegularParIter) -> Self { - Self { - par: regular_par, - phantom: PhantomData, - } + let (orchestrator, params, iter, x1) = regular_par.destruct(); + Self::new(orchestrator, params, iter, x1) } // params transformations fn with_runner( self, + orchestrator: Q, ) -> impl ParIterResult { - let (params, iter, m1) = self.par.destruct(); - ParXapResult { - par: ParXap::new(params, iter, m1), - phantom: PhantomData, - } + let (_, params, iter, x1) = self.destruct(); + ParXapResult::new(orchestrator, params, iter, x1) } // collect @@ -86,12 +95,10 @@ where C: ParCollectInto, Self::Item: Send, Self::Err: Send, - Self::Err: Send, { - let (params, iter, x1) = self.par.destruct(); + let (orchestrator, params, iter, x1) = self.destruct(); let x1 = |i: I::Item| x1(i).map_while_ok(|x| x.into_result()); - let x = X::new(params, iter, x1); - output.x_try_collect_into::(x) + output.x_try_collect_into(orchestrator, params, iter, x1) } // reduce @@ -102,10 +109,9 @@ where Self::Err: Send, Reduce: Fn(Self::Item, Self::Item) -> Self::Item + Sync, { - let (params, iter, x1) = self.par.destruct(); + let (orchestrator, params, iter, x1) = self.destruct(); let x1 = |i: I::Item| x1(i).map_while_ok(|x| x.into_result()); - let x = X::new(params, iter, x1); - x.try_reduce::(reduce).1 + prc::reduce::x(orchestrator, params, iter, x1, reduce).1 } // early exit @@ -115,12 +121,17 @@ where Self::Item: Send, Self::Err: Send, { - let (params, iter, x1) = self.par.destruct(); + let (orchestrator, params, iter, x1) = self.destruct(); let x1 = |i: I::Item| x1(i).map_while_ok(|x| x.into_result()); - let x = X::new(params, iter, x1); match params.iteration_order { - IterationOrder::Ordered => x.try_next::().1, - IterationOrder::Arbitrary => x.try_next_any::().1, + IterationOrder::Ordered => { + let (_, result) = prc::next::x(orchestrator, params, iter, x1); + result.map(|x| x.map(|y| y.1)) + } + IterationOrder::Arbitrary => { + let (_, result) = prc::next_any::x(orchestrator, params, iter, x1); + result + } } } } diff --git a/src/computational_variants/map.rs b/src/computational_variants/map.rs index 09fff927..ecbf2316 100644 --- a/src/computational_variants/map.rs +++ b/src/computational_variants/map.rs @@ -1,16 +1,13 @@ use super::xap::ParXap; -use crate::ParIterResult; use crate::computational_variants::fallible_result::ParMapResult; +use crate::executor::parallel_compute as prc; use crate::generic_values::{Vector, WhilstAtom}; use crate::par_iter_result::IntoResult; -use crate::{ - ChunkSize, IterationOrder, NumThreads, ParCollectInto, ParIter, ParIterUsing, Params, - computations::M, - runner::{DefaultRunner, ParallelRunner}, - using::{UsingClone, UsingFun, computational_variants::UParMap}, -}; +use crate::runner::{DefaultRunner, ParallelRunner}; +use crate::using::{UParMap, UsingClone, UsingFun}; +use crate::{ChunkSize, IterationOrder, NumThreads, ParCollectInto, ParIter, Params}; +use crate::{ParIterResult, ParIterUsing}; use orx_concurrent_iter::ConcurrentIter; -use std::marker::PhantomData; /// A parallel iterator that maps inputs. pub struct ParMap @@ -19,8 +16,10 @@ where I: ConcurrentIter, M1: Fn(I::Item) -> O + Sync, { - m: M, - phantom: PhantomData, + orchestrator: R, + params: Params, + iter: I, + map1: M1, } impl ParMap @@ -29,15 +28,17 @@ where I: ConcurrentIter, M1: Fn(I::Item) -> O + Sync, { - pub(crate) fn new(params: Params, iter: I, m1: M1) -> Self { + pub(crate) fn new(orchestrator: R, params: Params, iter: I, map1: M1) -> Self { Self { - m: M::new(params, iter, m1), - phantom: PhantomData, + orchestrator, + params, + iter, + map1, } } - pub(crate) fn destruct(self) -> (Params, I, M1) { - self.m.destruct() + pub(crate) fn destruct(self) -> (R, Params, I, M1) { + (self.orchestrator, self.params, self.iter, self.map1) } } @@ -66,33 +67,33 @@ where type Item = O; fn con_iter(&self) -> &impl ConcurrentIter { - self.m.iter() + &self.iter } fn params(&self) -> Params { - self.m.params() + self.params } // params transformations fn num_threads(mut self, num_threads: impl Into) -> Self { - self.m.num_threads(num_threads); + self.params = self.params.with_num_threads(num_threads); self } fn chunk_size(mut self, chunk_size: impl Into) -> Self { - self.m.chunk_size(chunk_size); + self.params = self.params.with_chunk_size(chunk_size); self } fn iteration_order(mut self, collect: IterationOrder) -> Self { - self.m.iteration_order(collect); + self.params = self.params.with_collect_ordering(collect); self } - fn with_runner(self) -> impl ParIter { - let (params, iter, map) = self.destruct(); - ParMap::new(params, iter, map) + fn with_runner(self, orchestrator: Q) -> impl ParIter { + let (_, params, iter, map) = self.destruct(); + ParMap::new(orchestrator, params, iter, map) } // using transformations @@ -102,26 +103,26 @@ where using: F, ) -> impl ParIterUsing, R, Item = >::Item> where - U: Send + 'static, - F: FnMut(usize) -> U, + U: 'static, + F: Fn(usize) -> U + Sync, { let using = UsingFun::new(using); - let (params, iter, m1) = self.destruct(); - let m1 = move |_: &mut U, t: I::Item| m1(t); - UParMap::new(using, params, iter, m1) + let (orchestrator, params, iter, x1) = self.destruct(); + let m1 = move |_: &mut U, t: I::Item| x1(t); + UParMap::new(using, orchestrator, params, iter, m1) } fn using_clone( self, - using: U, + value: U, ) -> impl ParIterUsing, R, Item = >::Item> where - U: Clone + Send + 'static, + U: Clone + 'static, { - let using = UsingClone::new(using); - let (params, iter, m1) = self.destruct(); - let m1 = move |_: &mut U, t: I::Item| m1(t); - UParMap::new(using, params, iter, m1) + let using = UsingClone::new(value); + let (orchestrator, params, iter, x1) = self.destruct(); + let m1 = move |_: &mut U, t: I::Item| x1(t); + UParMap::new(using, orchestrator, params, iter, m1) } // computation transformations @@ -130,22 +131,22 @@ where where Map: Fn(Self::Item) -> Out + Sync, { - let (params, iter, m1) = self.destruct(); + let (orchestrator, params, iter, m1) = self.destruct(); let m1 = move |x| map(m1(x)); - ParMap::new(params, iter, m1) + ParMap::new(orchestrator, params, iter, m1) } fn filter(self, filter: Filter) -> impl ParIter where Filter: Fn(&Self::Item) -> bool + Sync, { - let (params, iter, m1) = self.destruct(); + let (orchestrator, params, iter, m1) = self.destruct(); let x1 = move |i: I::Item| { let value = m1(i); filter(&value).then_some(value) }; - ParXap::new(params, iter, x1) + ParXap::new(orchestrator, params, iter, x1) } fn flat_map(self, flat_map: FlatMap) -> impl ParIter @@ -153,27 +154,27 @@ where IOut: IntoIterator, FlatMap: Fn(Self::Item) -> IOut + Sync, { - let (params, iter, m1) = self.destruct(); + let (orchestrator, params, iter, m1) = self.destruct(); let x1 = move |i: I::Item| Vector(flat_map(m1(i))); - ParXap::new(params, iter, x1) + ParXap::new(orchestrator, params, iter, x1) } fn filter_map(self, filter_map: FilterMap) -> impl ParIter where FilterMap: Fn(Self::Item) -> Option + Sync, { - let (params, iter, m1) = self.destruct(); + let (orchestrator, params, iter, m1) = self.destruct(); let x1 = move |i: I::Item| filter_map(m1(i)); - ParXap::new(params, iter, x1) + ParXap::new(orchestrator, params, iter, x1) } fn take_while(self, take_while: While) -> impl ParIter where While: Fn(&Self::Item) -> bool + Sync, { - let (params, iter, m1) = self.destruct(); + let (orchestrator, params, iter, m1) = self.destruct(); let x1 = move |value: I::Item| WhilstAtom::new(m1(value), &take_while); - ParXap::new(params, iter, x1) + ParXap::new(orchestrator, params, iter, x1) } fn into_fallible_result(self) -> impl ParIterResult @@ -189,7 +190,8 @@ where where C: ParCollectInto, { - output.m_collect_into::(self.m) + let (orchestrator, params, iter, m1) = self.destruct(); + output.m_collect_into(orchestrator, params, iter, m1) } // reduce @@ -199,7 +201,8 @@ where Self::Item: Send, Reduce: Fn(Self::Item, Self::Item) -> Self::Item + Sync, { - self.m.reduce::(reduce).1 + let (orchestrator, params, iter, m1) = self.destruct(); + prc::reduce::m(orchestrator, params, iter, m1, reduce).1 } // early exit @@ -208,9 +211,10 @@ where where Self::Item: Send, { - match self.params().iteration_order { - IterationOrder::Ordered => self.m.next::().1, - IterationOrder::Arbitrary => self.m.next_any::().1, + let (orchestrator, params, iter, m1) = self.destruct(); + match params.iteration_order { + IterationOrder::Ordered => prc::next::m(orchestrator, params, iter, m1).1, + IterationOrder::Arbitrary => prc::next_any::m(orchestrator, params, iter, m1).1, } } } diff --git a/src/computational_variants/mod.rs b/src/computational_variants/mod.rs index eea29b12..89df0fac 100644 --- a/src/computational_variants/mod.rs +++ b/src/computational_variants/mod.rs @@ -1,8 +1,12 @@ #[cfg(test)] mod tests; -pub(crate) mod fallible_option; -mod fallible_result; +/// A parallel iterator for which the computation either completely succeeds, +/// or fails and **early exits** with None. +pub mod fallible_option; +/// A parallel iterator for which the computation either completely succeeds, +/// or fails and **early exits** with an error. +pub mod fallible_result; mod map; mod par; mod xap; diff --git a/src/computational_variants/par.rs b/src/computational_variants/par.rs index a158359e..669ee757 100644 --- a/src/computational_variants/par.rs +++ b/src/computational_variants/par.rs @@ -1,17 +1,16 @@ use super::{map::ParMap, xap::ParXap}; use crate::computational_variants::fallible_result::ParResult; +use crate::executor::parallel_compute as prc; use crate::generic_values::{Vector, WhilstAtom}; use crate::par_iter_result::IntoResult; +use crate::runner::{DefaultRunner, ParallelRunner}; +use crate::using::{UPar, UsingClone, UsingFun}; use crate::{ - ChunkSize, IterationOrder, NumThreads, ParCollectInto, ParIter, ParIterUsing, Params, - computations::{M, map_self}, - runner::{DefaultRunner, ParallelRunner}, - using::{UsingClone, UsingFun, computational_variants::UPar}, + ChunkSize, IterationOrder, NumThreads, ParCollectInto, ParIter, Params, default_fns::map_self, }; -use crate::{IntoParIter, ParIterResult}; +use crate::{IntoParIter, ParIterResult, ParIterUsing}; use orx_concurrent_iter::chain::ChainKnownLenI; use orx_concurrent_iter::{ConcurrentIter, ExactSizeConcurrentIter}; -use std::marker::PhantomData; /// A parallel iterator. pub struct Par @@ -19,9 +18,9 @@ where R: ParallelRunner, I: ConcurrentIter, { - iter: I, + orchestrator: R, params: Params, - phantom: PhantomData, + iter: I, } impl Par @@ -29,21 +28,16 @@ where R: ParallelRunner, I: ConcurrentIter, { - pub(crate) fn new(params: Params, iter: I) -> Self { + pub(crate) fn new(orchestrator: R, params: Params, iter: I) -> Self { Self { + orchestrator, iter, params, - phantom: PhantomData, } } - pub(crate) fn destruct(self) -> (Params, I) { - (self.params, self.iter) - } - - fn m(self) -> M I::Item> { - let (params, iter) = self.destruct(); - M::new(params, iter, map_self) + pub(crate) fn destruct(self) -> (R, Params, I) { + (self.orchestrator, self.params, self.iter) } } @@ -93,8 +87,8 @@ where self } - fn with_runner(self) -> impl ParIter { - Par::new(self.params, self.iter) + fn with_runner(self, orchestrator: Q) -> impl ParIter { + Par::new(orchestrator, self.params, self.iter) } // using transformations @@ -104,22 +98,24 @@ where using: F, ) -> impl ParIterUsing, R, Item = >::Item> where - U: Send + 'static, - F: FnMut(usize) -> U, + U: 'static, + F: Fn(usize) -> U + Sync, { let using = UsingFun::new(using); - UPar::new(using, self.params, self.iter) + let (orchestrator, params, iter) = self.destruct(); + UPar::new(using, orchestrator, params, iter) } fn using_clone( self, - using: U, + value: U, ) -> impl ParIterUsing, R, Item = >::Item> where - U: Clone + Send + 'static, + U: Clone + 'static, { - let using = UsingClone::new(using); - UPar::new(using, self.params, self.iter) + let using = UsingClone::new(value); + let (orchestrator, params, iter) = self.destruct(); + UPar::new(using, orchestrator, params, iter) } // computation transformations @@ -128,17 +124,17 @@ where where Map: Fn(Self::Item) -> Out + Sync, { - let (params, iter) = self.destruct(); - ParMap::new(params, iter, map) + let (orchestrator, params, iter) = self.destruct(); + ParMap::new(orchestrator, params, iter, map) } fn filter(self, filter: Filter) -> impl ParIter where Filter: Fn(&Self::Item) -> bool + Sync, { - let (params, iter) = self.destruct(); + let (orchestrator, params, iter) = self.destruct(); let x1 = move |i: Self::Item| filter(&i).then_some(i); - ParXap::new(params, iter, x1) + ParXap::new(orchestrator, params, iter, x1) } fn flat_map(self, flat_map: FlatMap) -> impl ParIter @@ -146,26 +142,26 @@ where IOut: IntoIterator, FlatMap: Fn(Self::Item) -> IOut + Sync, { - let (params, iter) = self.destruct(); - let x1 = move |i: Self::Item| Vector(flat_map(i)); // TODO: inline - ParXap::new(params, iter, x1) + let (orchestrator, params, iter) = self.destruct(); + let x1 = move |i: Self::Item| Vector(flat_map(i)); + ParXap::new(orchestrator, params, iter, x1) } fn filter_map(self, filter_map: FilterMap) -> impl ParIter where FilterMap: Fn(Self::Item) -> Option + Sync, { - let (params, iter) = self.destruct(); - ParXap::new(params, iter, filter_map) + let (orchestrator, params, iter) = self.destruct(); + ParXap::new(orchestrator, params, iter, filter_map) } fn take_while(self, take_while: While) -> impl ParIter where While: Fn(&Self::Item) -> bool + Sync, { - let (params, iter) = self.destruct(); + let (orchestrator, params, iter) = self.destruct(); let x1 = move |value: Self::Item| WhilstAtom::new(value, &take_while); - ParXap::new(params, iter, x1) + ParXap::new(orchestrator, params, iter, x1) } fn into_fallible_result(self) -> impl ParIterResult @@ -181,7 +177,8 @@ where where C: ParCollectInto, { - output.m_collect_into::(self.m()) + let (orchestrator, params, iter) = self.destruct(); + output.m_collect_into(orchestrator, params, iter, map_self) } // reduce @@ -191,15 +188,17 @@ where Self::Item: Send, Reduce: Fn(Self::Item, Self::Item) -> Self::Item + Sync, { - self.m().reduce::(reduce).1 + let (orchestrator, params, iter) = self.destruct(); + prc::reduce::m(orchestrator, params, iter, map_self, reduce).1 } // early exit fn first(self) -> Option { - match self.params().iteration_order { - IterationOrder::Ordered => self.m().next::().1, - IterationOrder::Arbitrary => self.m().next_any::().1, + let (orchestrator, params, iter) = self.destruct(); + match params.iteration_order { + IterationOrder::Ordered => prc::next::m(orchestrator, params, iter, map_self).1, + IterationOrder::Arbitrary => prc::next_any::m(orchestrator, params, iter, map_self).1, } } } @@ -232,8 +231,8 @@ where I: ExactSizeConcurrentIter, C: IntoParIter, { - let (params, iter) = self.destruct(); + let (orchestrator, params, iter) = self.destruct(); let iter = iter.chain(other.into_con_iter()); - Par::new(params, iter) + Par::new(orchestrator, params, iter) } } diff --git a/src/computational_variants/tests/copied.rs b/src/computational_variants/tests/copied.rs index 45191d68..5347cb96 100644 --- a/src/computational_variants/tests/copied.rs +++ b/src/computational_variants/tests/copied.rs @@ -1,4 +1,5 @@ use crate::{test_utils::*, *}; +use alloc::vec::Vec; use test_case::test_matrix; fn input>(n: usize) -> O { diff --git a/src/computational_variants/tests/count.rs b/src/computational_variants/tests/count.rs index 77ca8c98..0383d4f9 100644 --- a/src/computational_variants/tests/count.rs +++ b/src/computational_variants/tests/count.rs @@ -1,4 +1,7 @@ use crate::{test_utils::*, *}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use test_case::test_matrix; fn input>(n: usize) -> O { diff --git a/src/computational_variants/tests/flatten.rs b/src/computational_variants/tests/flatten.rs index ae3ba624..a2db7df3 100644 --- a/src/computational_variants/tests/flatten.rs +++ b/src/computational_variants/tests/flatten.rs @@ -1,4 +1,7 @@ use crate::{test_utils::*, *}; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use alloc::{format, vec}; use test_case::test_matrix; #[test_matrix(N, NT, CHUNK)] @@ -70,7 +73,7 @@ fn flatten_xap_filter_xap(n: &[usize], nt: &[usize], chunk: &[usize]) { }; let filter = |x: &Vec| x.len() == 2; let map = |mut x: Vec| { - x.push("abc".to_string()); + x.push("lorem".to_string()); x }; let expected: Vec<_> = input() diff --git a/src/computational_variants/tests/for_each.rs b/src/computational_variants/tests/for_each.rs index 91b78e5f..b973feb3 100644 --- a/src/computational_variants/tests/for_each.rs +++ b/src/computational_variants/tests/for_each.rs @@ -1,4 +1,7 @@ use crate::{test_utils::*, *}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use orx_concurrent_vec::ConcurrentVec; use test_case::test_matrix; diff --git a/src/computational_variants/tests/inspect.rs b/src/computational_variants/tests/inspect.rs index 2334ce7c..49783871 100644 --- a/src/computational_variants/tests/inspect.rs +++ b/src/computational_variants/tests/inspect.rs @@ -1,4 +1,7 @@ use crate::{test_utils::*, *}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use orx_concurrent_vec::ConcurrentVec; use test_case::test_matrix; diff --git a/src/computational_variants/tests/iter_consuming.rs b/src/computational_variants/tests/iter_consuming.rs index 4cece5b3..0f15424e 100644 --- a/src/computational_variants/tests/iter_consuming.rs +++ b/src/computational_variants/tests/iter_consuming.rs @@ -1,4 +1,8 @@ use crate::{test_utils::*, *}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec; +use alloc::vec::Vec; use orx_fixed_vec::FixedVec; use orx_iterable::Collection; use orx_split_vec::SplitVec; diff --git a/src/computational_variants/tests/iter_ref.rs b/src/computational_variants/tests/iter_ref.rs index 1e5cc66f..f17bf7a8 100644 --- a/src/computational_variants/tests/iter_ref.rs +++ b/src/computational_variants/tests/iter_ref.rs @@ -1,4 +1,8 @@ use crate::{collect_into::ParCollectIntoCore, test_utils::*, *}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec; +use alloc::vec::Vec; use orx_fixed_vec::FixedVec; use orx_iterable::{Collection, IntoCloningIterable}; use orx_split_vec::{Doubling, Linear, PseudoDefault, SplitVec}; diff --git a/src/computations/map/tests/collect.rs b/src/computational_variants/tests/map/collect.rs similarity index 80% rename from src/computations/map/tests/collect.rs rename to src/computational_variants/tests/map/collect.rs index 9ae4f7c7..3928e33e 100644 --- a/src/computations/map/tests/collect.rs +++ b/src/computational_variants/tests/map/collect.rs @@ -1,4 +1,8 @@ -use crate::{IterationOrder, Params, computations::map::m::M, runner::DefaultRunner}; +use crate::collect_into::collect::map_collect_into; +use crate::{IterationOrder, Params, runner::DefaultRunner}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use orx_concurrent_iter::IntoConcurrentIter; use orx_pinned_vec::PinnedVec; use orx_split_vec::SplitVec; @@ -33,9 +37,7 @@ fn m_map_collect(n: usize, nt: usize, chunk: usize, ordering: IterationOrder) { let params = Params::new(nt, chunk, ordering); let iter = input.into_con_iter(); - let m = M::new(params, iter, map); - - let (_, mut output) = m.collect_into::(output); + let (_, mut output) = map_collect_into(DefaultRunner::default(), params, iter, map, output); if !params.is_sequential() && matches!(params.iteration_order, IterationOrder::Arbitrary) { expected.sort(); diff --git a/src/computations/map/tests/find.rs b/src/computational_variants/tests/map/find.rs similarity index 72% rename from src/computations/map/tests/find.rs rename to src/computational_variants/tests/map/find.rs index 301e7bb4..ac613066 100644 --- a/src/computations/map/tests/find.rs +++ b/src/computational_variants/tests/map/find.rs @@ -1,7 +1,7 @@ -use crate::{ - DefaultRunner, Params, - computations::{map::m::M, map_self}, -}; +use crate::{Params, default_fns::map_self, executor::parallel_compute, runner::DefaultRunner}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use orx_concurrent_iter::IntoConcurrentIter; use test_case::test_matrix; @@ -22,9 +22,8 @@ fn m_find(n: usize, nt: usize, chunk: usize) { let params = Params::new(nt, chunk, Default::default()); let iter = input.into_con_iter(); - let m = M::new(params, iter, map_self); - let output = m.next::().1; + let output = parallel_compute::next::m(DefaultRunner::default(), params, iter, map_self).1; assert_eq!(expected, output); } @@ -41,8 +40,7 @@ fn m_map_find(n: usize, nt: usize, chunk: usize) { let params = Params::new(nt, chunk, Default::default()); let iter = input.into_con_iter(); - let m = M::new(params, iter, map); - let output = m.next::().1; + let output = parallel_compute::next::m(DefaultRunner::default(), params, iter, map).1; assert_eq!(expected, output); } diff --git a/src/computations/map/tests/mod.rs b/src/computational_variants/tests/map/mod.rs similarity index 100% rename from src/computations/map/tests/mod.rs rename to src/computational_variants/tests/map/mod.rs diff --git a/src/computations/map/tests/reduce.rs b/src/computational_variants/tests/map/reduce.rs similarity index 74% rename from src/computations/map/tests/reduce.rs rename to src/computational_variants/tests/map/reduce.rs index 2b8cbeba..f7985342 100644 --- a/src/computations/map/tests/reduce.rs +++ b/src/computational_variants/tests/map/reduce.rs @@ -1,8 +1,7 @@ -use crate::{ - Params, - computations::{map::m::M, map_self}, - runner::DefaultRunner, -}; +use crate::{Params, default_fns::map_self, executor::parallel_compute, runner::DefaultRunner}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use orx_concurrent_iter::IntoConcurrentIter; use test_case::test_matrix; @@ -27,8 +26,8 @@ fn m_reduce(n: usize, nt: usize, chunk: usize) { let params = Params::new(nt, chunk, Default::default()); let iter = input.into_con_iter(); - let m = M::new(params, iter, map_self); - let (_, output) = m.reduce::(reduce); + let (_, output) = + parallel_compute::reduce::m(DefaultRunner::default(), params, iter, map_self, reduce); assert_eq!(expected, output); } @@ -50,8 +49,8 @@ fn m_map_reduce(n: usize, nt: usize, chunk: usize) { let params = Params::new(nt, chunk, Default::default()); let iter = input.into_con_iter(); - let m = M::new(params, iter, map); - let (_, output) = m.reduce::(reduce); + let (_, output) = + parallel_compute::reduce::m(DefaultRunner::default(), params, iter, map, reduce); assert_eq!(expected, output); } diff --git a/src/computational_variants/tests/min_max.rs b/src/computational_variants/tests/min_max.rs index 5f56fc58..7fe87241 100644 --- a/src/computational_variants/tests/min_max.rs +++ b/src/computational_variants/tests/min_max.rs @@ -1,4 +1,6 @@ use crate::{test_utils::*, *}; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use core::cmp::Ordering; use test_case::test_matrix; diff --git a/src/computational_variants/tests/mod.rs b/src/computational_variants/tests/mod.rs index b36eec84..54f1bfce 100644 --- a/src/computational_variants/tests/mod.rs +++ b/src/computational_variants/tests/mod.rs @@ -5,8 +5,10 @@ mod for_each; mod inspect; mod iter_consuming; mod iter_ref; +mod map; mod min_max; mod range; mod slice; mod sum; mod vectors; +mod xap; diff --git a/src/computational_variants/tests/range.rs b/src/computational_variants/tests/range.rs index f67b5b19..4d7bf50d 100644 --- a/src/computational_variants/tests/range.rs +++ b/src/computational_variants/tests/range.rs @@ -1,4 +1,7 @@ use crate::{test_utils::*, *}; +use alloc::string::{String, ToString}; +use alloc::vec; +use alloc::vec::Vec; use orx_fixed_vec::FixedVec; use orx_iterable::Iterable; use orx_split_vec::SplitVec; diff --git a/src/computational_variants/tests/slice.rs b/src/computational_variants/tests/slice.rs index 6067c018..991dc196 100644 --- a/src/computational_variants/tests/slice.rs +++ b/src/computational_variants/tests/slice.rs @@ -1,4 +1,8 @@ use crate::{collect_into::ParCollectIntoCore, test_utils::*, *}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec; +use alloc::vec::Vec; use orx_fixed_vec::FixedVec; use orx_iterable::Collection; use orx_split_vec::{Doubling, Linear, PseudoDefault, SplitVec}; diff --git a/src/computational_variants/tests/sum.rs b/src/computational_variants/tests/sum.rs index f4f78ad9..b67143dc 100644 --- a/src/computational_variants/tests/sum.rs +++ b/src/computational_variants/tests/sum.rs @@ -1,4 +1,6 @@ use crate::{test_utils::*, *}; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use test_case::test_matrix; fn input>(n: usize) -> O { diff --git a/src/computational_variants/tests/vectors.rs b/src/computational_variants/tests/vectors.rs index 0a8f668b..4ce0a947 100644 --- a/src/computational_variants/tests/vectors.rs +++ b/src/computational_variants/tests/vectors.rs @@ -1,4 +1,8 @@ use crate::{test_utils::*, *}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec; +use alloc::vec::Vec; use orx_fixed_vec::FixedVec; use orx_iterable::Collection; use orx_split_vec::SplitVec; diff --git a/src/computations/xap/tests/collect.rs b/src/computational_variants/tests/xap/collect.rs similarity index 58% rename from src/computations/xap/tests/collect.rs rename to src/computational_variants/tests/xap/collect.rs index 8257386e..c08ba4a4 100644 --- a/src/computations/xap/tests/collect.rs +++ b/src/computational_variants/tests/xap/collect.rs @@ -1,5 +1,11 @@ +use crate::ParIter; +use crate::computational_variants::ParXap; use crate::generic_values::Vector; -use crate::{IterationOrder, Params, computations::X, runner::DefaultRunner}; +use crate::runner::DefaultRunner; +use crate::{IterationOrder, Params}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use orx_concurrent_iter::IntoConcurrentIter; use orx_pinned_vec::PinnedVec; use orx_split_vec::SplitVec; @@ -10,6 +16,46 @@ const N: [usize; 2] = [37, 125]; #[cfg(not(miri))] const N: [usize; 2] = [1025, 4735]; +#[test] +fn todo_panic_at_con_bag_new() { + // TODO: this code panics when ParThreadPool::map uses ConcurrentBag::new rather than ConcurrentBag::with_fixed_capacity + let n = 10; + let nt = 2; + let chunk = 1; + let ordering = IterationOrder::Arbitrary; + + let offset = 33; + + let input: Vec<_> = (0..n).map(|x| x.to_string()).collect(); + let fmap = |x: String| x.chars().map(|x| x.to_string()).collect::>(); + let xmap = |x: String| Vector(fmap(x)); + + let mut output = SplitVec::with_doubling_growth_and_max_concurrent_capacity(); + let mut expected = Vec::new(); + + for i in 0..offset { + let i = i.to_string(); + for x in fmap(i) { + output.push(x.clone()); + expected.push(x); + } + } + expected.extend(input.clone().into_iter().flat_map(&fmap)); + + let params = Params::new(nt, chunk, ordering); + let iter = input.into_con_iter(); + let x = ParXap::new(DefaultRunner::default(), params, iter, xmap); + + let mut output = x.collect_into(output); + + if !params.is_sequential() && matches!(params.iteration_order, IterationOrder::Arbitrary) { + expected.sort(); + output.sort(); + } + + assert_eq!(expected, output.to_vec()); +} + #[test_matrix( [0, 1, N[0], N[1]], [1, 4], @@ -37,9 +83,9 @@ fn x_flat_map_collect(n: usize, nt: usize, chunk: usize, ordering: IterationOrde let params = Params::new(nt, chunk, ordering); let iter = input.into_con_iter(); - let x = X::new(params, iter, xmap); + let x = ParXap::new(DefaultRunner::default(), params, iter, xmap); - let (_, mut output) = x.collect_into::(output); + let mut output = x.collect_into(output); if !params.is_sequential() && matches!(params.iteration_order, IterationOrder::Arbitrary) { expected.sort(); @@ -76,9 +122,9 @@ fn x_filter_map_collect(n: usize, nt: usize, chunk: usize, ordering: IterationOr let params = Params::new(nt, chunk, ordering); let iter = input.into_con_iter(); - let x = X::new(params, iter, xmap); + let x = ParXap::new(DefaultRunner::default(), params, iter, xmap); - let (_, mut output) = x.collect_into::(output); + let mut output = x.collect_into(output); if !params.is_sequential() && matches!(params.iteration_order, IterationOrder::Arbitrary) { expected.sort(); diff --git a/src/computations/xap/tests/find.rs b/src/computational_variants/tests/xap/find.rs similarity index 76% rename from src/computations/xap/tests/find.rs rename to src/computational_variants/tests/xap/find.rs index c57b36e3..7fbb5c00 100644 --- a/src/computations/xap/tests/find.rs +++ b/src/computational_variants/tests/xap/find.rs @@ -1,5 +1,11 @@ +use crate::ParIter; +use crate::Params; +use crate::computational_variants::ParXap; use crate::generic_values::Vector; -use crate::{DefaultRunner, Params, computations::X}; +use crate::runner::DefaultRunner; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use orx_concurrent_iter::IntoConcurrentIter; use test_case::test_matrix; @@ -22,9 +28,9 @@ fn x_flat_map_find(n: usize, nt: usize, chunk: usize) { let params = Params::new(nt, chunk, Default::default()); let iter = input.into_con_iter(); - let x = X::new(params, iter, xmap); + let x = ParXap::new(DefaultRunner::default(), params, iter, xmap); - let output = x.next::().1; + let output = x.first(); assert_eq!(expected, output); } @@ -43,9 +49,9 @@ fn x_filter_map_find(n: usize, nt: usize, chunk: usize) { let params = Params::new(nt, chunk, Default::default()); let iter = input.into_con_iter(); - let x = X::new(params, iter, xmap); + let x = ParXap::new(DefaultRunner::default(), params, iter, xmap); - let output = x.next::().1; + let output = x.first(); assert_eq!(expected, output); } diff --git a/src/computations/xap/tests/mod.rs b/src/computational_variants/tests/xap/mod.rs similarity index 100% rename from src/computations/xap/tests/mod.rs rename to src/computational_variants/tests/xap/mod.rs diff --git a/src/computations/xap/tests/reduce.rs b/src/computational_variants/tests/xap/reduce.rs similarity index 78% rename from src/computations/xap/tests/reduce.rs rename to src/computational_variants/tests/xap/reduce.rs index cff81969..bf15ae15 100644 --- a/src/computations/xap/tests/reduce.rs +++ b/src/computational_variants/tests/xap/reduce.rs @@ -1,5 +1,11 @@ +use crate::ParIter; +use crate::Params; +use crate::computational_variants::ParXap; use crate::generic_values::Vector; -use crate::{Params, computations::X, runner::DefaultRunner}; +use crate::runner::DefaultRunner; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use orx_concurrent_iter::IntoConcurrentIter; use test_case::test_matrix; @@ -26,9 +32,9 @@ fn x_flat_map_reduce(n: usize, nt: usize, chunk: usize) { let params = Params::new(nt, chunk, Default::default()); let iter = input.into_con_iter(); - let x = X::new(params, iter, xmap); + let x = ParXap::new(DefaultRunner::default(), params, iter, xmap); - let (_, output) = x.reduce::(reduce); + let output = x.reduce(reduce); assert_eq!(expected, output); } @@ -51,9 +57,9 @@ fn x_filter_map_reduce(n: usize, nt: usize, chunk: usize) { let params = Params::new(nt, chunk, Default::default()); let iter = input.into_con_iter(); - let x = X::new(params, iter, xmap); + let x = ParXap::new(DefaultRunner::default(), params, iter, xmap); - let (_, output) = x.reduce::(reduce); + let output = x.reduce(reduce); assert_eq!(expected, output); } diff --git a/src/computational_variants/xap.rs b/src/computational_variants/xap.rs index 12488f74..f8bc1aed 100644 --- a/src/computational_variants/xap.rs +++ b/src/computational_variants/xap.rs @@ -1,105 +1,106 @@ -use crate::ParIterResult; use crate::computational_variants::fallible_result::ParXapResult; +use crate::executor::parallel_compute as prc; use crate::generic_values::TransformableValues; use crate::generic_values::runner_results::Infallible; use crate::par_iter_result::IntoResult; -use crate::{ - ChunkSize, IterationOrder, NumThreads, ParCollectInto, ParIter, ParIterUsing, Params, - computations::X, - runner::{DefaultRunner, ParallelRunner}, - using::{UsingClone, UsingFun, computational_variants::UParXap}, -}; +use crate::runner::{DefaultRunner, ParallelRunner}; +use crate::using::{UParXap, UsingClone, UsingFun}; +use crate::{ChunkSize, IterationOrder, NumThreads, ParCollectInto, ParIter, Params}; +use crate::{ParIterResult, ParIterUsing}; use orx_concurrent_iter::ConcurrentIter; -use std::marker::PhantomData; /// A parallel iterator that xaps inputs. /// /// *xap* is a generalization of one-to-one map, filter-map and flat-map operations. -pub struct ParXap +pub struct ParXap where R: ParallelRunner, I: ConcurrentIter, Vo: TransformableValues, - M1: Fn(I::Item) -> Vo + Sync, + X1: Fn(I::Item) -> Vo + Sync, { - x: X, - phantom: PhantomData, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, } -impl ParXap +impl ParXap where R: ParallelRunner, I: ConcurrentIter, Vo: TransformableValues, - M1: Fn(I::Item) -> Vo + Sync, + X1: Fn(I::Item) -> Vo + Sync, { - pub(crate) fn new(params: Params, iter: I, x1: M1) -> Self { + pub(crate) fn new(orchestrator: R, params: Params, iter: I, xap1: X1) -> Self { Self { - x: X::new(params, iter, x1), - phantom: PhantomData, + orchestrator, + params, + iter, + xap1, } } - pub(crate) fn destruct(self) -> (Params, I, M1) { - self.x.destruct() + pub(crate) fn destruct(self) -> (R, Params, I, X1) { + (self.orchestrator, self.params, self.iter, self.xap1) } } -unsafe impl Send for ParXap +unsafe impl Send for ParXap where R: ParallelRunner, I: ConcurrentIter, Vo: TransformableValues, - M1: Fn(I::Item) -> Vo + Sync, + X1: Fn(I::Item) -> Vo + Sync, { } -unsafe impl Sync for ParXap +unsafe impl Sync for ParXap where R: ParallelRunner, I: ConcurrentIter, Vo: TransformableValues, - M1: Fn(I::Item) -> Vo + Sync, + X1: Fn(I::Item) -> Vo + Sync, { } -impl ParIter for ParXap +impl ParIter for ParXap where R: ParallelRunner, I: ConcurrentIter, Vo: TransformableValues, - M1: Fn(I::Item) -> Vo + Sync, + X1: Fn(I::Item) -> Vo + Sync, { type Item = Vo::Item; fn con_iter(&self) -> &impl ConcurrentIter { - self.x.iter() + &self.iter } fn params(&self) -> Params { - self.x.params() + self.params } // params transformations fn num_threads(mut self, num_threads: impl Into) -> Self { - self.x.num_threads(num_threads); + self.params = self.params.with_num_threads(num_threads); self } fn chunk_size(mut self, chunk_size: impl Into) -> Self { - self.x.chunk_size(chunk_size); + self.params = self.params.with_chunk_size(chunk_size); self } fn iteration_order(mut self, collect: IterationOrder) -> Self { - self.x.iteration_order(collect); + self.params = self.params.with_collect_ordering(collect); self } - fn with_runner(self) -> impl ParIter { - let (params, iter, map1) = self.destruct(); - ParXap::new(params, iter, map1) + fn with_runner(self, orchestrator: Q) -> impl ParIter { + let (_, params, iter, x1) = self.destruct(); + ParXap::new(orchestrator, params, iter, x1) } // using transformations @@ -109,26 +110,26 @@ where using: F, ) -> impl ParIterUsing, R, Item = >::Item> where - U: Send + 'static, - F: FnMut(usize) -> U, + U: 'static, + F: Fn(usize) -> U + Sync, { let using = UsingFun::new(using); - let (params, iter, x1) = self.destruct(); + let (orchestrator, params, iter, x1) = self.destruct(); let m1 = move |_: &mut U, t: I::Item| x1(t); - UParXap::new(using, params, iter, m1) + UParXap::new(using, orchestrator, params, iter, m1) } fn using_clone( self, - using: U, + value: U, ) -> impl ParIterUsing, R, Item = >::Item> where - U: Clone + Send + 'static, + U: Clone + 'static, { - let using = UsingClone::new(using); - let (params, iter, x1) = self.destruct(); + let using = UsingClone::new(value); + let (orchestrator, params, iter, x1) = self.destruct(); let m1 = move |_: &mut U, t: I::Item| x1(t); - UParXap::new(using, params, iter, m1) + UParXap::new(using, orchestrator, params, iter, m1) } // computation transformations @@ -137,25 +138,25 @@ where where Map: Fn(Self::Item) -> Out + Sync + Clone, { - let (params, iter, x1) = self.destruct(); + let (orchestrator, params, iter, x1) = self.destruct(); let x1 = move |i: I::Item| { let vo = x1(i); vo.map(map.clone()) }; - ParXap::new(params, iter, x1) + ParXap::new(orchestrator, params, iter, x1) } fn filter(self, filter: Filter) -> impl ParIter where Filter: Fn(&Self::Item) -> bool + Sync + Clone, { - let (params, iter, x1) = self.destruct(); + let (orchestrator, params, iter, x1) = self.destruct(); let x1 = move |i: I::Item| { let values = x1(i); values.filter(filter.clone()) }; - ParXap::new(params, iter, x1) + ParXap::new(orchestrator, params, iter, x1) } fn flat_map(self, flat_map: FlatMap) -> impl ParIter @@ -163,43 +164,44 @@ where IOut: IntoIterator, FlatMap: Fn(Self::Item) -> IOut + Sync + Clone, { - let (params, iter, x1) = self.destruct(); + let (orchestrator, params, iter, x1) = self.destruct(); let x1 = move |i: I::Item| { let vo = x1(i); vo.flat_map(flat_map.clone()) }; - ParXap::new(params, iter, x1) + ParXap::new(orchestrator, params, iter, x1) } fn filter_map(self, filter_map: FilterMap) -> impl ParIter where FilterMap: Fn(Self::Item) -> Option + Sync + Clone, { - let (params, iter, x1) = self.destruct(); + let (orchestrator, params, iter, x1) = self.destruct(); let x1 = move |i: I::Item| { let vo = x1(i); vo.filter_map(filter_map.clone()) }; - ParXap::new(params, iter, x1) + ParXap::new(orchestrator, params, iter, x1) } fn take_while(self, take_while: While) -> impl ParIter where While: Fn(&Self::Item) -> bool + Sync + Clone, { - let (params, iter, x1) = self.destruct(); + let (orchestrator, params, iter, x1) = self.destruct(); let x1 = move |i: I::Item| { let vo = x1(i); vo.whilst(take_while.clone()) }; - ParXap::new(params, iter, x1) + ParXap::new(orchestrator, params, iter, x1) } fn into_fallible_result(self) -> impl ParIterResult where Self::Item: IntoResult, { - ParXapResult::new(self) + let (orchestrator, params, iter, x1) = self.destruct(); + ParXapResult::new(orchestrator, params, iter, x1) } // collect @@ -208,7 +210,8 @@ where where C: ParCollectInto, { - output.x_collect_into::(self.x) + let (orchestrator, params, iter, x1) = self.destruct(); + output.x_collect_into(orchestrator, params, iter, x1) } // reduce @@ -218,7 +221,9 @@ where Self::Item: Send, Reduce: Fn(Self::Item, Self::Item) -> Self::Item + Sync, { - self.x.reduce::(reduce).1 + let (orchestrator, params, iter, x1) = self.destruct(); + let (_, Ok(acc)) = prc::reduce::x(orchestrator, params, iter, x1, reduce); + acc } // early exit @@ -227,9 +232,16 @@ where where Self::Item: Send, { - match self.params().iteration_order { - IterationOrder::Ordered => self.x.next::().1, - IterationOrder::Arbitrary => self.x.next_any::().1, + let (orchestrator, params, iter, x1) = self.destruct(); + match params.iteration_order { + IterationOrder::Ordered => { + let (_num_threads, Ok(result)) = prc::next::x(orchestrator, params, iter, x1); + result.map(|x| x.1) + } + IterationOrder::Arbitrary => { + let (_num_threads, Ok(result)) = prc::next_any::x(orchestrator, params, iter, x1); + result + } } } } diff --git a/src/computations/default_fns.rs b/src/computations/default_fns.rs deleted file mode 100644 index b2e4bf71..00000000 --- a/src/computations/default_fns.rs +++ /dev/null @@ -1,32 +0,0 @@ -use std::ops::Add; - -#[inline(always)] -pub fn map_self(input: T) -> T { - input -} - -#[inline(always)] -pub fn map_count(_: T) -> usize { - 1 -} - -#[inline(always)] -pub fn map_copy(x: &T) -> T { - *x -} - -#[inline(always)] -pub fn map_clone(x: &T) -> T { - x.clone() -} - -#[inline(always)] -pub fn reduce_sum(a: T, b: T) -> T -where - T: Add, -{ - a + b -} - -#[inline(always)] -pub fn reduce_unit(_: (), _: ()) {} diff --git a/src/computations/map/collect.rs b/src/computations/map/collect.rs deleted file mode 100644 index 6b797ca8..00000000 --- a/src/computations/map/collect.rs +++ /dev/null @@ -1,46 +0,0 @@ -use super::m::M; -#[cfg(test)] -use crate::IterationOrder; -#[cfg(test)] -use crate::runner::parallel_runner_compute::collect_arbitrary; -use crate::runner::parallel_runner_compute::collect_ordered; -use crate::runner::{ParallelRunner, ParallelRunnerCompute}; -use orx_concurrent_iter::ConcurrentIter; -use orx_pinned_vec::IntoConcurrentPinnedVec; - -impl M -where - I: ConcurrentIter, - O: Send, - M1: Fn(I::Item) -> O + Sync, -{ - pub fn collect_into(self, pinned_vec: P) -> (usize, P) - where - R: ParallelRunner, - P: IntoConcurrentPinnedVec, - { - let (len, p) = self.len_and_params(); - match (p.is_sequential(), p.iteration_order) { - (true, _) => (0, self.sequential(pinned_vec)), - #[cfg(test)] - (false, IterationOrder::Arbitrary) => { - collect_arbitrary::m(R::collection(p, len), self, pinned_vec) - } - (false, _) => collect_ordered::m(R::collection(p, len), self, pinned_vec), - } - } - - fn sequential

(self, mut pinned_vec: P) -> P - where - P: IntoConcurrentPinnedVec, - { - let (_, iter, map1) = self.destruct(); - - let iter = iter.into_seq_iter(); - for i in iter { - pinned_vec.push(map1(i)); - } - - pinned_vec - } -} diff --git a/src/computations/map/m.rs b/src/computations/map/m.rs deleted file mode 100644 index 455d1a40..00000000 --- a/src/computations/map/m.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::{ChunkSize, IterationOrder, NumThreads, Params}; -use orx_concurrent_iter::ConcurrentIter; - -pub struct M -where - I: ConcurrentIter, - M1: Fn(I::Item) -> O, -{ - params: Params, - iter: I, - map1: M1, -} - -impl M -where - I: ConcurrentIter, - M1: Fn(I::Item) -> O, -{ - pub fn new(params: Params, iter: I, map1: M1) -> Self { - Self { params, iter, map1 } - } - - pub fn destruct(self) -> (Params, I, M1) { - (self.params, self.iter, self.map1) - } - - pub fn params(&self) -> Params { - self.params - } - - pub fn len_and_params(&self) -> (Option, Params) { - (self.iter.try_get_len(), self.params) - } - - pub fn num_threads(&mut self, num_threads: impl Into) { - self.params = self.params.with_num_threads(num_threads); - } - - pub fn chunk_size(&mut self, chunk_size: impl Into) { - self.params = self.params.with_chunk_size(chunk_size); - } - - pub fn iteration_order(&mut self, collect: IterationOrder) { - self.params = self.params.with_collect_ordering(collect); - } - - pub fn iter(&self) -> &I { - &self.iter - } - - pub fn par_len(&self) -> Option { - match (self.params.is_sequential(), self.iter.try_get_len()) { - (true, _) => None, // not required to concurrent reserve when seq - (false, x) => x, - } - } -} diff --git a/src/computations/map/mod.rs b/src/computations/map/mod.rs deleted file mode 100644 index 3a6638b7..00000000 --- a/src/computations/map/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[cfg(test)] -mod tests; - -mod collect; -mod m; -mod next; -mod reduce; -mod transformations; - -pub use m::M; diff --git a/src/computations/map/next.rs b/src/computations/map/next.rs deleted file mode 100644 index 41112bae..00000000 --- a/src/computations/map/next.rs +++ /dev/null @@ -1,27 +0,0 @@ -use super::m::M; -use crate::runner::parallel_runner_compute::{next, next_any}; -use crate::runner::{ParallelRunner, ParallelRunnerCompute}; -use orx_concurrent_iter::ConcurrentIter; - -impl M -where - I: ConcurrentIter, - M1: Fn(I::Item) -> O + Sync, - O: Send, -{ - pub fn next(self) -> (usize, Option) - where - R: ParallelRunner, - { - let (len, p) = self.len_and_params(); - next::m(R::early_return(p, len), self) - } - - pub fn next_any(self) -> (usize, Option) - where - R: ParallelRunner, - { - let (len, p) = self.len_and_params(); - next_any::m(R::early_return(p, len), self) - } -} diff --git a/src/computations/map/reduce.rs b/src/computations/map/reduce.rs deleted file mode 100644 index 55740c22..00000000 --- a/src/computations/map/reduce.rs +++ /dev/null @@ -1,20 +0,0 @@ -use super::m::M; -use crate::runner::parallel_runner_compute::reduce; -use crate::runner::{ParallelRunner, ParallelRunnerCompute}; -use orx_concurrent_iter::ConcurrentIter; - -impl M -where - I: ConcurrentIter, - O: Send, - M1: Fn(I::Item) -> O + Sync, -{ - pub fn reduce(self, reduce: X) -> (usize, Option) - where - R: ParallelRunner, - X: Fn(O, O) -> O + Sync, - { - let (len, p) = self.len_and_params(); - reduce::m(R::reduce(p, len), self, reduce) - } -} diff --git a/src/computations/map/transformations.rs b/src/computations/map/transformations.rs deleted file mode 100644 index c6465897..00000000 --- a/src/computations/map/transformations.rs +++ /dev/null @@ -1,18 +0,0 @@ -use super::m::M; -use orx_concurrent_iter::ConcurrentIter; - -impl M -where - I: ConcurrentIter, - M1: Fn(I::Item) -> O, -{ - pub fn map(self, map: M2) -> M Q> - where - M2: Fn(O) -> Q, - Q: Send, - { - let (params, iter, map1) = self.destruct(); - let map2 = move |t| map(map1(t)); - M::new(params, iter, map2) - } -} diff --git a/src/computations/mod.rs b/src/computations/mod.rs deleted file mode 100644 index a1a5a7f5..00000000 --- a/src/computations/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod default_fns; -mod heap_sort; -mod map; -mod xap; - -pub(crate) use default_fns::*; -pub(crate) use heap_sort::heap_sort_into; -pub(crate) use map::M; -pub(crate) use xap::X; diff --git a/src/computations/xap/collect.rs b/src/computations/xap/collect.rs deleted file mode 100644 index beaf5cc8..00000000 --- a/src/computations/xap/collect.rs +++ /dev/null @@ -1,117 +0,0 @@ -use super::x::X; -use crate::generic_values::runner_results::{ - Fallibility, Infallible, ParallelCollect, ParallelCollectArbitrary, Stop, -}; -use crate::runner::parallel_runner_compute::{collect_arbitrary, collect_ordered}; -use crate::{ - IterationOrder, - generic_values::Values, - runner::{ParallelRunner, ParallelRunnerCompute}, -}; -use orx_concurrent_iter::ConcurrentIter; -use orx_fixed_vec::IntoConcurrentPinnedVec; - -impl X -where - I: ConcurrentIter, - Vo: Values, - Vo::Item: Send, - M1: Fn(I::Item) -> Vo + Sync, -{ - pub fn collect_into(self, pinned_vec: P) -> (usize, P) - where - R: ParallelRunner, - P: IntoConcurrentPinnedVec, - Vo: Values, - { - let (len, p) = self.len_and_params(); - match (p.is_sequential(), p.iteration_order) { - (true, _) => (0, self.sequential(pinned_vec)), - (false, IterationOrder::Arbitrary) => { - let (num_threads, result) = - collect_arbitrary::x(R::collection(p, len), self, pinned_vec); - let pinned_vec = match result { - ParallelCollectArbitrary::AllCollected { pinned_vec } => pinned_vec, - ParallelCollectArbitrary::StoppedByWhileCondition { pinned_vec } => pinned_vec, - }; - (num_threads, pinned_vec) - } - (false, IterationOrder::Ordered) => { - let (num_threads, result) = - collect_ordered::x(R::collection(p, len), self, pinned_vec); - let pinned_vec = match result { - ParallelCollect::AllCollected { pinned_vec } => pinned_vec, - ParallelCollect::StoppedByWhileCondition { - pinned_vec, - stopped_idx: _, - } => pinned_vec, - }; - (num_threads, pinned_vec) - } - } - } - - pub fn try_collect_into( - self, - pinned_vec: P, - ) -> (usize, Result::Error>) - where - R: ParallelRunner, - P: IntoConcurrentPinnedVec, - { - let (len, p) = self.len_and_params(); - match (p.is_sequential(), p.iteration_order) { - (true, _) => (0, self.try_sequential(pinned_vec)), - (false, IterationOrder::Arbitrary) => { - let (nt, result) = collect_arbitrary::x(R::collection(p, len), self, pinned_vec); - (nt, result.into_result()) - } - (false, IterationOrder::Ordered) => { - let (nt, result) = collect_ordered::x(R::collection(p, len), self, pinned_vec); - (nt, result.into_result()) - } - } - } - - fn sequential

(self, mut pinned_vec: P) -> P - where - P: IntoConcurrentPinnedVec, - { - let (_, iter, xap1) = self.destruct(); - - let iter = iter.into_seq_iter(); - for i in iter { - let vt = xap1(i); - let done = vt.push_to_pinned_vec(&mut pinned_vec); - if Vo::sequential_push_to_stop(done).is_some() { - break; - } - } - - pinned_vec - } - - fn try_sequential

( - self, - mut pinned_vec: P, - ) -> Result::Error> - where - P: IntoConcurrentPinnedVec, - { - let (_, iter, xap1) = self.destruct(); - - let iter = iter.into_seq_iter(); - for i in iter { - let vt = xap1(i); - let done = vt.push_to_pinned_vec(&mut pinned_vec); - if let Some(stop) = Vo::sequential_push_to_stop(done) { - match stop { - Stop::DueToWhile => return Ok(pinned_vec), - Stop::DueToError { error } => return Err(error), - } - } - } - - Ok(pinned_vec) - } -} diff --git a/src/computations/xap/mod.rs b/src/computations/xap/mod.rs deleted file mode 100644 index bf1d7410..00000000 --- a/src/computations/xap/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[cfg(test)] -mod tests; - -mod collect; -mod next; -mod reduce; -mod x; - -pub use x::X; diff --git a/src/computations/xap/next.rs b/src/computations/xap/next.rs deleted file mode 100644 index 2911bd50..00000000 --- a/src/computations/xap/next.rs +++ /dev/null @@ -1,56 +0,0 @@ -use super::x::X; -use crate::generic_values::Values; -use crate::generic_values::runner_results::{Fallibility, Infallible}; -use crate::runner::parallel_runner_compute::{next, next_any}; -use crate::runner::{ParallelRunner, ParallelRunnerCompute}; -use orx_concurrent_iter::ConcurrentIter; - -impl X -where - I: ConcurrentIter, - Vo: Values, - M1: Fn(I::Item) -> Vo + Sync, - Vo::Item: Send, -{ - pub fn next(self) -> (usize, Option) - where - R: ParallelRunner, - Vo: Values, - { - let (len, p) = self.len_and_params(); - let (num_threads, Ok(result)) = next::x(R::early_return(p, len), self); - (num_threads, result.map(|x| x.1)) - } - - pub fn next_any(self) -> (usize, Option) - where - R: ParallelRunner, - Vo: Values, - { - let (len, p) = self.len_and_params(); - let (num_threads, Ok(next)) = next_any::x(R::early_return(p, len), self); - (num_threads, next) - } - - pub fn try_next(self) -> (usize, ResultTryNext) - where - R: ParallelRunner, - { - let (len, p) = self.len_and_params(); - let (num_threads, result) = next::x(R::early_return(p, len), self); - let result = result.map(|x| x.map(|y| y.1)); - (num_threads, result) - } - - pub fn try_next_any(self) -> (usize, ResultTryNext) - where - R: ParallelRunner, - { - let (len, p) = self.len_and_params(); - let (num_threads, result) = next_any::x(R::early_return(p, len), self); - (num_threads, result) - } -} - -type ResultTryNext = - Result::Item>, <::Fallibility as Fallibility>::Error>; diff --git a/src/computations/xap/reduce.rs b/src/computations/xap/reduce.rs deleted file mode 100644 index 1c223ac2..00000000 --- a/src/computations/xap/reduce.rs +++ /dev/null @@ -1,37 +0,0 @@ -use super::x::X; -use crate::generic_values::Values; -use crate::generic_values::runner_results::{Fallibility, Infallible}; -use crate::runner::parallel_runner_compute::reduce; -use crate::runner::{ParallelRunner, ParallelRunnerCompute}; -use orx_concurrent_iter::ConcurrentIter; - -impl X -where - I: ConcurrentIter, - Vo: Values, - Vo::Item: Send, - M1: Fn(I::Item) -> Vo + Sync, -{ - pub fn reduce(self, reduce: Red) -> (usize, Option) - where - R: ParallelRunner, - Red: Fn(Vo::Item, Vo::Item) -> Vo::Item + Sync, - Vo: Values, - { - let (len, p) = self.len_and_params(); - let (num_threads, Ok(acc)) = reduce::x(R::reduce(p, len), self, reduce); - (num_threads, acc) - } - - pub fn try_reduce(self, reduce: Red) -> (usize, ResultTryReduce) - where - R: ParallelRunner, - Red: Fn(Vo::Item, Vo::Item) -> Vo::Item + Sync, - { - let (len, p) = self.len_and_params(); - reduce::x(R::reduce(p, len), self, reduce) - } -} - -type ResultTryReduce = - Result::Item>, <::Fallibility as Fallibility>::Error>; diff --git a/src/computations/xap/x.rs b/src/computations/xap/x.rs deleted file mode 100644 index dd0448d2..00000000 --- a/src/computations/xap/x.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::{ChunkSize, IterationOrder, NumThreads, Params, generic_values::Values}; -use orx_concurrent_iter::ConcurrentIter; - -pub struct X -where - I: ConcurrentIter, - Vo: Values, - M1: Fn(I::Item) -> Vo, -{ - params: Params, - iter: I, - xap1: M1, -} - -impl X -where - I: ConcurrentIter, - Vo: Values, - M1: Fn(I::Item) -> Vo, -{ - pub fn new(params: Params, iter: I, xap1: M1) -> Self { - Self { params, iter, xap1 } - } - - pub fn destruct(self) -> (Params, I, M1) { - (self.params, self.iter, self.xap1) - } - - pub fn params(&self) -> Params { - self.params - } - - pub fn len_and_params(&self) -> (Option, Params) { - (self.iter.try_get_len(), self.params) - } - - pub fn num_threads(&mut self, num_threads: impl Into) { - self.params = self.params.with_num_threads(num_threads); - } - - pub fn chunk_size(&mut self, chunk_size: impl Into) { - self.params = self.params.with_chunk_size(chunk_size); - } - - pub fn iteration_order(&mut self, collect: IterationOrder) { - self.params = self.params.with_collect_ordering(collect); - } - - pub fn iter(&self) -> &I { - &self.iter - } - - pub fn par_len(&self) -> Option { - match (self.params.is_sequential(), self.iter.try_get_len()) { - (true, _) => None, // not required to concurrent reserve when seq - (false, x) => x, - } - } -} diff --git a/src/using/computations/default_fns.rs b/src/default_fns.rs similarity index 53% rename from src/using/computations/default_fns.rs rename to src/default_fns.rs index 84d9739e..523904de 100644 --- a/src/using/computations/default_fns.rs +++ b/src/default_fns.rs @@ -1,4 +1,37 @@ -use std::ops::Add; +use core::ops::Add; + +#[inline(always)] +pub fn map_self(input: T) -> T { + input +} + +#[inline(always)] +pub fn map_count(_: T) -> usize { + 1 +} + +#[inline(always)] +pub fn map_copy(x: &T) -> T { + *x +} + +#[inline(always)] +pub fn map_clone(x: &T) -> T { + x.clone() +} + +#[inline(always)] +pub fn reduce_sum(a: T, b: T) -> T +where + T: Add, +{ + a + b +} + +#[inline(always)] +pub fn reduce_unit(_: (), _: ()) {} + +// using #[inline(always)] pub fn u_map_self(_: &mut U, input: T) -> T { diff --git a/src/env.rs b/src/env.rs index f9cb08de..24635f71 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1 +1,19 @@ -pub const MAX_NUM_THREADS_ENV_VARIABLE: &str = "ORX_PARALLEL_MAX_NUM_THREADS"; +use core::num::NonZeroUsize; + +#[cfg(feature = "std")] +const MAX_NUM_THREADS_ENV_VARIABLE: &str = "ORX_PARALLEL_MAX_NUM_THREADS"; + +pub fn max_num_threads_by_env_variable() -> Option { + #[cfg(feature = "std")] + match std::env::var(MAX_NUM_THREADS_ENV_VARIABLE) { + Ok(s) => match s.parse::() { + Ok(0) => None, // consistent with .num_threads(0) representing no bound + Ok(x) => Some(NonZeroUsize::new(x).expect("x>0")), // set to a positive bound + Err(_e) => None, // not a number, ignored assuming no bound + }, + Err(_e) => None, // not set, no bound + } + + #[cfg(not(feature = "std"))] + None +} diff --git a/src/executor/computation_kind.rs b/src/executor/computation_kind.rs new file mode 100644 index 00000000..0359e43d --- /dev/null +++ b/src/executor/computation_kind.rs @@ -0,0 +1,10 @@ +/// Computation kind. +#[derive(Clone, Copy)] +pub enum ComputationKind { + /// Computation where outputs are collected into a collection. + Collect, + /// Computation where the inputs or intermediate results are reduced to a single value. + Reduce, + /// Computation which allows for early returns, such as `find` operation. + EarlyReturn, +} diff --git a/src/runner/fixed_chunk_runner/chunk_size.rs b/src/executor/fixed_chunk_executor/chunk_size.rs similarity index 87% rename from src/runner/fixed_chunk_runner/chunk_size.rs rename to src/executor/fixed_chunk_executor/chunk_size.rs index 58e942f4..5b0a7fc4 100644 --- a/src/runner/fixed_chunk_runner/chunk_size.rs +++ b/src/executor/fixed_chunk_executor/chunk_size.rs @@ -1,4 +1,5 @@ -use crate::{parameters::ChunkSize, runner::computation_kind::ComputationKind}; +use crate::{parameters::ChunkSize, runner::ComputationKind}; +use core::num::NonZeroUsize; const MAX_CHUNK_SIZE: usize = 1 << 20; const DESIRED_MIN_CHUNK_SIZE: usize = 64; @@ -13,7 +14,7 @@ impl ResolvedChunkSize { pub fn new( kind: ComputationKind, initial_len: Option, - max_num_threads: usize, + max_num_threads: NonZeroUsize, chunk_size: ChunkSize, ) -> Self { match chunk_size { @@ -42,7 +43,7 @@ const fn min_required_len(kind: ComputationKind, one_round_len: usize) -> usize fn auto_chunk_size( kind: ComputationKind, initial_len: Option, - max_num_threads: usize, + max_num_threads: NonZeroUsize, ) -> usize { fn find_chunk_size(kind: ComputationKind, input_len: usize, num_threads: usize) -> usize { let mut chunk_size = MAX_CHUNK_SIZE; @@ -74,11 +75,16 @@ fn auto_chunk_size( match initial_len { None => 1, // TODO: is this a good choice? Some(0) => 1, - Some(input_len) => find_chunk_size(kind, input_len, max_num_threads), + Some(input_len) => find_chunk_size(kind, input_len, max_num_threads.into()), } } -fn min_chunk_size(initial_len: Option, max_num_threads: usize, chunk_size: usize) -> usize { +fn min_chunk_size( + initial_len: Option, + max_num_threads: NonZeroUsize, + chunk_size: usize, +) -> usize { + let max_num_threads: usize = max_num_threads.into(); match initial_len { None => chunk_size, Some(0) => 1, diff --git a/src/executor/fixed_chunk_executor/mod.rs b/src/executor/fixed_chunk_executor/mod.rs new file mode 100644 index 00000000..093860c4 --- /dev/null +++ b/src/executor/fixed_chunk_executor/mod.rs @@ -0,0 +1,5 @@ +mod chunk_size; +mod parallel_executor; +mod thread_executor; + +pub use parallel_executor::FixedChunkRunner; diff --git a/src/runner/fixed_chunk_runner/parallel_runner.rs b/src/executor/fixed_chunk_executor/parallel_executor.rs similarity index 76% rename from src/runner/fixed_chunk_runner/parallel_runner.rs rename to src/executor/fixed_chunk_executor/parallel_executor.rs index 4d2c510b..be328a4e 100644 --- a/src/runner/fixed_chunk_runner/parallel_runner.rs +++ b/src/executor/fixed_chunk_executor/parallel_executor.rs @@ -1,13 +1,13 @@ -use super::{ - chunk_size::ResolvedChunkSize, num_threads::maximum_num_threads, - thread_runner::FixedChunkThreadRunner, -}; +use super::{chunk_size::ResolvedChunkSize, thread_executor::FixedChunkThreadExecutor}; +use crate::runner::ComputationKind; use crate::{ - parameters::Params, - runner::{computation_kind::ComputationKind, parallel_runner::ParallelRunner}, + executor::parallel_executor::ParallelExecutor, parameters::Params, runner::NumSpawned, +}; +use core::{ + num::NonZeroUsize, + sync::atomic::{AtomicUsize, Ordering}, }; use orx_concurrent_iter::ConcurrentIter; -use std::sync::atomic::{AtomicUsize, Ordering}; const LAG_PERIODICITY: usize = 4; @@ -72,31 +72,36 @@ impl FixedChunkRunner { } } -impl ParallelRunner for FixedChunkRunner { +impl ParallelExecutor for FixedChunkRunner { type SharedState = (); - type ThreadRunner = FixedChunkThreadRunner; + type ThreadExecutor = FixedChunkThreadExecutor; - fn new(kind: ComputationKind, params: Params, initial_len: Option) -> Self { - let max_num_threads = maximum_num_threads(initial_len, params.num_threads); + fn new( + kind: ComputationKind, + params: Params, + initial_len: Option, + max_num_threads: NonZeroUsize, + ) -> Self { let resolved_chunk_size = ResolvedChunkSize::new(kind, initial_len, max_num_threads, params.chunk_size); Self { initial_len, resolved_chunk_size, - max_num_threads, + max_num_threads: max_num_threads.into(), current_chunk_size: resolved_chunk_size.chunk_size().into(), } } fn new_shared_state(&self) -> Self::SharedState {} - fn do_spawn_new(&self, num_spawned: usize, _: &Self::SharedState, iter: &I) -> bool + fn do_spawn_new(&self, num_spawned: NumSpawned, _: &Self::SharedState, iter: &I) -> bool where I: ConcurrentIter, { - if num_spawned % LAG_PERIODICITY == 0 { + let num_spawned = num_spawned.into_inner(); + if num_spawned.is_multiple_of(LAG_PERIODICITY) { match self.next_chunk(num_spawned, iter.try_get_len()) { Some(c) => self.current_chunk_size.store(c, Ordering::Relaxed), None => return false, @@ -106,8 +111,8 @@ impl ParallelRunner for FixedChunkRunner { self.spawn_new(num_spawned, iter.try_get_len()) } - fn new_thread_runner(&self, _: &Self::SharedState) -> Self::ThreadRunner { - Self::ThreadRunner { + fn new_thread_executor(&self, _: &Self::SharedState) -> Self::ThreadExecutor { + Self::ThreadExecutor { chunk_size: self.current_chunk_size.load(Ordering::Relaxed), } } diff --git a/src/runner/fixed_chunk_runner/thread_runner.rs b/src/executor/fixed_chunk_executor/thread_executor.rs similarity index 75% rename from src/runner/fixed_chunk_runner/thread_runner.rs rename to src/executor/fixed_chunk_executor/thread_executor.rs index 55ea9071..186d7149 100644 --- a/src/runner/fixed_chunk_runner/thread_runner.rs +++ b/src/executor/fixed_chunk_executor/thread_executor.rs @@ -1,11 +1,11 @@ -use crate::runner::thread_runner::ThreadRunner; +use crate::executor::thread_executor::ThreadExecutor; use orx_concurrent_iter::ConcurrentIter; -pub struct FixedChunkThreadRunner { +pub struct FixedChunkThreadExecutor { pub(super) chunk_size: usize, } -impl ThreadRunner for FixedChunkThreadRunner { +impl ThreadExecutor for FixedChunkThreadExecutor { type SharedState = (); #[inline(always)] diff --git a/src/executor/mod.rs b/src/executor/mod.rs new file mode 100644 index 00000000..8deafde4 --- /dev/null +++ b/src/executor/mod.rs @@ -0,0 +1,11 @@ +mod fixed_chunk_executor; +pub(crate) mod parallel_compute; +mod parallel_executor; +mod thread_compute; +mod thread_executor; + +pub use parallel_executor::ParallelExecutor; +pub use thread_executor::ThreadExecutor; + +/// Default parallel executor. +pub type DefaultExecutor = fixed_chunk_executor::FixedChunkRunner; diff --git a/src/executor/parallel_compute/collect_arbitrary.rs b/src/executor/parallel_compute/collect_arbitrary.rs new file mode 100644 index 00000000..50deea34 --- /dev/null +++ b/src/executor/parallel_compute/collect_arbitrary.rs @@ -0,0 +1,84 @@ +use crate::Params; +use crate::executor::thread_compute as th; +use crate::generic_values::Values; +use crate::generic_values::runner_results::ParallelCollectArbitrary; +use crate::runner::{ComputationKind, NumSpawned, ParallelRunner, SharedStateOf, ThreadRunnerOf}; +use orx_concurrent_bag::ConcurrentBag; +use orx_concurrent_iter::ConcurrentIter; +use orx_fixed_vec::IntoConcurrentPinnedVec; + +#[cfg(test)] +pub fn m( + mut orchestrator: C, + params: Params, + iter: I, + map1: M1, + pinned_vec: P, +) -> (NumSpawned, P) +where + C: ParallelRunner, + I: ConcurrentIter, + O: Send, + M1: Fn(I::Item) -> O + Sync, + P: IntoConcurrentPinnedVec, +{ + let capacity_bound = pinned_vec.capacity_bound(); + let offset = pinned_vec.len(); + let mut bag: ConcurrentBag = pinned_vec.into(); + match iter.try_get_len() { + Some(iter_len) => bag.reserve_maximum_capacity(offset + iter_len), + None => bag.reserve_maximum_capacity(capacity_bound), + }; + + let thread_work = |_, iter: &I, state: &SharedStateOf, thread_runner: ThreadRunnerOf| { + th::collect_arbitrary::m(thread_runner, iter, state, &map1, &bag); + }; + let num_spawned = orchestrator.run_all(params, iter, ComputationKind::Collect, thread_work); + + let values = bag.into_inner(); + (num_spawned, values) +} + +pub fn x( + mut orchestrator: C, + params: Params, + iter: I, + xap1: X1, + pinned_vec: P, +) -> (NumSpawned, ParallelCollectArbitrary) +where + C: ParallelRunner, + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + X1: Fn(I::Item) -> Vo + Sync, + P: IntoConcurrentPinnedVec, +{ + let capacity_bound = pinned_vec.capacity_bound(); + let offset = pinned_vec.len(); + + let mut bag: ConcurrentBag = pinned_vec.into(); + match iter.try_get_len() { + Some(iter_len) => bag.reserve_maximum_capacity(offset + iter_len), + None => bag.reserve_maximum_capacity(capacity_bound), + }; + + let thread_map = |_, iter: &I, state: &SharedStateOf, thread_runner: ThreadRunnerOf| { + th::collect_arbitrary::x(thread_runner, iter, state, &xap1, &bag).into_result() + }; + let (num_spawned, result) = orchestrator.map_all::( + params, + iter, + ComputationKind::Collect, + thread_map, + ); + + let result = match result { + Err(error) => ParallelCollectArbitrary::StoppedByError { error }, + Ok(_) => ParallelCollectArbitrary::AllOrUntilWhileCollected { + pinned_vec: bag.into_inner(), + }, + }; + + (num_spawned, result) +} diff --git a/src/executor/parallel_compute/collect_ordered.rs b/src/executor/parallel_compute/collect_ordered.rs new file mode 100644 index 00000000..66332670 --- /dev/null +++ b/src/executor/parallel_compute/collect_ordered.rs @@ -0,0 +1,67 @@ +use crate::Params; +use crate::executor::thread_compute as th; +use crate::generic_values::Values; +use crate::generic_values::runner_results::{Fallibility, ParallelCollect}; +use crate::runner::{ComputationKind, NumSpawned, ParallelRunner, SharedStateOf, ThreadRunnerOf}; +use orx_concurrent_iter::ConcurrentIter; +use orx_concurrent_ordered_bag::ConcurrentOrderedBag; +use orx_fixed_vec::IntoConcurrentPinnedVec; + +pub fn m( + mut orchestrator: C, + params: Params, + iter: I, + map1: M1, + pinned_vec: P, +) -> (NumSpawned, P) +where + C: ParallelRunner, + I: ConcurrentIter, + O: Send, + M1: Fn(I::Item) -> O + Sync, + P: IntoConcurrentPinnedVec, +{ + let offset = pinned_vec.len(); + let o_bag: ConcurrentOrderedBag = pinned_vec.into(); + + let thread_do = |_, iter: &I, state: &SharedStateOf, thread_runner: ThreadRunnerOf| { + th::collect_ordered::m(thread_runner, iter, state, &map1, &o_bag, offset); + }; + let num_spawned = orchestrator.run_all(params, iter, ComputationKind::Collect, thread_do); + + let values = unsafe { o_bag.into_inner().unwrap_only_if_counts_match() }; + (num_spawned, values) +} + +pub fn x( + mut orchestrator: C, + params: Params, + iter: I, + xap1: X1, + pinned_vec: P, +) -> (NumSpawned, ParallelCollect) +where + C: ParallelRunner, + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + ::Error: Send, + X1: Fn(I::Item) -> Vo + Sync, + P: IntoConcurrentPinnedVec, +{ + let thread_map = |_, iter: &I, state: &SharedStateOf, thread_runner: ThreadRunnerOf| { + th::collect_ordered::x(thread_runner, iter, state, &xap1).into_result() + }; + let (num_spawned, result) = orchestrator.map_all::( + params, + iter, + ComputationKind::Collect, + thread_map, + ); + + let result = match result { + Err(error) => ParallelCollect::StoppedByError { error }, + Ok(results) => ParallelCollect::reduce(results, pinned_vec), + }; + (num_spawned, result) +} diff --git a/src/runner/thread_runner_compute/mod.rs b/src/executor/parallel_compute/mod.rs similarity index 100% rename from src/runner/thread_runner_compute/mod.rs rename to src/executor/parallel_compute/mod.rs diff --git a/src/executor/parallel_compute/next.rs b/src/executor/parallel_compute/next.rs new file mode 100644 index 00000000..5653175a --- /dev/null +++ b/src/executor/parallel_compute/next.rs @@ -0,0 +1,72 @@ +use crate::Params; +use crate::executor::thread_compute as th; +use crate::generic_values::Values; +use crate::generic_values::runner_results::{Fallibility, NextSuccess, NextWithIdx}; +use crate::runner::{ComputationKind, NumSpawned, ParallelRunner, SharedStateOf}; +use orx_concurrent_iter::ConcurrentIter; + +pub fn m( + mut orchestrator: C, + params: Params, + iter: I, + map1: M1, +) -> (NumSpawned, Option) +where + C: ParallelRunner, + I: ConcurrentIter, + O: Send, + M1: Fn(I::Item) -> O + Sync, +{ + let thread_map = |_, iter: &I, state: &SharedStateOf, thread_runner| { + Ok(th::next::m(thread_runner, iter, state, &map1)) + }; + let (num_spawned, result) = + orchestrator.map_infallible(params, iter, ComputationKind::Collect, thread_map); + + let next = match result { + Ok(results) => results + .into_iter() + .flatten() + .min_by_key(|x| x.0) + .map(|x| x.1), + }; + (num_spawned, next) +} + +type ResultNext = Result< + Option<(usize, ::Item)>, + <::Fallibility as Fallibility>::Error, +>; + +pub fn x( + mut orchestrator: C, + params: Params, + iter: I, + xap1: X1, +) -> (NumSpawned, ResultNext) +where + C: ParallelRunner, + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + X1: Fn(I::Item) -> Vo + Sync, +{ + let thread_map = |_, iter: &I, state: &SharedStateOf, th_runner| match th::next::x( + th_runner, iter, state, &xap1, + ) { + NextWithIdx::Found { idx, value } => Ok(Some(NextSuccess::Found { idx, value })), + NextWithIdx::NotFound => Ok(None), + NextWithIdx::StoppedByWhileCondition { idx } => { + Ok(Some(NextSuccess::StoppedByWhileCondition { idx })) + } + NextWithIdx::StoppedByError { error } => Err(error), + }; + let (num_spawned, result) = orchestrator.map_all::( + params, + iter, + ComputationKind::Collect, + thread_map, + ); + let next = result.map(|results| NextSuccess::reduce(results.into_iter().flatten())); + (num_spawned, next) +} diff --git a/src/executor/parallel_compute/next_any.rs b/src/executor/parallel_compute/next_any.rs new file mode 100644 index 00000000..c3d5d3c3 --- /dev/null +++ b/src/executor/parallel_compute/next_any.rs @@ -0,0 +1,59 @@ +use crate::Params; +use crate::executor::thread_compute as th; +use crate::generic_values::Values; +use crate::generic_values::runner_results::Fallibility; +use crate::runner::{ComputationKind, NumSpawned, ParallelRunner, SharedStateOf}; +use orx_concurrent_iter::ConcurrentIter; + +pub fn m( + mut orchestrator: C, + params: Params, + iter: I, + map1: M1, +) -> (NumSpawned, Option) +where + C: ParallelRunner, + I: ConcurrentIter, + O: Send, + M1: Fn(I::Item) -> O + Sync, +{ + let thread_map = |_, iter: &I, state: &SharedStateOf, thread_runner| { + Ok(th::next_any::m(thread_runner, iter, state, &map1)) + }; + let (num_spawned, result) = + orchestrator.map_infallible(params, iter, ComputationKind::Collect, thread_map); + + let next = match result { + Ok(results) => results.into_iter().flatten().next(), + }; + (num_spawned, next) +} + +type ResultNextAny = + Result::Item>, <::Fallibility as Fallibility>::Error>; + +pub fn x( + mut orchestrator: C, + params: Params, + iter: I, + xap1: X1, +) -> (NumSpawned, ResultNextAny) +where + C: ParallelRunner, + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + X1: Fn(I::Item) -> Vo + Sync, +{ + let thread_map = |_, iter: &I, state: &SharedStateOf, th_runner| { + th::next_any::x(th_runner, iter, state, &xap1) + }; + let (num_spawned, result) = orchestrator.map_all::( + params, + iter, + ComputationKind::Collect, + thread_map, + ); + let next = result.map(|results| results.into_iter().flatten().next()); + (num_spawned, next) +} diff --git a/src/executor/parallel_compute/reduce.rs b/src/executor/parallel_compute/reduce.rs new file mode 100644 index 00000000..663b1c97 --- /dev/null +++ b/src/executor/parallel_compute/reduce.rs @@ -0,0 +1,64 @@ +use crate::Params; +use crate::executor::thread_compute as th; +use crate::generic_values::Values; +use crate::generic_values::runner_results::Fallibility; +use crate::runner::{ComputationKind, NumSpawned, ParallelRunner, SharedStateOf, ThreadRunnerOf}; +use orx_concurrent_iter::ConcurrentIter; + +pub fn m( + mut orchestrator: C, + params: Params, + iter: I, + map1: M1, + reduce: Red, +) -> (NumSpawned, Option) +where + C: ParallelRunner, + I: ConcurrentIter, + M1: Fn(I::Item) -> O + Sync, + Red: Fn(O, O) -> O + Sync, + O: Send, +{ + let thread_map = |_, iter: &I, state: &SharedStateOf, thread_runner: ThreadRunnerOf| { + Ok(th::reduce::m(thread_runner, iter, state, &map1, &reduce)) + }; + let (num_spawned, result) = + orchestrator.map_infallible(params, iter, ComputationKind::Collect, thread_map); + + let acc = match result { + Ok(results) => results.into_iter().flatten().reduce(reduce), + }; + + (num_spawned, acc) +} + +type ResultReduce = + Result::Item>, <::Fallibility as Fallibility>::Error>; + +pub fn x( + mut orchestrator: C, + params: Params, + iter: I, + xap1: X1, + reduce: Red, +) -> (NumSpawned, ResultReduce) +where + C: ParallelRunner, + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + X1: Fn(I::Item) -> Vo + Sync, + Red: Fn(Vo::Item, Vo::Item) -> Vo::Item + Sync, +{ + let thread_map = |_, iter: &I, state: &SharedStateOf, thread_runner: ThreadRunnerOf| { + th::reduce::x(thread_runner, iter, state, &xap1, &reduce).into_result() + }; + let (num_spawned, result) = orchestrator.map_all::( + params, + iter, + ComputationKind::Collect, + thread_map, + ); + let acc = result.map(|results| results.into_iter().flatten().reduce(reduce)); + (num_spawned, acc) +} diff --git a/src/executor/parallel_executor.rs b/src/executor/parallel_executor.rs new file mode 100644 index 00000000..abf434d8 --- /dev/null +++ b/src/executor/parallel_executor.rs @@ -0,0 +1,47 @@ +use super::thread_executor::ThreadExecutor; +use crate::{ + parameters::Params, + runner::{ComputationKind, NumSpawned}, +}; +use core::num::NonZeroUsize; +use orx_concurrent_iter::ConcurrentIter; + +/// A parallel executor which is responsible for taking a computation defined as a composition +/// of iterator methods, spawns threads, shares tasks and returns the result of the parallel +/// execution. +pub trait ParallelExecutor: Sized + Sync + 'static { + /// Data shared to the thread executors. + type SharedState: Send + Sync; + + /// Thread executor that is responsible for executing the tasks allocated to a thread. + type ThreadExecutor: ThreadExecutor; + + /// Creates a new parallel executor for the given computation `kind`, parallelization `params` + /// and `initial_input_len`. + fn new( + kind: ComputationKind, + params: Params, + initial_input_len: Option, + max_num_threads: NonZeroUsize, + ) -> Self; + + /// Creates an initial shared state. + fn new_shared_state(&self) -> Self::SharedState; + + /// Returns true if it is beneficial to spawn a new thread provided that: + /// + /// * `num_spawned` threads are already been spawned, and + /// * `shared_state` is the current parallel execution state. + fn do_spawn_new( + &self, + num_spawned: NumSpawned, + shared_state: &Self::SharedState, + iter: &I, + ) -> bool + where + I: ConcurrentIter; + + /// Creates a new thread executor provided that the current parallel execution state is + /// `shared_state`. + fn new_thread_executor(&self, shared_state: &Self::SharedState) -> Self::ThreadExecutor; +} diff --git a/src/runner/thread_runner_compute/collect_arbitrary.rs b/src/executor/thread_compute/collect_arbitrary.rs similarity index 98% rename from src/runner/thread_runner_compute/collect_arbitrary.rs rename to src/executor/thread_compute/collect_arbitrary.rs index 2d7ffde3..0b632e35 100644 --- a/src/runner/thread_runner_compute/collect_arbitrary.rs +++ b/src/executor/thread_compute/collect_arbitrary.rs @@ -1,4 +1,4 @@ -use crate::ThreadRunner; +use crate::ThreadExecutor; use crate::generic_values::Values; use crate::generic_values::runner_results::{Stop, ThreadCollectArbitrary}; use orx_concurrent_bag::ConcurrentBag; @@ -15,7 +15,7 @@ pub fn m( map1: &M1, bag: &ConcurrentBag, ) where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, M1: Fn(I::Item) -> O, P: IntoConcurrentPinnedVec, @@ -62,7 +62,7 @@ pub fn x( bag: &ConcurrentBag, ) -> ThreadCollectArbitrary where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, Vo: Values, X1: Fn(I::Item) -> Vo, diff --git a/src/runner/thread_runner_compute/collect_ordered.rs b/src/executor/thread_compute/collect_ordered.rs similarity index 98% rename from src/runner/thread_runner_compute/collect_ordered.rs rename to src/executor/thread_compute/collect_ordered.rs index 0bf6f657..10984a02 100644 --- a/src/runner/thread_runner_compute/collect_ordered.rs +++ b/src/executor/thread_compute/collect_ordered.rs @@ -1,6 +1,7 @@ -use crate::ThreadRunner; +use crate::ThreadExecutor; use crate::generic_values::Values; use crate::generic_values::runner_results::{StopWithIdx, ThreadCollect}; +use alloc::vec::Vec; use orx_concurrent_iter::{ChunkPuller, ConcurrentIter}; use orx_concurrent_ordered_bag::ConcurrentOrderedBag; use orx_fixed_vec::IntoConcurrentPinnedVec; @@ -13,7 +14,7 @@ pub fn m( o_bag: &ConcurrentOrderedBag, offset: usize, ) where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, M1: Fn(I::Item) -> O, P: IntoConcurrentPinnedVec, @@ -59,7 +60,7 @@ pub fn x( xap1: &X1, ) -> ThreadCollect where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, Vo: Values, X1: Fn(I::Item) -> Vo, diff --git a/src/executor/thread_compute/mod.rs b/src/executor/thread_compute/mod.rs new file mode 100644 index 00000000..ddc6e7e6 --- /dev/null +++ b/src/executor/thread_compute/mod.rs @@ -0,0 +1,5 @@ +pub(super) mod collect_arbitrary; +pub(super) mod collect_ordered; +pub(super) mod next; +pub(super) mod next_any; +pub(super) mod reduce; diff --git a/src/runner/thread_runner_compute/next.rs b/src/executor/thread_compute/next.rs similarity index 98% rename from src/runner/thread_runner_compute/next.rs rename to src/executor/thread_compute/next.rs index e4325799..d6d6fac8 100644 --- a/src/runner/thread_runner_compute/next.rs +++ b/src/executor/thread_compute/next.rs @@ -1,5 +1,5 @@ use crate::{ - ThreadRunner, + ThreadExecutor, generic_values::Values, generic_values::runner_results::{Next, NextWithIdx}, }; @@ -12,7 +12,7 @@ pub fn m( map1: &M1, ) -> Option<(usize, O)> where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, M1: Fn(I::Item) -> O, { @@ -69,7 +69,7 @@ pub fn x( xap1: &X1, ) -> NextWithIdx where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, Vo: Values, X1: Fn(I::Item) -> Vo, diff --git a/src/runner/thread_runner_compute/next_any.rs b/src/executor/thread_compute/next_any.rs similarity index 98% rename from src/runner/thread_runner_compute/next_any.rs rename to src/executor/thread_compute/next_any.rs index 433badcb..a09926f7 100644 --- a/src/runner/thread_runner_compute/next_any.rs +++ b/src/executor/thread_compute/next_any.rs @@ -1,5 +1,5 @@ use crate::{ - ThreadRunner, + ThreadExecutor, generic_values::Values, generic_values::runner_results::{Fallibility, Next}, }; @@ -12,7 +12,7 @@ pub fn m( map1: &M1, ) -> Option where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, O: Send, M1: Fn(I::Item) -> O, @@ -70,7 +70,7 @@ pub fn x( xap1: &X1, ) -> Result, ::Error> where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, Vo: Values, Vo::Item: Send, diff --git a/src/runner/thread_runner_compute/reduce.rs b/src/executor/thread_compute/reduce.rs similarity index 98% rename from src/runner/thread_runner_compute/reduce.rs rename to src/executor/thread_compute/reduce.rs index ce054040..9b0b03be 100644 --- a/src/runner/thread_runner_compute/reduce.rs +++ b/src/executor/thread_compute/reduce.rs @@ -1,5 +1,5 @@ use crate::{ - ThreadRunner, + ThreadExecutor, generic_values::{ Values, runner_results::{Reduce, StopReduce}, @@ -17,7 +17,7 @@ pub fn m( reduce: &Red, ) -> Option where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, M1: Fn(I::Item) -> O, Red: Fn(O, O) -> O, @@ -80,7 +80,7 @@ pub fn x( reduce: &Red, ) -> Reduce where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, Vo: Values, X1: Fn(I::Item) -> Vo, diff --git a/src/runner/thread_runner.rs b/src/executor/thread_executor.rs similarity index 79% rename from src/runner/thread_runner.rs rename to src/executor/thread_executor.rs index 4a50f6a2..dd96e072 100644 --- a/src/runner/thread_runner.rs +++ b/src/executor/thread_executor.rs @@ -1,8 +1,8 @@ use orx_concurrent_iter::ConcurrentIter; -/// Thread runner responsible for executing the tasks assigned to the thread by the -/// parallel runner. -pub trait ThreadRunner: Sized { +/// Thread executor responsible for executing the tasks assigned to the thread by the +/// parallel executor. +pub trait ThreadExecutor: Sized { /// Type of the shared state among threads. type SharedState; @@ -17,11 +17,11 @@ pub trait ThreadRunner: Sized { /// Hook that will be called after completing the chunk of the given `chunk_size`. /// The `shared_state` is also provided so that it can be updated to send information to the - /// parallel runner and other thread runners. + /// parallel executor and other thread executors. fn complete_chunk(&mut self, shared_state: &Self::SharedState, chunk_size: usize); /// Hook that will be called after completing the task. /// The `shared_state` is also provided so that it can be updated to send information to the - /// parallel runner and other thread runners. + /// parallel executor and other thread executors. fn complete_task(&mut self, shared_state: &Self::SharedState); } diff --git a/src/generic_iterator/collect.rs b/src/generic_iterator/collect.rs index 92d641b3..a74bfd31 100644 --- a/src/generic_iterator/collect.rs +++ b/src/generic_iterator/collect.rs @@ -1,5 +1,6 @@ use super::iter::GenericIterator; use crate::ParIter; +use alloc::vec::Vec; impl GenericIterator where diff --git a/src/generic_iterator/reduce.rs b/src/generic_iterator/reduce.rs index cfd27e8f..73809ce2 100644 --- a/src/generic_iterator/reduce.rs +++ b/src/generic_iterator/reduce.rs @@ -159,7 +159,7 @@ where /// [`sum`]: crate::ParIter::sum pub fn sum(self) -> T where - T: crate::special_type_sets::Sum + std::iter::Sum, + T: crate::special_type_sets::Sum + core::iter::Sum, { match self { GenericIterator::Sequential(x) => x.sum(), diff --git a/src/generic_values/option.rs b/src/generic_values/option.rs index bbdd8dd8..6b1c0cd0 100644 --- a/src/generic_values/option.rs +++ b/src/generic_values/option.rs @@ -7,6 +7,7 @@ use crate::generic_values::{ }, whilst_option::WhilstOption, }; +use alloc::vec::Vec; use orx_concurrent_bag::ConcurrentBag; use orx_pinned_vec::{IntoConcurrentPinnedVec, PinnedVec}; diff --git a/src/generic_values/option_result.rs b/src/generic_values/option_result.rs index 64f9cb13..7aaf00f6 100644 --- a/src/generic_values/option_result.rs +++ b/src/generic_values/option_result.rs @@ -2,6 +2,7 @@ use crate::generic_values::Values; use crate::generic_values::runner_results::{ ArbitraryPush, Fallible, Next, OrderedPush, Reduce, SequentialPush, }; +use alloc::vec::Vec; use orx_concurrent_bag::ConcurrentBag; use orx_pinned_vec::{IntoConcurrentPinnedVec, PinnedVec}; diff --git a/src/generic_values/result.rs b/src/generic_values/result.rs index fd7cd106..69c73006 100644 --- a/src/generic_values/result.rs +++ b/src/generic_values/result.rs @@ -2,6 +2,7 @@ use crate::generic_values::Values; use crate::generic_values::runner_results::{ ArbitraryPush, Fallible, Next, OrderedPush, Reduce, SequentialPush, }; +use alloc::vec::Vec; use orx_concurrent_bag::ConcurrentBag; use orx_pinned_vec::{IntoConcurrentPinnedVec, PinnedVec}; diff --git a/src/generic_values/runner_results/collect_arbitrary.rs b/src/generic_values/runner_results/collect_arbitrary.rs index c48cc2f2..ea6cf6ba 100644 --- a/src/generic_values/runner_results/collect_arbitrary.rs +++ b/src/generic_values/runner_results/collect_arbitrary.rs @@ -16,8 +16,17 @@ where StoppedByError { error: F::Error }, } +impl ThreadCollectArbitrary { + pub fn into_result(self) -> Result<(), F::Error> { + match self { + Self::StoppedByError { error } => Err(error), + _ => Ok(()), + } + } +} + impl core::fmt::Debug for ThreadCollectArbitrary { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::AllCollected => write!(f, "AllCollected"), Self::StoppedByWhileCondition => write!(f, "StoppedByWhileCondition"), @@ -31,12 +40,10 @@ where V: Values, P: IntoConcurrentPinnedVec, { - AllCollected { - pinned_vec: P, - }, - StoppedByWhileCondition { + AllOrUntilWhileCollected { pinned_vec: P, }, + StoppedByError { error: ::Error, }, @@ -47,16 +54,12 @@ where V: Values, P: IntoConcurrentPinnedVec, { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - Self::AllCollected { pinned_vec } => f + Self::AllOrUntilWhileCollected { pinned_vec } => f .debug_struct("AllCollected") .field("pinned_vec.len()", &pinned_vec.len()) .finish(), - Self::StoppedByWhileCondition { pinned_vec } => f - .debug_struct("StoppedByWhileCondition") - .field("pinned_vec.len()", &pinned_vec.len()) - .finish(), Self::StoppedByError { error: _ } => f.debug_struct("StoppedByError").finish(), } } @@ -69,8 +72,7 @@ where { pub fn into_result(self) -> Result::Error> { match self { - Self::AllCollected { pinned_vec } => Ok(pinned_vec), - Self::StoppedByWhileCondition { pinned_vec } => Ok(pinned_vec), + Self::AllOrUntilWhileCollected { pinned_vec } => Ok(pinned_vec), Self::StoppedByError { error } => Err(error), } } diff --git a/src/generic_values/runner_results/collect_ordered.rs b/src/generic_values/runner_results/collect_ordered.rs index 54aa71da..2d67750d 100644 --- a/src/generic_values/runner_results/collect_ordered.rs +++ b/src/generic_values/runner_results/collect_ordered.rs @@ -1,7 +1,8 @@ use crate::{ - computations::heap_sort_into, generic_values::{Values, runner_results::Fallibility}, + heap_sort::heap_sort_into, }; +use alloc::vec::Vec; use core::fmt::Debug; use orx_fixed_vec::IntoConcurrentPinnedVec; @@ -28,7 +29,7 @@ where } impl Debug for ThreadCollect { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::AllCollected { vec } => f .debug_struct("AllCollected") @@ -75,7 +76,7 @@ where V: Values, P: IntoConcurrentPinnedVec, { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::AllCollected { pinned_vec } => f .debug_struct("AllCollected") diff --git a/src/generic_values/runner_results/collect_sequential.rs b/src/generic_values/runner_results/collect_sequential.rs index 3dcb4cf2..c5b4cc41 100644 --- a/src/generic_values/runner_results/collect_sequential.rs +++ b/src/generic_values/runner_results/collect_sequential.rs @@ -1,7 +1,28 @@ -use crate::generic_values::runner_results::Fallibility; +use crate::generic_values::runner_results::{ + Fallibility, Fallible, Infallible, Stop, fallibility::Never, +}; pub enum SequentialPush { Done, StoppedByWhileCondition, StoppedByError { error: F::Error }, } + +impl SequentialPush { + pub fn sequential_push_to_stop(self) -> Option> { + match self { + SequentialPush::StoppedByWhileCondition => Some(Stop::DueToWhile), + _ => None, + } + } +} + +impl SequentialPush> { + pub fn sequential_push_to_stop(self) -> Option> { + match self { + SequentialPush::Done => None, + SequentialPush::StoppedByWhileCondition => Some(Stop::DueToWhile), + SequentialPush::StoppedByError { error } => Some(Stop::DueToError { error }), + } + } +} diff --git a/src/generic_values/runner_results/fallibility.rs b/src/generic_values/runner_results/fallibility.rs index eafb2970..6a02580a 100644 --- a/src/generic_values/runner_results/fallibility.rs +++ b/src/generic_values/runner_results/fallibility.rs @@ -4,7 +4,8 @@ use crate::generic_values::{ ArbitraryPush, OrderedPush, Reduce, SequentialPush, Stop, StopWithIdx, stop::StopReduce, }, }; -use std::marker::PhantomData; +use alloc::vec::Vec; +use core::marker::PhantomData; pub trait Fallibility: Sized { type Error: Send; @@ -18,6 +19,8 @@ pub trait Fallibility: Sized { fn reduce_to_stop(reduce: Reduce) -> Result, StopReduce> where V: Values; + + fn reduce_results(results: Vec>) -> Result, Self::Error>; } pub struct Infallible; @@ -59,6 +62,15 @@ impl Fallibility for Infallible { Reduce::StoppedByWhileCondition { acc } => Err(StopReduce::DueToWhile { acc }), } } + + fn reduce_results(results: Vec>) -> Result, Self::Error> { + Ok(results + .into_iter() + .map(|x| match x { + Ok(x) => x, + }) + .collect()) + } } pub struct Fallible(PhantomData); @@ -106,6 +118,17 @@ impl Fallibility for Fallible { Reduce::StoppedByError { error } => Err(StopReduce::DueToError { error }), } } + + fn reduce_results(results: Vec>) -> Result, Self::Error> { + let mut ok_results = Vec::with_capacity(results.len()); + for result in results { + match result { + Ok(x) => ok_results.push(x), + Err(e) => return Err(e), + } + } + Ok(ok_results) + } } pub enum Never {} diff --git a/src/generic_values/runner_results/mod.rs b/src/generic_values/runner_results/mod.rs index 89ece609..2d37f8cc 100644 --- a/src/generic_values/runner_results/mod.rs +++ b/src/generic_values/runner_results/mod.rs @@ -9,7 +9,7 @@ mod stop; pub use collect_arbitrary::{ArbitraryPush, ParallelCollectArbitrary, ThreadCollectArbitrary}; pub use collect_ordered::{OrderedPush, ParallelCollect, ThreadCollect}; pub use collect_sequential::SequentialPush; -pub use fallibility::{Fallibility, Fallible, Infallible}; +pub use fallibility::{Fallibility, Fallible, Infallible, Never}; pub use next::{Next, NextSuccess, NextWithIdx}; pub use reduce::Reduce; pub use stop::{Stop, StopReduce, StopWithIdx}; diff --git a/src/generic_values/runner_results/next.rs b/src/generic_values/runner_results/next.rs index b5f13377..94eb9b02 100644 --- a/src/generic_values/runner_results/next.rs +++ b/src/generic_values/runner_results/next.rs @@ -30,7 +30,7 @@ pub enum NextSuccess { } impl NextSuccess { - pub fn reduce(results: Vec) -> Option<(usize, T)> { + pub fn reduce(results: impl IntoIterator) -> Option<(usize, T)> { let mut result = None; let mut idx_bound = usize::MAX; for x in results { diff --git a/src/generic_values/runner_results/reduce.rs b/src/generic_values/runner_results/reduce.rs index db4a9e5a..9704230c 100644 --- a/src/generic_values/runner_results/reduce.rs +++ b/src/generic_values/runner_results/reduce.rs @@ -12,8 +12,18 @@ pub enum Reduce { }, } +impl Reduce { + pub fn into_result(self) -> Result, ::Error> { + match self { + Reduce::Done { acc } => Ok(acc), + Reduce::StoppedByWhileCondition { acc } => Ok(acc), + Reduce::StoppedByError { error } => Err(error), + } + } +} + impl core::fmt::Debug for Reduce { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Done { acc: _ } => f.debug_struct("Done").finish(), Self::StoppedByWhileCondition { acc: _ } => { diff --git a/src/generic_values/transformable_values.rs b/src/generic_values/transformable_values.rs index 631aeccd..2194b3ec 100644 --- a/src/generic_values/transformable_values.rs +++ b/src/generic_values/transformable_values.rs @@ -51,16 +51,6 @@ pub trait TransformableValues: Values { where M: Fn(&mut U, Self::Item) -> O; - // fn u_map2( - // self, - // u: &mut U, - // map: M, - // ) -> impl TransformableValues + 'static - // where - // M: Fn(&mut U, Self::Item) -> O, - // { - // } - fn u_filter( self, u: &mut U, diff --git a/src/generic_values/values.rs b/src/generic_values/values.rs index 8702b675..21de598f 100644 --- a/src/generic_values/values.rs +++ b/src/generic_values/values.rs @@ -2,6 +2,7 @@ use crate::generic_values::runner_results::{ ArbitraryPush, Fallibility, Next, OrderedPush, Reduce, SequentialPush, Stop, StopReduce, StopWithIdx, }; +use alloc::vec::Vec; use orx_concurrent_bag::ConcurrentBag; use orx_fixed_vec::IntoConcurrentPinnedVec; use orx_pinned_vec::PinnedVec; diff --git a/src/generic_values/vector.rs b/src/generic_values/vector.rs index 8bb79a20..be591f07 100644 --- a/src/generic_values/vector.rs +++ b/src/generic_values/vector.rs @@ -5,6 +5,7 @@ use crate::generic_values::{ ArbitraryPush, Fallible, Infallible, Next, OrderedPush, Reduce, SequentialPush, }, }; +use alloc::vec::Vec; use orx_concurrent_bag::ConcurrentBag; use orx_fixed_vec::IntoConcurrentPinnedVec; use orx_pinned_vec::PinnedVec; diff --git a/src/generic_values/vector_result.rs b/src/generic_values/vector_result.rs index f50627b6..ae7df91f 100644 --- a/src/generic_values/vector_result.rs +++ b/src/generic_values/vector_result.rs @@ -2,6 +2,7 @@ use crate::generic_values::Values; use crate::generic_values::runner_results::{ ArbitraryPush, Fallible, Next, OrderedPush, Reduce, SequentialPush, }; +use alloc::vec::Vec; use orx_concurrent_bag::ConcurrentBag; use orx_fixed_vec::IntoConcurrentPinnedVec; use orx_pinned_vec::PinnedVec; diff --git a/src/generic_values/whilst_atom.rs b/src/generic_values/whilst_atom.rs index 550fbcfe..80946956 100644 --- a/src/generic_values/whilst_atom.rs +++ b/src/generic_values/whilst_atom.rs @@ -4,6 +4,7 @@ use crate::generic_values::runner_results::{ use crate::generic_values::whilst_atom_result::WhilstAtomResult; use crate::generic_values::whilst_iterators::WhilstAtomFlatMapIter; use crate::generic_values::{TransformableValues, Values, WhilstOption, WhilstVector}; +use alloc::vec::Vec; use orx_concurrent_bag::ConcurrentBag; use orx_pinned_vec::{IntoConcurrentPinnedVec, PinnedVec}; diff --git a/src/generic_values/whilst_atom_result.rs b/src/generic_values/whilst_atom_result.rs index 7045de49..a5d1a8b2 100644 --- a/src/generic_values/whilst_atom_result.rs +++ b/src/generic_values/whilst_atom_result.rs @@ -2,6 +2,7 @@ use crate::generic_values::Values; use crate::generic_values::runner_results::{ ArbitraryPush, Fallible, Next, OrderedPush, Reduce, SequentialPush, }; +use alloc::vec::Vec; use orx_concurrent_bag::ConcurrentBag; use orx_pinned_vec::{IntoConcurrentPinnedVec, PinnedVec}; diff --git a/src/generic_values/whilst_option.rs b/src/generic_values/whilst_option.rs index fb29dfb2..b9f5b98a 100644 --- a/src/generic_values/whilst_option.rs +++ b/src/generic_values/whilst_option.rs @@ -4,6 +4,7 @@ use crate::generic_values::runner_results::{ use crate::generic_values::whilst_iterators::WhilstOptionFlatMapIter; use crate::generic_values::whilst_option_result::WhilstOptionResult; use crate::generic_values::{TransformableValues, Values, WhilstVector}; +use alloc::vec::Vec; use orx_concurrent_bag::ConcurrentBag; use orx_pinned_vec::{IntoConcurrentPinnedVec, PinnedVec}; diff --git a/src/generic_values/whilst_option_result.rs b/src/generic_values/whilst_option_result.rs index 07b7c287..7be5d455 100644 --- a/src/generic_values/whilst_option_result.rs +++ b/src/generic_values/whilst_option_result.rs @@ -2,6 +2,7 @@ use crate::generic_values::Values; use crate::generic_values::runner_results::{ ArbitraryPush, Fallible, Next, OrderedPush, Reduce, SequentialPush, }; +use alloc::vec::Vec; use orx_concurrent_bag::ConcurrentBag; use orx_pinned_vec::{IntoConcurrentPinnedVec, PinnedVec}; diff --git a/src/generic_values/whilst_vector.rs b/src/generic_values/whilst_vector.rs index b3121a12..240f3bff 100644 --- a/src/generic_values/whilst_vector.rs +++ b/src/generic_values/whilst_vector.rs @@ -7,6 +7,7 @@ use crate::generic_values::{ whilst_iterators::WhilstAtomFlatMapIter, whilst_vector_result::WhilstVectorResult, }; +use alloc::vec::Vec; use orx_concurrent_bag::ConcurrentBag; use orx_fixed_vec::IntoConcurrentPinnedVec; use orx_pinned_vec::PinnedVec; diff --git a/src/generic_values/whilst_vector_result.rs b/src/generic_values/whilst_vector_result.rs index 30031f59..df86b15e 100644 --- a/src/generic_values/whilst_vector_result.rs +++ b/src/generic_values/whilst_vector_result.rs @@ -2,6 +2,7 @@ use crate::generic_values::{ Values, WhilstAtom, runner_results::{ArbitraryPush, Fallible, Next, OrderedPush, Reduce, SequentialPush}, }; +use alloc::vec::Vec; use orx_concurrent_bag::ConcurrentBag; use orx_fixed_vec::IntoConcurrentPinnedVec; use orx_pinned_vec::PinnedVec; diff --git a/src/computations/heap_sort.rs b/src/heap_sort.rs similarity index 98% rename from src/computations/heap_sort.rs rename to src/heap_sort.rs index 964cf29d..193ce42f 100644 --- a/src/computations/heap_sort.rs +++ b/src/heap_sort.rs @@ -1,3 +1,5 @@ +use alloc::vec; +use alloc::vec::Vec; use orx_pinned_vec::PinnedVec; use orx_priority_queue::{BinaryHeap, PriorityQueue}; diff --git a/src/into_par_iter.rs b/src/into_par_iter.rs index 441eb81f..f545f500 100644 --- a/src/into_par_iter.rs +++ b/src/into_par_iter.rs @@ -55,7 +55,7 @@ where I: IntoConcurrentIter, { fn into_par(self) -> Par { - Par::new(Params::default(), self.into_con_iter()) + Par::new(Default::default(), Params::default(), self.into_con_iter()) } } @@ -65,6 +65,7 @@ impl IntoConcurrentIter for Par { type IntoIter = I; fn into_con_iter(self) -> Self::IntoIter { - self.destruct().1 + let (_, _, iter) = self.destruct(); + iter } } diff --git a/src/iter/special_iterators.rs b/src/iter/special_iterators.rs index 6d1753b7..5970372d 100644 --- a/src/iter/special_iterators.rs +++ b/src/iter/special_iterators.rs @@ -6,5 +6,5 @@ pub type ParEmpty = Par, R>; /// Creates an empty parallel iterator which does not yield any elements. pub fn empty() -> ParEmpty { - ParEmpty::new(Default::default(), Default::default()) + ParEmpty::new(Default::default(), Default::default(), Default::default()) } diff --git a/src/iter_into_par_iter.rs b/src/iter_into_par_iter.rs index 03724d42..42211d5d 100644 --- a/src/iter_into_par_iter.rs +++ b/src/iter_into_par_iter.rs @@ -128,6 +128,10 @@ where I::Item: Send + Sync, { fn iter_into_par(self) -> Par, DefaultRunner> { - Par::new(Params::default(), self.iter_into_con_iter()) + Par::new( + Default::default(), + Params::default(), + self.iter_into_con_iter(), + ) } } diff --git a/src/lib.rs b/src/lib.rs index b20c2fbf..728e05b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,13 +10,22 @@ clippy::missing_panics_doc, clippy::todo )] +#![no_std] + +extern crate alloc; + +#[cfg(any(test, feature = "std"))] +extern crate std; mod collect_into; /// Module containing variants of parallel iterators. pub mod computational_variants; -mod computations; +mod default_fns; mod env; +/// Module defining the parallel runner trait and the default parallel runner. +pub mod executor; mod generic_values; +mod heap_sort; mod into_par_iter; /// Module for creating special iterators. pub mod iter; @@ -24,12 +33,13 @@ mod iter_into_par_iter; mod par_iter; mod par_iter_option; mod par_iter_result; +mod par_thread_pool; mod parallel_drainable; mod parallelizable; mod parallelizable_collection; mod parallelizable_collection_mut; mod parameters; -/// Module defining the parallel runner trait and the default parallel runner. +/// ParallelRunner for parallel execution and managing threads. pub mod runner; mod special_type_sets; /// Module defining parallel iterators with mutable access to values distributed to each thread. @@ -48,16 +58,26 @@ pub mod generic_iterator; mod test_utils; pub use collect_into::ParCollectInto; +pub use executor::{DefaultExecutor, ParallelExecutor, ThreadExecutor}; pub use into_par_iter::IntoParIter; pub use iter_into_par_iter::IterIntoParIter; pub use par_iter::ParIter; pub use par_iter_option::ParIterOption; pub use par_iter_result::ParIterResult; +pub use par_thread_pool::ParThreadPool; pub use parallel_drainable::ParallelDrainableOverSlice; pub use parallelizable::Parallelizable; pub use parallelizable_collection::ParallelizableCollection; pub use parallelizable_collection_mut::ParallelizableCollectionMut; pub use parameters::{ChunkSize, IterationOrder, NumThreads, Params}; -pub use runner::{DefaultRunner, ParallelRunner, ThreadRunner}; pub use special_type_sets::Sum; pub use using::ParIterUsing; + +pub use runner::{DefaultPool, DefaultRunner, ParallelRunner, RunnerWithPool, SequentialPool}; + +#[cfg(feature = "pond")] +pub use runner::PondPool; +#[cfg(feature = "std")] +pub use runner::StdDefaultPool; +#[cfg(feature = "yastl")] +pub use runner::YastlPool; diff --git a/src/par_iter.rs b/src/par_iter.rs index d709b94c..329fde74 100644 --- a/src/par_iter.rs +++ b/src/par_iter.rs @@ -1,14 +1,14 @@ -use crate::ParIterResult; use crate::computational_variants::fallible_option::ParOption; use crate::par_iter_option::{IntoOption, ParIterOption}; use crate::par_iter_result::IntoResult; +use crate::runner::{DefaultRunner, ParallelRunner}; use crate::using::{UsingClone, UsingFun}; +use crate::{ParIterResult, ParThreadPool, RunnerWithPool}; use crate::{ ParIterUsing, Params, collect_into::ParCollectInto, - computations::{map_clone, map_copy, map_count, reduce_sum, reduce_unit}, + default_fns::{map_clone, map_copy, map_count, reduce_sum, reduce_unit}, parameters::{ChunkSize, IterationOrder, NumThreads}, - runner::{DefaultRunner, ParallelRunner}, special_type_sets::Sum, }; use core::cmp::Ordering; @@ -249,20 +249,133 @@ where /// Rather than the [`DefaultRunner`], uses the parallel runner `Q` which implements [`ParallelRunner`]. /// + /// See also [`with_pool`]. + /// + /// Parallel runner of each computation can be independently specified using `with_runner`. + /// + /// When not specified the default runner is used, which is: + /// * [`RunnerWithPool`] with [`StdDefaultPool`] when "std" feature is enabled, + /// * [`RunnerWithPool`] with [`SequentialPool`] when "std" feature is disabled. + /// + /// Note that [`StdDefaultPool`] uses standard native threads. + /// + /// When working in a no-std environment, the default runner falls back to sequential. + /// In this case, `RunnerWithPool` using a particular thread pool must be passed in using `with_runner` + /// transformation to achieve parallel computation. + /// + /// [`RunnerWithPool`]: crate::RunnerWithPool + /// [`StdDefaultPool`]: crate::StdDefaultPool + /// [`SequentialPool`]: crate::SequentialPool + /// [`with_pool`]: crate::ParIter::with_pool + /// /// # Examples /// - /// ```ignore + /// ``` /// use orx_parallel::*; /// - /// let inputs = vec![1, 2, 3, 4]; + /// let inputs: Vec<_> = (0..42).collect(); /// - /// // uses the default runner + /// // uses the DefaultRunner + /// // assuming "std" enabled, RunnerWithPool will be used; i.e., native threads /// let sum = inputs.par().sum(); /// - /// // uses the custom parallel runner MyParallelRunner: ParallelRunner - /// let sum = inputs.par().with_runner::().sum(); + /// // equivalent to: + /// let sum2 = inputs.par().with_runner(RunnerWithPool::from(StdDefaultPool::default())).sum(); + /// assert_eq!(sum, sum2); + /// + /// #[cfg(feature = "scoped_threadpool")] + /// { + /// let mut pool = scoped_threadpool::Pool::new(8); + /// + /// // uses the scoped_threadpool::Pool created with 8 threads + /// let sum2 = inputs.par().with_runner(RunnerWithPool::from(&mut pool)).sum(); + /// assert_eq!(sum, sum2); + /// } + /// + /// #[cfg(feature = "rayon-core")] + /// { + /// let pool = rayon_core::ThreadPoolBuilder::new() + /// .num_threads(8) + /// .build() + /// .unwrap(); + /// + /// // uses the rayon-core::ThreadPool created with 8 threads + /// let sum2 = inputs.par().with_runner(RunnerWithPool::from(&pool)).sum(); + /// assert_eq!(sum, sum2); + /// } /// ``` - fn with_runner(self) -> impl ParIter; + fn with_runner(self, runner: Q) -> impl ParIter; + + /// Rather than [`DefaultPool`], uses the parallel runner with the given `pool` implementing + /// [`ParThreadPool`]. + /// + /// See also [`with_runner`]. + /// + /// Thread pool of each computation can be independently specified using `with_pool`. + /// + /// When not specified the default pool is used, which is: + /// * [`StdDefaultPool`] when "std" feature is enabled, + /// * [`SequentialPool`] when "std" feature is disabled. + /// + /// Note that [`StdDefaultPool`] uses standard native threads. + /// + /// When working in a no-std environment, the default pool falls back to sequential. + /// In this case, a thread pool must be passed in using `with_pool` transformation to achieve parallel computation. + /// + /// Note that if a thread pool, say `pool`, is of a type that implements [`ParThreadPool`]; then: + /// * `with_pool` can be called with owned value `with_pool(pool)` for all implementors; but also, + /// * with a shared reference `with_pool(&pool)` for most of the implementations (eg: rayon-core, yastl), and + /// * with a mutable reference `with_pool(&mut pool)` for others (eg: scoped_threadpool). + /// + /// [`DefaultPool`]: crate::DefaultPool + /// [`RunnerWithPool`]: crate::RunnerWithPool + /// [`StdDefaultPool`]: crate::StdDefaultPool + /// [`SequentialPool`]: crate::SequentialPool + /// [`with_runner`]: crate::ParIter::with_runner + /// + /// # Examples + /// + /// ``` + /// use orx_parallel::*; + /// + /// let inputs: Vec<_> = (0..42).collect(); + /// + /// // uses the DefaultPool + /// // assuming "std" enabled, StdDefaultPool will be used; i.e., native threads + /// let sum = inputs.par().sum(); + /// + /// // equivalent to: + /// let sum2 = inputs.par().with_pool(StdDefaultPool::default()).sum(); + /// assert_eq!(sum, sum2); + /// + /// #[cfg(feature = "scoped_threadpool")] + /// { + /// let mut pool = scoped_threadpool::Pool::new(8); + /// + /// // uses the scoped_threadpool::Pool created with 8 threads + /// let sum2 = inputs.par().with_pool(&mut pool).sum(); + /// assert_eq!(sum, sum2); + /// } + /// + /// #[cfg(feature = "rayon-core")] + /// { + /// let pool = rayon_core::ThreadPoolBuilder::new() + /// .num_threads(8) + /// .build() + /// .unwrap(); + /// + /// // uses the rayon-core::ThreadPool created with 8 threads + /// let sum2 = inputs.par().with_pool(&pool).sum(); + /// assert_eq!(sum, sum2); + /// } + /// ``` + fn with_pool( + self, + pool: P, + ) -> impl ParIter, Item = Self::Item> { + let runner = RunnerWithPool::from(pool).with_executor::(); + self.with_runner(runner) + } // using transformations @@ -401,6 +514,7 @@ where /// struct ComputationMetrics { /// thread_metrics: UnsafeCell<[ThreadMetrics; MAX_NUM_THREADS]>, /// } + /// unsafe impl Sync for ComputationMetrics {} /// impl ComputationMetrics { /// fn new() -> Self { /// let mut thread_metrics: [ThreadMetrics; MAX_NUM_THREADS] = Default::default(); @@ -414,7 +528,7 @@ where /// } /// /// impl ComputationMetrics { - /// unsafe fn create_for_thread<'a>(&mut self, thread_idx: usize) -> ThreadMetricsWriter<'a> { + /// unsafe fn create_for_thread<'a>(&self, thread_idx: usize) -> ThreadMetricsWriter<'a> { /// // SAFETY: here we create a mutable variable to the thread_idx-th metrics /// // * If we call this method multiple times with the same index, /// // we create multiple mutable references to the same ThreadMetrics, @@ -441,6 +555,8 @@ where /// .par() /// // SAFETY: we do not call `create_for_thread` externally; /// // it is safe if it is called only by the parallel computation. + /// // Since we unsafely implement Sync for ComputationMetrics, + /// // we must ensure that ComputationMetrics is not used elsewhere. /// .using(|t| unsafe { metrics.create_for_thread(t) }) /// .map(|m: &mut ThreadMetricsWriter<'_>, i| { /// // collect some useful metrics @@ -462,6 +578,8 @@ where /// .num_threads(MAX_NUM_THREADS) /// .sum(); /// + /// assert_eq!(sum, 9100); + /// /// let total_by_metrics: usize = metrics /// .thread_metrics /// .get_mut() @@ -477,8 +595,8 @@ where using: F, ) -> impl ParIterUsing, R, Item = >::Item> where - U: Send + 'static, - F: FnMut(usize) -> U; + U: 'static, + F: Fn(usize) -> U + Sync; /// Converts the [`ParIter`] into [`ParIterUsing`] which will have access to a mutable reference of the /// used variable throughout the computation. @@ -498,7 +616,7 @@ where value: U, ) -> impl ParIterUsing, R, Item = >::Item> where - U: Clone + Send + 'static; + U: Clone + 'static; // transformations into fallible computations diff --git a/src/par_iter_option.rs b/src/par_iter_option.rs index 42575d89..116f8ec3 100644 --- a/src/par_iter_option.rs +++ b/src/par_iter_option.rs @@ -1,6 +1,7 @@ -use crate::computations::{map_count, reduce_sum, reduce_unit}; +use crate::default_fns::{map_count, reduce_sum, reduce_unit}; +use crate::runner::{DefaultRunner, ParallelRunner}; use crate::{ - ChunkSize, DefaultRunner, IterationOrder, NumThreads, ParCollectInto, ParallelRunner, Sum, + ChunkSize, IterationOrder, NumThreads, ParCollectInto, ParThreadPool, RunnerWithPool, Sum, }; use core::cmp::Ordering; @@ -159,8 +160,32 @@ where /// Rather than the [`DefaultRunner`], uses the parallel runner `Q` which implements [`ParallelRunner`]. /// - /// See [`crate::ParIter::with_runner`] for details. - fn with_runner(self) -> impl ParIterOption; + /// See [`ParIter::with_runner`] for details. + /// + /// [`DefaultRunner`]: crate::DefaultRunner + /// [`ParIter::with_runner`]: crate::ParIter::with_runner + fn with_runner( + self, + orchestrator: Q, + ) -> impl ParIterOption; + + /// Rather than [`DefaultPool`], uses the parallel runner with the given `pool` implementing + /// [`ParThreadPool`]. + /// + /// See [`ParIter::with_pool`] for details. + /// + /// [`DefaultPool`]: crate::DefaultPool + /// [`ParIter::with_pool`]: crate::ParIter::with_pool + fn with_pool( + self, + pool: P, + ) -> impl ParIterOption, Item = Self::Item> + where + Self: Sized, + { + let runner = RunnerWithPool::from(pool).with_executor::(); + self.with_runner(runner) + } // computation transformations diff --git a/src/par_iter_result.rs b/src/par_iter_result.rs index 64998afb..7e704fec 100644 --- a/src/par_iter_result.rs +++ b/src/par_iter_result.rs @@ -1,9 +1,7 @@ -use crate::computations::{map_count, reduce_sum, reduce_unit}; -use crate::{ChunkSize, IterationOrder, NumThreads, Sum}; -use crate::{ - DefaultRunner, ParCollectInto, ParIter, ParallelRunner, - generic_values::fallible_iterators::ResultOfIter, -}; +use crate::default_fns::{map_count, reduce_sum, reduce_unit}; +use crate::runner::{DefaultRunner, ParallelRunner}; +use crate::{ChunkSize, IterationOrder, NumThreads, ParThreadPool, RunnerWithPool, Sum}; +use crate::{ParCollectInto, ParIter, generic_values::fallible_iterators::ResultOfIter}; use core::cmp::Ordering; /// A parallel iterator for which the computation either completely succeeds, @@ -202,10 +200,32 @@ where /// Rather than the [`DefaultRunner`], uses the parallel runner `Q` which implements [`ParallelRunner`]. /// /// See [`ParIter::with_runner`] for details. + /// + /// [`DefaultRunner`]: crate::DefaultRunner + /// [`ParIter::with_runner`]: crate::ParIter::with_runner fn with_runner( self, + orchestrator: Q, ) -> impl ParIterResult; + /// Rather than [`DefaultPool`], uses the parallel runner with the given `pool` implementing + /// [`ParThreadPool`]. + /// + /// See [`ParIter::with_pool`] for details. + /// + /// [`DefaultPool`]: crate::DefaultPool + /// [`ParIter::with_pool`]: crate::ParIter::with_pool + fn with_pool( + self, + pool: P, + ) -> impl ParIterResult, Item = Self::Item, Err = Self::Err> + where + Self: Sized, + { + let runner = RunnerWithPool::from(pool).with_executor::(); + self.with_runner(runner) + } + // computation transformations /// Takes a closure `map` and creates a parallel iterator which calls that closure on each element. diff --git a/src/par_thread_pool.rs b/src/par_thread_pool.rs new file mode 100644 index 00000000..cad4278e --- /dev/null +++ b/src/par_thread_pool.rs @@ -0,0 +1,173 @@ +use crate::{generic_values::runner_results::Fallibility, runner::NumSpawned}; +use alloc::vec::Vec; +use core::num::NonZeroUsize; +use orx_concurrent_bag::ConcurrentBag; + +/// A thread pool that can be used for parallel computation. +/// +/// orx_parallel abstracts away the thread pool implementation and can work with different +/// thread pool implementations. +/// +/// Parallel computation will not use any threads outside the pool. +/// Default std thread pool assumes all OS threads are available in the pool. +/// +/// # Examples +/// +/// ## Default std pool +/// +/// **requires std feature** +/// +/// Default parallel runner spawns scoped threads using `std::thread::scope`. +/// +/// ``` +/// use orx_parallel::*; +/// +/// let sum = (0..1000).par().sum(); +/// assert_eq!(sum, 1000 * 999 / 2); +/// +/// // this is equivalent to +/// let sum = (0..1000).par().with_runner(DefaultRunner::default()).sum(); +/// assert_eq!(sum, 1000 * 999 / 2); +/// ``` +/// +/// ## Rayon thread pool +/// +/// **requires rayon feature** +/// +/// The following example demonstrate using a rayon thread pool as the thread provider of +/// the parallel computation. +/// +/// ``` +/// use orx_parallel::*; +/// +/// #[cfg(feature = "rayon-core")] +/// { +/// let pool = rayon::ThreadPoolBuilder::new() +/// .num_threads(4) +/// .build() +/// .unwrap(); +/// +/// // creating a runner for the computation +/// let runner = RunnerWithPool::from(&pool); +/// let sum = (0..1000).par().with_runner(runner).sum(); +/// assert_eq!(sum, 1000 * 999 / 2); +/// +/// // or reuse a runner multiple times (identical under the hood) +/// let mut runner = RunnerWithPool::from(&pool); +/// let sum = (0..1000).par().with_runner(&mut runner).sum(); +/// assert_eq!(sum, 1000 * 999 / 2); +/// } +/// ``` +/// +/// Note that since rayon::ThreadPool::scope only requires a shared reference `&self`, +/// we can concurrently create as many runners as we want from the same thread pool and use them concurrently. +/// +/// ## Scoped thread pool +/// +/// **requires scoped_threadpool feature** +/// +/// The following example demonstrate using a scoped_threadpool thread pool as the thread provider of +/// the parallel computation. +/// +/// ``` +/// use orx_parallel::*; +/// +/// #[cfg(feature = "scoped_threadpool")] +/// { +/// // creating a runner for the computation +/// let mut pool = scoped_threadpool::Pool::new(4); +/// let runner = RunnerWithPool::from(&mut pool); +/// let sum = (0..1000).par().with_runner(runner).sum(); +/// assert_eq!(sum, 1000 * 999 / 2); +/// +/// // or reuse a runner multiple times (identical under the hood) +/// let mut pool = scoped_threadpool::Pool::new(4); +/// let mut runner = RunnerWithPool::from(&mut pool); +/// let sum = (0..1000).par().with_runner(&mut runner).sum(); +/// assert_eq!(sum, 1000 * 999 / 2); +/// } +/// ``` +/// +/// Since scoped_thread_pool::Pool::scoped requires an exclusive reference `&mut self`, +/// we can create one runner from a pool at a time, note use of `&mut pool` in runner creation. +pub trait ParThreadPool { + /// Scope type of the thread pool. + type ScopeRef<'s, 'env, 'scope> + where + 'scope: 's, + 'env: 'scope + 's; + + /// Executes the `work` within scope `s`. + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env; + + /// Executes the scoped computation `f`. + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(Self::ScopeRef<'s, 'env, 'scope>) + Send; + + /// Returns the maximum number of threads available in the pool. + fn max_num_threads(&self) -> NonZeroUsize; +} + +// derived + +pub trait ParThreadPoolCompute: ParThreadPool { + fn map_in_pool( + &mut self, + do_spawn: S, + thread_map: M, + max_num_threads: NonZeroUsize, + ) -> (NumSpawned, Result, F::Error>) + where + F: Fallibility, + S: Fn(NumSpawned) -> bool + Sync, + M: Fn(NumSpawned) -> Result + Sync, + T: Send, + F::Error: Send, + { + let thread_map = &thread_map; + let mut nt = NumSpawned::zero(); + let thread_results = ConcurrentBag::with_fixed_capacity(max_num_threads.into()); + let bag = &thread_results; + self.scoped_computation(|s| { + while do_spawn(nt) { + let num_spawned = nt; + nt.increment(); + let work = move || { + bag.push(thread_map(num_spawned)); + }; + Self::run_in_scope(&s, work); + } + }); + + let thread_results: Vec<_> = thread_results.into_inner().into(); + let result = F::reduce_results(thread_results); + + (nt, result) + } + + fn run_in_pool(&mut self, do_spawn: S, thread_do: F) -> NumSpawned + where + S: Fn(NumSpawned) -> bool + Sync, + F: Fn(NumSpawned) + Sync, + { + let thread_do = &thread_do; + let mut nt = NumSpawned::zero(); + self.scoped_computation(|s| { + while do_spawn(nt) { + let num_spawned = nt; + nt.increment(); + let work = move || thread_do(num_spawned); + Self::run_in_scope(&s, work); + } + }); + nt + } +} + +impl ParThreadPoolCompute for X {} diff --git a/src/parallel_drainable.rs b/src/parallel_drainable.rs index e7b42440..d889f832 100644 --- a/src/parallel_drainable.rs +++ b/src/parallel_drainable.rs @@ -1,6 +1,6 @@ -use crate::{DefaultRunner, Params, computational_variants::Par}; +use crate::{Params, computational_variants::Par, runner::DefaultRunner}; +use core::ops::RangeBounds; use orx_concurrent_iter::ConcurrentDrainableOverSlice; -use std::ops::RangeBounds; /// A type which can create a parallel draining iterator over any of its sub-slices. /// @@ -50,7 +50,7 @@ pub trait ParallelDrainableOverSlice: ConcurrentDrainableOverSlice { where R: RangeBounds, { - Par::new(Params::default(), self.con_drain(range)) + Par::new(Default::default(), Params::default(), self.con_drain(range)) } } diff --git a/src/parallelizable.rs b/src/parallelizable.rs index 69c93ad6..47d1d654 100644 --- a/src/parallelizable.rs +++ b/src/parallelizable.rs @@ -62,7 +62,7 @@ pub trait Parallelizable: ConcurrentIterable { /// assert_eq!(range.par().max(), Some(4)); /// ``` fn par(&self) -> Par<::Iter, DefaultRunner> { - Par::new(Params::default(), self.con_iter()) + Par::new(Default::default(), Params::default(), self.con_iter()) } } diff --git a/src/parallelizable_collection.rs b/src/parallelizable_collection.rs index f2304a33..82992d20 100644 --- a/src/parallelizable_collection.rs +++ b/src/parallelizable_collection.rs @@ -77,7 +77,7 @@ pub trait ParallelizableCollection: ConcurrentCollection { <::Iterable<'_> as ConcurrentIterable>::Iter, DefaultRunner, > { - Par::new(Params::default(), self.con_iter()) + Par::new(Default::default(), Params::default(), self.con_iter()) } } diff --git a/src/parallelizable_collection_mut.rs b/src/parallelizable_collection_mut.rs index d0d103e4..864b37c3 100644 --- a/src/parallelizable_collection_mut.rs +++ b/src/parallelizable_collection_mut.rs @@ -1,5 +1,5 @@ use crate::{ - DefaultRunner, ParIter, ParallelizableCollection, Params, computational_variants::Par, + ParIter, ParallelizableCollection, Params, computational_variants::Par, runner::DefaultRunner, }; use orx_concurrent_iter::ConcurrentCollectionMut; @@ -60,7 +60,7 @@ pub trait ParallelizableCollectionMut: ConcurrentCollectionMut + ParallelizableC /// assert_eq!(&vec, &[1, 2, 13, 14]); /// ``` fn par_mut(&mut self) -> impl ParIter { - Par::new(Params::default(), self.con_iter_mut()) + Par::new(Default::default(), Params::default(), self.con_iter_mut()) } } diff --git a/src/parameters/chunk_size.rs b/src/parameters/chunk_size.rs index 66b4d4ab..49fdd069 100644 --- a/src/parameters/chunk_size.rs +++ b/src/parameters/chunk_size.rs @@ -1,4 +1,4 @@ -use std::num::NonZeroUsize; +use core::num::NonZeroUsize; /// `ChunkSize` represents the batch size of elements each thread will pull from the main iterator once it becomes idle again. /// It is possible to define a minimum or exact chunk size. diff --git a/src/parameters/num_threads.rs b/src/parameters/num_threads.rs index f332c3ff..4716d2d2 100644 --- a/src/parameters/num_threads.rs +++ b/src/parameters/num_threads.rs @@ -1,4 +1,4 @@ -use std::num::NonZeroUsize; +use core::num::NonZeroUsize; /// `NumThreads` represents the degree of parallelization. It is possible to define an upper bound on the number of threads to be used for the parallel computation. /// When set to **1**, the computation will be executed sequentially without any overhead. @@ -37,7 +37,7 @@ pub enum NumThreads { Max(NonZeroUsize), } -const SEQUENTIAL_NUM_THREADS: NonZeroUsize = NonZeroUsize::new(1).expect("seq=1 is positive"); +const ONE: NonZeroUsize = NonZeroUsize::new(1).expect("seq=1 is positive"); impl From for NumThreads { /// Converts the nonnegative integer to number of threads as follows: @@ -58,13 +58,13 @@ impl NumThreads { /// This will lead to a sequential execution of the defined computation on the main thread. /// Both in terms of used resources and computation time, this mode is not similar but **identical** to a sequential execution using the regular sequential `Iterator`s. pub const fn sequential() -> Self { - NumThreads::Max(SEQUENTIAL_NUM_THREADS) + NumThreads::Max(ONE) } /// Returns true if number of threads is set to 1. /// /// Note that in this case the computation will be executed sequentially using regular iterators. pub fn is_sequential(self) -> bool { - matches!(self, Self::Max(n) if n == SEQUENTIAL_NUM_THREADS) + matches!(self, Self::Max(n) if n == ONE) } } diff --git a/src/runner/fixed_chunk_runner/mod.rs b/src/runner/fixed_chunk_runner/mod.rs deleted file mode 100644 index 00f26a23..00000000 --- a/src/runner/fixed_chunk_runner/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod chunk_size; -mod num_threads; -mod parallel_runner; -mod thread_runner; - -pub use parallel_runner::FixedChunkRunner; diff --git a/src/runner/fixed_chunk_runner/num_threads.rs b/src/runner/fixed_chunk_runner/num_threads.rs deleted file mode 100644 index dcfd45fb..00000000 --- a/src/runner/fixed_chunk_runner/num_threads.rs +++ /dev/null @@ -1,58 +0,0 @@ -use crate::{env::MAX_NUM_THREADS_ENV_VARIABLE, parameters::NumThreads}; - -const MAX_UNSET_NUM_THREADS: usize = 8; - -pub fn maximum_num_threads(input_len: Option, num_threads: NumThreads) -> usize { - let max_num_threads = max_num_threads_by_env_variable().unwrap_or(usize::MAX); - match num_threads { - NumThreads::Auto => from_auto_num_threads(input_len), - NumThreads::Max(x) => from_max_num_threads(input_len, x.into()), - } - .max(1) - .min(max_num_threads) -} - -fn from_auto_num_threads(input_len: Option) -> usize { - match std::thread::available_parallelism() { - Err(e) => { - debug_assert!( - false, - "Failed to get maximum available parallelism (std::thread::available_parallelism()): {e}", - ); - input_len - .unwrap_or(MAX_UNSET_NUM_THREADS) - .min(MAX_UNSET_NUM_THREADS) - } - Ok(available_threads) => input_len - .unwrap_or(MAX_UNSET_NUM_THREADS) - .min(available_threads.into()), - } -} - -fn from_max_num_threads(input_len: Option, max_num_threads: usize) -> usize { - // TODO: need to get the number of free threads? - match std::thread::available_parallelism() { - Err(e) => { - debug_assert!( - false, - "Failed to get maximum available parallelism (std::thread::available_parallelism()); falling back to sequential execution.: {e}", - ); - input_len.unwrap_or(max_num_threads).min(max_num_threads) - } - Ok(available_threads) => input_len - .unwrap_or(usize::MAX) - .min(max_num_threads) - .min(available_threads.into()), - } -} - -fn max_num_threads_by_env_variable() -> Option { - match std::env::var(MAX_NUM_THREADS_ENV_VARIABLE) { - Ok(s) => match s.parse::() { - Ok(0) => None, // consistent with .num_threads(0) representing no bound - Ok(x) => Some(x), // set to a positive bound - Err(_e) => None, // not a number, ignored assuming no bound - }, - Err(_e) => None, // not set, no bound - } -} diff --git a/src/runner/implementations/mod.rs b/src/runner/implementations/mod.rs new file mode 100644 index 00000000..46ec3523 --- /dev/null +++ b/src/runner/implementations/mod.rs @@ -0,0 +1,35 @@ +#[cfg(test)] +mod tests; + +mod runner_with_pool; +pub use runner_with_pool::RunnerWithPool; + +mod sequential; +pub use sequential::SequentialPool; + +#[cfg(feature = "std")] +mod std_runner; +#[cfg(feature = "std")] +pub use std_runner::StdDefaultPool; + +#[cfg(feature = "pond")] +mod pond; +#[cfg(feature = "pond")] +pub use pond::PondPool; + +#[cfg(feature = "poolite")] +mod poolite; + +#[cfg(feature = "rayon-core")] +mod rayon_core; + +#[cfg(feature = "scoped-pool")] +mod scoped_pool; + +#[cfg(feature = "scoped_threadpool")] +mod scoped_threadpool; + +#[cfg(feature = "yastl")] +mod yastl; +#[cfg(feature = "yastl")] +pub use yastl::YastlPool; diff --git a/src/runner/implementations/pond.rs b/src/runner/implementations/pond.rs new file mode 100644 index 00000000..be8bf8ed --- /dev/null +++ b/src/runner/implementations/pond.rs @@ -0,0 +1,97 @@ +use crate::par_thread_pool::ParThreadPool; +use core::num::NonZeroUsize; +use pond::{Pool, Scope}; + +/// A wrapper for `pond::Pool` and number of threads it was built with. +/// +/// NOTE: The reason why `pond::Pool` does not directly implement `ParThreadPool` +/// is simply to be able to provide `max_num_threads` which is the argument used +/// to create the pool with. +/// +/// Following constructor of the `pond::Pool` is made available to `PondPool`: +/// * [`PondPool::new_threads_unbounded`] +pub struct PondPool(Pool, NonZeroUsize); + +impl PondPool { + /// Spawn a number of threads. The pool's queue of pending jobs is limited. + /// The backlog is unbounded as in unbounded. + pub fn new_threads_unbounded(num_threads: usize) -> Self { + let num_threads = num_threads.min(1); + let pool = Pool::new_threads_unbounded(num_threads); + #[allow(clippy::missing_panics_doc)] + Self(pool, NonZeroUsize::new(num_threads).expect(">0")) + } + + /// Reference to wrapped `pond::Pool`. + pub fn inner(&self) -> &Pool { + &self.0 + } + + /// Mutable reference to wrapped `pond::Pool`. + pub fn inner_mut(&mut self) -> &mut Pool { + &mut self.0 + } + + /// Returns the wrapped `pond::Pool`. + pub fn into_inner(self) -> Pool { + self.0 + } +} + +impl ParThreadPool for PondPool { + type ScopeRef<'s, 'env, 'scope> + = Scope<'env, 'scope> + where + 'scope: 's, + 'env: 'scope + 's; + + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + s.execute(work); + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(Scope<'env, 'scope>) + Send, + { + self.0.scoped(f) + } + + fn max_num_threads(&self) -> NonZeroUsize { + self.1 + } +} + +impl ParThreadPool for &mut PondPool { + type ScopeRef<'s, 'env, 'scope> + = Scope<'env, 'scope> + where + 'scope: 's, + 'env: 'scope + 's; + + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + s.execute(work); + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(Scope<'env, 'scope>) + Send, + { + self.0.scoped(f) + } + + fn max_num_threads(&self) -> NonZeroUsize { + self.1 + } +} diff --git a/src/runner/implementations/poolite.rs b/src/runner/implementations/poolite.rs new file mode 100644 index 00000000..92a9aeda --- /dev/null +++ b/src/runner/implementations/poolite.rs @@ -0,0 +1,61 @@ +use crate::par_thread_pool::ParThreadPool; +use core::num::NonZeroUsize; +use poolite::{Pool, Scoped}; + +impl ParThreadPool for Pool { + type ScopeRef<'s, 'env, 'scope> + = &'s Scoped<'env, 'scope> + where + 'scope: 's, + 'env: 'scope + 's; + + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + s.push(work); + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(&'s Scoped<'env, 'scope>) + Send, + { + self.scoped(f); + } + + fn max_num_threads(&self) -> NonZeroUsize { + NonZeroUsize::new(self.threads_future().max(1)).expect(">0") + } +} + +impl ParThreadPool for &Pool { + type ScopeRef<'s, 'env, 'scope> + = &'s Scoped<'env, 'scope> + where + 'scope: 's, + 'env: 'scope + 's; + + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + s.push(work); + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(&'s Scoped<'env, 'scope>) + Send, + { + self.scoped(f); + } + + fn max_num_threads(&self) -> NonZeroUsize { + NonZeroUsize::new(self.threads_future().max(1)).expect(">0") + } +} diff --git a/src/runner/implementations/rayon_core.rs b/src/runner/implementations/rayon_core.rs new file mode 100644 index 00000000..062f9482 --- /dev/null +++ b/src/runner/implementations/rayon_core.rs @@ -0,0 +1,61 @@ +use crate::par_thread_pool::ParThreadPool; +use core::num::NonZeroUsize; +use rayon_core::ThreadPool; + +impl ParThreadPool for ThreadPool { + type ScopeRef<'s, 'env, 'scope> + = &'s rayon_core::Scope<'scope> + where + 'scope: 's, + 'env: 'scope + 's; + + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + s.spawn(move |_| work()); + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(&'s rayon_core::Scope<'scope>) + Send, + { + self.scope(f) + } + + fn max_num_threads(&self) -> NonZeroUsize { + NonZeroUsize::new(self.current_num_threads().max(1)).expect(">0") + } +} + +impl ParThreadPool for &rayon_core::ThreadPool { + type ScopeRef<'s, 'env, 'scope> + = &'s rayon_core::Scope<'scope> + where + 'scope: 's, + 'env: 'scope + 's; + + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + s.spawn(move |_| work()); + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(&'s rayon_core::Scope<'scope>) + Send, + { + self.scope(f) + } + + fn max_num_threads(&self) -> NonZeroUsize { + NonZeroUsize::new(self.current_num_threads().max(1)).expect(">0") + } +} diff --git a/src/runner/implementations/runner_with_pool.rs b/src/runner/implementations/runner_with_pool.rs new file mode 100644 index 00000000..a96585d8 --- /dev/null +++ b/src/runner/implementations/runner_with_pool.rs @@ -0,0 +1,202 @@ +use crate::{DefaultExecutor, ParThreadPool, ParallelExecutor, runner::ParallelRunner}; +use core::marker::PhantomData; + +/// Parallel runner with a given pool of type `P` and parallel executor of `R`. +/// +/// A `RunnerWithPool` can always be created from owned `pool` implementing [`ParThreadPool`], but also from +/// * `&pool` in most cases, +/// * `&mut pool` in others. +/// +/// Note that default parallel runner; i.e., [`DefaultRunner`] is: +/// * `RunnerWithPool` when "std" feature is enabled, +/// * `RunnerWithPool` when "std" feature is disabled. +/// +/// [`DefaultRunner`]: crate::DefaultRunner +/// +/// # Examples +/// +/// ``` +/// use orx_parallel::*; +/// +/// // parallel computation generic over parallel runner; and hence, the thread pool +/// fn run_with_runner(runner: R, input: &[usize]) -> Vec { +/// input +/// .par() +/// .with_runner(runner) +/// .flat_map(|x| [*x, 2 * x, x / 7]) +/// .map(|x| x.to_string()) +/// .collect() +/// } +/// +/// let vec: Vec<_> = (0..42).collect(); +/// let input = vec.as_slice(); +/// +/// // runs sequentially on the main thread +/// let runner = RunnerWithPool::from(SequentialPool); +/// let expected = run_with_runner(runner, input); +/// +/// // uses native threads +/// let runner = RunnerWithPool::from(StdDefaultPool::default()); +/// let result = run_with_runner(runner, input); +/// assert_eq!(&expected, &result); +/// +/// // uses rayon-core ThreadPool with 8 threads +/// #[cfg(feature = "rayon-core")] +/// { +/// let pool = rayon_core::ThreadPoolBuilder::new() +/// .num_threads(8) +/// .build() +/// .unwrap(); +/// let result = run_with_runner(RunnerWithPool::from(&pool), input); +/// assert_eq!(&expected, &result); +/// } +/// +/// // uses scoped-pool Pool with 8 threads +/// #[cfg(feature = "scoped-pool")] +/// { +/// let pool = scoped_pool::Pool::new(8); +/// let result = run_with_runner(RunnerWithPool::from(&pool), input); +/// assert_eq!(&expected, &result); +/// } +/// +/// // uses scoped_threadpool Pool with 8 threads +/// #[cfg(feature = "scoped_threadpool")] +/// { +/// let mut pool = scoped_threadpool::Pool::new(8); +/// let result = run_with_runner(RunnerWithPool::from(&mut pool), input); // requires &mut pool +/// assert_eq!(&expected, &result); +/// } +/// +/// // uses yastl Pool wrapped as YastlPool with 8 threads +/// #[cfg(feature = "yastl")] +/// { +/// let pool = YastlPool::new(8); +/// let result = run_with_runner(RunnerWithPool::from(&pool), input); +/// assert_eq!(&expected, &result); +/// } +/// +/// // uses pond Pool wrapped as PondPool with 8 threads +/// #[cfg(feature = "pond")] +/// { +/// let mut pool = PondPool::new_threads_unbounded(8); +/// let result = run_with_runner(RunnerWithPool::from(&mut pool), input); // requires &mut pool +/// assert_eq!(&expected, &result); +/// } +/// +/// // uses poolite Pool with 8 threads +/// #[cfg(feature = "poolite")] +/// { +/// let pool = poolite::Pool::with_builder(poolite::Builder::new().min(8).max(8)).unwrap(); +/// let result = run_with_runner(RunnerWithPool::from(&pool), input); +/// assert_eq!(&expected, &result); +/// } +/// ``` +pub struct RunnerWithPool +where + P: ParThreadPool, + R: ParallelExecutor, +{ + pool: P, + runner: PhantomData, +} + +impl Default for RunnerWithPool +where + P: ParThreadPool + Default, + R: ParallelExecutor, +{ + fn default() -> Self { + Self { + pool: Default::default(), + runner: PhantomData, + } + } +} + +impl From

for RunnerWithPool { + fn from(pool: P) -> Self { + Self { + pool, + runner: PhantomData, + } + } +} + +impl RunnerWithPool +where + P: ParThreadPool, + R: ParallelExecutor, +{ + /// Converts the runner into the wrapped underlying pool. + /// + /// Note that a `RunnerWithPool` can always be created from owned `pool`, but also from + /// * `&pool` in most cases, + /// * `&mut pool` in others. + /// + /// This function is only relevant when the runner is created from owned pool, in which case + /// `into_inner_pool` can be used to get back ownership of the pool. + /// + /// # Example + /// + /// The following example demonstrates the use case for rayon-core thread pool; however, it + /// holds for all thread pool implementations. + /// + /// ``` + /// use orx_parallel::*; + /// + /// let vec: Vec<_> = (0..42).collect(); + /// let input = vec.as_slice(); + /// + /// #[cfg(feature = "rayon-core")] + /// { + /// let pool = rayon_core::ThreadPoolBuilder::new() + /// .num_threads(8) + /// .build() + /// .unwrap(); + /// + /// // create runner owning the pool + /// let mut runner = RunnerWithPool::from(pool); + /// + /// // use runner, and hence the pool, in parallel computations + /// let sum = input.par().with_runner(&mut runner).sum(); + /// let max = input.par().with_runner(&mut runner).max(); + /// let txt: Vec<_> = input + /// .par() + /// .with_runner(&mut runner) + /// .map(|x| x.to_string()) + /// .collect(); + /// + /// // get back ownership of the pool + /// let pool: rayon_core::ThreadPool = runner.into_inner_pool(); + /// } + /// ``` + pub fn into_inner_pool(self) -> P { + self.pool + } + + /// Converts the runner into one using the [`ParallelExecutor`] `Q` rather than `R`. + pub fn with_executor(self) -> RunnerWithPool { + RunnerWithPool { + pool: self.pool, + runner: PhantomData, + } + } +} + +impl ParallelRunner for RunnerWithPool +where + P: ParThreadPool, + R: ParallelExecutor, +{ + type Executor = R; + + type ThreadPool = P; + + fn thread_pool(&self) -> &Self::ThreadPool { + &self.pool + } + + fn thread_pool_mut(&mut self) -> &mut Self::ThreadPool { + &mut self.pool + } +} diff --git a/src/runner/implementations/scoped_pool.rs b/src/runner/implementations/scoped_pool.rs new file mode 100644 index 00000000..17e4ceed --- /dev/null +++ b/src/runner/implementations/scoped_pool.rs @@ -0,0 +1,61 @@ +use crate::par_thread_pool::ParThreadPool; +use core::num::NonZeroUsize; +use scoped_pool::{Pool, Scope}; + +impl ParThreadPool for Pool { + type ScopeRef<'s, 'env, 'scope> + = &'s Scope<'scope> + where + 'scope: 's, + 'env: 'scope + 's; + + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + s.execute(work); + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(&Scope<'scope>) + Send, + { + self.scoped(f) + } + + fn max_num_threads(&self) -> NonZeroUsize { + NonZeroUsize::new(self.workers().max(1)).expect(">0") + } +} + +impl ParThreadPool for &Pool { + type ScopeRef<'s, 'env, 'scope> + = &'s Scope<'scope> + where + 'scope: 's, + 'env: 'scope + 's; + + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + s.execute(work); + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(&Scope<'scope>) + Send, + { + self.scoped(f) + } + + fn max_num_threads(&self) -> core::num::NonZeroUsize { + NonZeroUsize::new(self.workers().max(1)).expect(">0") + } +} diff --git a/src/runner/implementations/scoped_threadpool.rs b/src/runner/implementations/scoped_threadpool.rs new file mode 100644 index 00000000..79852f0a --- /dev/null +++ b/src/runner/implementations/scoped_threadpool.rs @@ -0,0 +1,61 @@ +use crate::par_thread_pool::ParThreadPool; +use core::num::NonZeroUsize; +use scoped_threadpool::Pool; + +impl ParThreadPool for Pool { + type ScopeRef<'s, 'env, 'scope> + = &'s scoped_threadpool::Scope<'env, 'scope> + where + 'scope: 's, + 'env: 'scope + 's; + + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + s.execute(work); + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(&'s scoped_threadpool::Scope<'env, 'scope>) + Send, + { + self.scoped(f) + } + + fn max_num_threads(&self) -> NonZeroUsize { + NonZeroUsize::new((self.thread_count() as usize).max(1)).expect(">0") + } +} + +impl ParThreadPool for &mut Pool { + type ScopeRef<'s, 'env, 'scope> + = &'s scoped_threadpool::Scope<'env, 'scope> + where + 'scope: 's, + 'env: 'scope + 's; + + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + s.execute(work); + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(&'s scoped_threadpool::Scope<'env, 'scope>) + Send, + { + self.scoped(f) + } + + fn max_num_threads(&self) -> NonZeroUsize { + NonZeroUsize::new((self.thread_count() as usize).max(1)).expect(">0") + } +} diff --git a/src/runner/implementations/sequential.rs b/src/runner/implementations/sequential.rs new file mode 100644 index 00000000..eb09a8e0 --- /dev/null +++ b/src/runner/implementations/sequential.rs @@ -0,0 +1,43 @@ +use crate::ParThreadPool; +use core::num::NonZeroUsize; + +/// A 'thread pool' with [`max_num_threads`] of 1. +/// All computations using this thread pool are executed sequentially by the main thread. +/// +/// This is the default thread pool used when "std" feature is disabled. +/// Note that the thread pool to be used for a parallel computation can be set by the +/// [`with_runner`] transformation separately for each parallel iterator. +/// +/// [`max_num_threads`]: ParThreadPool::max_num_threads +/// [`with_runner`]: crate::ParIter::with_runner +#[derive(Default)] +pub struct SequentialPool; + +impl ParThreadPool for SequentialPool { + type ScopeRef<'s, 'env, 'scope> + = () + where + 'scope: 's, + 'env: 'scope + 's; + + fn run_in_scope<'s, 'env, 'scope, W>(_: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + work() + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(()) + Send, + { + f(()) + } + + fn max_num_threads(&self) -> NonZeroUsize { + NonZeroUsize::new(1).expect(">0") + } +} diff --git a/src/runner/implementations/std_runner.rs b/src/runner/implementations/std_runner.rs new file mode 100644 index 00000000..d2c17cac --- /dev/null +++ b/src/runner/implementations/std_runner.rs @@ -0,0 +1,98 @@ +use crate::par_thread_pool::ParThreadPool; +use core::num::NonZeroUsize; + +const MAX_UNSET_NUM_THREADS: NonZeroUsize = NonZeroUsize::new(8).expect(">0"); + +/// Native standard thread pool. +/// +/// This is the default thread pool used when "std" feature is enabled. +/// Note that the thread pool to be used for a parallel computation can be set by the +/// [`with_runner`] transformation separately for each parallel iterator. +/// +/// Uses `std::thread::scope` and `scope.spawn(..)` to distribute work to threads. +/// +/// Value of [`max_num_threads`] is determined as the minimum of: +/// +/// * the available parallelism of the host obtained via `std::thread::available_parallelism()`, and +/// * the upper bound set by the environment variable "ORX_PARALLEL_MAX_NUM_THREADS", when set. +/// +/// [`max_num_threads`]: ParThreadPool::max_num_threads +/// [`with_runner`]: crate::ParIter::with_runner +pub struct StdDefaultPool { + max_num_threads: NonZeroUsize, +} + +impl Default for StdDefaultPool { + fn default() -> Self { + let env_max_num_threads = crate::env::max_num_threads_by_env_variable(); + + let ava_max_num_threads = std::thread::available_parallelism().ok(); + + let max_num_threads = match (env_max_num_threads, ava_max_num_threads) { + (Some(env), Some(ava)) => env.min(ava), + (Some(env), None) => env, + (None, Some(ava)) => ava, + (None, None) => MAX_UNSET_NUM_THREADS, + }; + + Self { max_num_threads } + } +} + +impl ParThreadPool for StdDefaultPool { + type ScopeRef<'s, 'env, 'scope> + = &'s std::thread::Scope<'s, 'env> + where + 'scope: 's, + 'env: 'scope + 's; + + fn max_num_threads(&self) -> NonZeroUsize { + self.max_num_threads + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(&'s std::thread::Scope<'s, 'env>) + Send, + { + std::thread::scope(f) + } + + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + s.spawn(work); + } +} + +impl ParThreadPool for &StdDefaultPool { + type ScopeRef<'s, 'env, 'scope> + = &'s std::thread::Scope<'s, 'env> + where + 'scope: 's, + 'env: 'scope + 's; + + fn max_num_threads(&self) -> NonZeroUsize { + self.max_num_threads + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(&'s std::thread::Scope<'s, 'env>) + Send, + { + std::thread::scope(f) + } + + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + s.spawn(work); + } +} diff --git a/src/runner/implementations/tests/mod.rs b/src/runner/implementations/tests/mod.rs new file mode 100644 index 00000000..16a91f0a --- /dev/null +++ b/src/runner/implementations/tests/mod.rs @@ -0,0 +1,26 @@ +#[cfg(feature = "pond")] +mod pond; + +#[cfg(feature = "poolite")] +mod poolite; + +#[cfg(feature = "rayon-core")] +mod rayon_core; + +#[cfg(feature = "scoped-pool")] +mod scoped_pool; + +#[cfg(feature = "scoped_threadpool")] +mod scoped_threadpool; + +#[cfg(feature = "std")] +mod std; + +#[cfg(feature = "yastl")] +mod yastl; + +mod sequential; + +mod utils; + +use utils::run_map; diff --git a/src/runner/implementations/tests/pond.rs b/src/runner/implementations/tests/pond.rs new file mode 100644 index 00000000..4727956e --- /dev/null +++ b/src/runner/implementations/tests/pond.rs @@ -0,0 +1,23 @@ +use super::run_map; +use crate::{ + IterationOrder, + runner::implementations::{PondPool, RunnerWithPool}, +}; +use test_case::test_matrix; + +#[cfg(miri)] +const N: [usize; 2] = [37, 125]; +#[cfg(not(miri))] +const N: [usize; 2] = [1025, 4735]; + +#[test_matrix( + [0, 1, N[0], N[1]], + [1, 4], + [1, 64], + [IterationOrder::Ordered, IterationOrder::Arbitrary]) +] +fn pool_pond_map(n: usize, nt: usize, chunk: usize, ordering: IterationOrder) { + let mut pool = PondPool::new_threads_unbounded(nt); + let orch: RunnerWithPool<_> = (&mut pool).into(); + run_map(n, chunk, ordering, orch); +} diff --git a/src/runner/implementations/tests/poolite.rs b/src/runner/implementations/tests/poolite.rs new file mode 100644 index 00000000..67122a6e --- /dev/null +++ b/src/runner/implementations/tests/poolite.rs @@ -0,0 +1,21 @@ +use super::run_map; +use crate::{IterationOrder, runner::implementations::RunnerWithPool}; +use poolite::{Builder, Pool}; +use test_case::test_matrix; + +#[cfg(miri)] +const N: [usize; 2] = [37, 125]; +#[cfg(not(miri))] +const N: [usize; 2] = [1025, 4735]; + +#[test_matrix( + [0, 1, N[0], N[1]], + [1, 4], + [1, 64], + [IterationOrder::Ordered, IterationOrder::Arbitrary]) +] +fn pool_poolite_map(n: usize, nt: usize, chunk: usize, ordering: IterationOrder) { + let pool = Pool::with_builder(Builder::new().max(nt).min(nt)).unwrap(); + let orch: RunnerWithPool<_> = (&pool).into(); + run_map(n, chunk, ordering, orch); +} diff --git a/src/runner/implementations/tests/rayon_core.rs b/src/runner/implementations/tests/rayon_core.rs new file mode 100644 index 00000000..6ffb0189 --- /dev/null +++ b/src/runner/implementations/tests/rayon_core.rs @@ -0,0 +1,25 @@ +use super::run_map; +use crate::{IterationOrder, runner::implementations::RunnerWithPool}; +use test_case::test_matrix; + +#[cfg(miri)] +const N: [usize; 2] = [37, 125]; +#[cfg(not(miri))] +const N: [usize; 2] = [1025, 4735]; + +// TODO: rayon_core pool fails the miri test (integer-to-pointer cast crossbeam-epoch-0.9.18/src/atomic.rs:204:11) +#[cfg(not(miri))] +#[test_matrix( + [0, 1, N[0], N[1]], + [1, 4], + [1, 64], + [IterationOrder::Ordered, IterationOrder::Arbitrary]) +] +fn pool_rayon_map(n: usize, nt: usize, chunk: usize, ordering: IterationOrder) { + let pool = rayon_core::ThreadPoolBuilder::new() + .num_threads(nt) + .build() + .unwrap(); + let orch: RunnerWithPool<_> = (&pool).into(); + run_map(n, chunk, ordering, orch); +} diff --git a/src/runner/implementations/tests/scoped_pool.rs b/src/runner/implementations/tests/scoped_pool.rs new file mode 100644 index 00000000..bab42948 --- /dev/null +++ b/src/runner/implementations/tests/scoped_pool.rs @@ -0,0 +1,21 @@ +use super::run_map; +use crate::{IterationOrder, runner::implementations::RunnerWithPool}; +use scoped_pool::Pool; +use test_case::test_matrix; + +#[cfg(miri)] +const N: [usize; 2] = [37, 125]; +#[cfg(not(miri))] +const N: [usize; 2] = [1025, 4735]; + +#[test_matrix( + [0, 1, N[0], N[1]], + [1, 4], + [1, 64], + [IterationOrder::Ordered, IterationOrder::Arbitrary]) +] +fn pool_scoped_pool_map(n: usize, nt: usize, chunk: usize, ordering: IterationOrder) { + let pool = Pool::new(nt); + let orch = RunnerWithPool::from(&pool); + run_map(n, chunk, ordering, orch); +} diff --git a/src/runner/implementations/tests/scoped_threadpool.rs b/src/runner/implementations/tests/scoped_threadpool.rs new file mode 100644 index 00000000..47f0b2bb --- /dev/null +++ b/src/runner/implementations/tests/scoped_threadpool.rs @@ -0,0 +1,21 @@ +use super::run_map; +use crate::{IterationOrder, runner::implementations::RunnerWithPool}; +use scoped_threadpool::Pool; +use test_case::test_matrix; + +#[cfg(miri)] +const N: [usize; 2] = [37, 125]; +#[cfg(not(miri))] +const N: [usize; 2] = [1025, 4735]; + +#[test_matrix( + [0, 1, N[0], N[1]], + [1, 4], + [1, 64], + [IterationOrder::Ordered, IterationOrder::Arbitrary]) +] +fn pool_scoped_threadpool_map(n: usize, nt: usize, chunk: usize, ordering: IterationOrder) { + let mut pool = Pool::new(nt as u32); + let orch: RunnerWithPool<_> = (&mut pool).into(); + run_map(n, chunk, ordering, orch); +} diff --git a/src/runner/implementations/tests/sequential.rs b/src/runner/implementations/tests/sequential.rs new file mode 100644 index 00000000..2dbbf13e --- /dev/null +++ b/src/runner/implementations/tests/sequential.rs @@ -0,0 +1,19 @@ +use super::run_map; +use crate::{IterationOrder, RunnerWithPool, runner::implementations::sequential::SequentialPool}; +use test_case::test_matrix; + +#[cfg(miri)] +const N: [usize; 2] = [37, 125]; +#[cfg(not(miri))] +const N: [usize; 2] = [1025, 4735]; + +#[test_matrix( + [0, 1, N[0], N[1]], + [1, 4], + [1, 64], + [IterationOrder::Ordered, IterationOrder::Arbitrary]) +] +fn pool_scoped_threadpool_map(n: usize, _: usize, chunk: usize, ordering: IterationOrder) { + let orch = RunnerWithPool::from(SequentialPool); + run_map(n, chunk, ordering, orch); +} diff --git a/src/runner/implementations/tests/std.rs b/src/runner/implementations/tests/std.rs new file mode 100644 index 00000000..50693432 --- /dev/null +++ b/src/runner/implementations/tests/std.rs @@ -0,0 +1,26 @@ +use super::run_map; +use crate::{ + DefaultRunner, IterationOrder, RunnerWithPool, + runner::implementations::std_runner::StdDefaultPool, +}; +use test_case::test_matrix; + +#[cfg(miri)] +const N: [usize; 2] = [37, 125]; +#[cfg(not(miri))] +const N: [usize; 2] = [1025, 4735]; + +#[test_matrix( + [0, 1, N[0], N[1]], + [1, 4], + [1, 64], + [IterationOrder::Ordered, IterationOrder::Arbitrary]) +] +fn pool_scoped_threadpool_map(n: usize, _: usize, chunk: usize, ordering: IterationOrder) { + let orch = DefaultRunner::default(); + run_map(n, chunk, ordering, orch); + + let pool = StdDefaultPool::default(); + let orch: RunnerWithPool<_> = (&pool).into(); + run_map(n, chunk, ordering, orch); +} diff --git a/src/runner/implementations/tests/utils.rs b/src/runner/implementations/tests/utils.rs new file mode 100644 index 00000000..af9fdd9c --- /dev/null +++ b/src/runner/implementations/tests/utils.rs @@ -0,0 +1,38 @@ +use crate::{IntoParIter, IterationOrder, ParIter, runner::ParallelRunner}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use orx_pinned_vec::PinnedVec; +use orx_split_vec::SplitVec; + +pub fn run_map(n: usize, chunk: usize, ordering: IterationOrder, mut orch: impl ParallelRunner) { + let offset = 33; + + let input: Vec<_> = (0..n).map(|x| x.to_string()).collect(); + let map = |x: String| format!("{}!", x); + + let mut output = SplitVec::with_doubling_growth_and_max_concurrent_capacity(); + let mut expected = Vec::new(); + + for i in 0..offset { + let value = || map(i.to_string()); + output.push(value()); + expected.push(value()); + } + expected.extend(input.clone().into_iter().map(|x| map(x))); + + let mut output = input + .into_par() + .with_runner(&mut orch) + .chunk_size(chunk) + .iteration_order(ordering) + .map(map) + .collect_into(output); + + if matches!(ordering, IterationOrder::Arbitrary) { + expected.sort(); + output.sort(); + } + + assert_eq!(expected, output.to_vec()); +} diff --git a/src/runner/implementations/tests/yastl.rs b/src/runner/implementations/tests/yastl.rs new file mode 100644 index 00000000..f20e8529 --- /dev/null +++ b/src/runner/implementations/tests/yastl.rs @@ -0,0 +1,28 @@ +use super::run_map; +use crate::{ + IterationOrder, + runner::implementations::{RunnerWithPool, YastlPool}, +}; +use test_case::test_matrix; +use yastl::ThreadConfig; + +#[cfg(miri)] +const N: [usize; 2] = [37, 125]; +#[cfg(not(miri))] +const N: [usize; 2] = [1025, 4735]; + +#[test_matrix( + [0, 1, N[0], N[1]], + [1, 4], + [1, 64], + [IterationOrder::Ordered, IterationOrder::Arbitrary]) +] +fn pool_yastl_map(n: usize, nt: usize, chunk: usize, ordering: IterationOrder) { + let pool = YastlPool::new(nt); + let orch: RunnerWithPool<_> = (&pool).into(); + run_map(n, chunk, ordering, orch); + + let pool = YastlPool::with_config(nt, ThreadConfig::new()); + let orch: RunnerWithPool<_> = (pool).into(); + run_map(n, chunk, ordering, orch); +} diff --git a/src/runner/implementations/yastl.rs b/src/runner/implementations/yastl.rs new file mode 100644 index 00000000..77ce5094 --- /dev/null +++ b/src/runner/implementations/yastl.rs @@ -0,0 +1,106 @@ +use crate::ParThreadPool; +use core::num::NonZeroUsize; +use yastl::{Pool, Scope, ThreadConfig}; + +/// A wrapper for `yastl::Pool` and number of threads it was built with. +/// +/// NOTE: The reason why `yastl::Pool` does not directly implement `ParThreadPool` +/// is simply to be able to provide `max_num_threads` which is the argument used +/// to create the pool with. +/// +/// Two constructors of the `yastl::Pool` are made available to `YastlPool`: +/// * [`YastlPool::new`] +/// * [`YastlPool::with_config`] +pub struct YastlPool(Pool, NonZeroUsize); + +impl YastlPool { + /// Create a new Pool that will execute it's tasks on `num_threads` worker threads. + pub fn new(num_threads: usize) -> Self { + let num_threads = num_threads.min(1); + let pool = Pool::new(num_threads); + #[allow(clippy::missing_panics_doc)] + Self(pool, NonZeroUsize::new(num_threads).expect(">0")) + } + + /// Create a new Pool that will execute it's tasks on `num_threads` worker threads and + /// spawn them using the given `config`. + pub fn with_config(num_threads: usize, config: ThreadConfig) -> Self { + let num_threads = num_threads.min(1); + let pool = Pool::with_config(num_threads, config); + #[allow(clippy::missing_panics_doc)] + Self(pool, NonZeroUsize::new(num_threads).expect(">0")) + } + + /// Reference to wrapped `yastl::Pool`. + pub fn inner(&self) -> &Pool { + &self.0 + } + + /// Mutable reference to wrapped `yastl::Pool`. + pub fn inner_mut(&mut self) -> &mut Pool { + &mut self.0 + } + + /// Returns the wrapped `yastl::Pool`. + pub fn into_inner(self) -> Pool { + self.0 + } +} + +impl ParThreadPool for YastlPool { + type ScopeRef<'s, 'env, 'scope> + = &'s Scope<'scope> + where + 'scope: 's, + 'env: 'scope + 's; + + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + s.execute(work); + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(&'s Scope<'scope>) + Send, + { + self.0.scoped(f) + } + + fn max_num_threads(&self) -> NonZeroUsize { + self.1 + } +} + +impl ParThreadPool for &YastlPool { + type ScopeRef<'s, 'env, 'scope> + = &'s Scope<'scope> + where + 'scope: 's, + 'env: 'scope + 's; + + fn run_in_scope<'s, 'env, 'scope, W>(s: &Self::ScopeRef<'s, 'env, 'scope>, work: W) + where + 'scope: 's, + 'env: 'scope + 's, + W: Fn() + Send + 'scope + 'env, + { + s.execute(work); + } + + fn scoped_computation<'env, 'scope, F>(&'env mut self, f: F) + where + 'env: 'scope, + for<'s> F: FnOnce(&'s Scope<'scope>) + Send, + { + self.0.scoped(f) + } + + fn max_num_threads(&self) -> NonZeroUsize { + self.1 + } +} diff --git a/src/runner/mod.rs b/src/runner/mod.rs index 0d7b8fe0..2adeb4c9 100644 --- a/src/runner/mod.rs +++ b/src/runner/mod.rs @@ -1,19 +1,41 @@ mod computation_kind; -mod fixed_chunk_runner; +mod implementations; +mod num_spawned; mod parallel_runner; -pub(crate) mod parallel_runner_compute; -mod thread_runner; -mod thread_runner_compute; + +pub(crate) use parallel_runner::{SharedStateOf, ThreadRunnerOf}; pub use computation_kind::ComputationKind; +pub use implementations::{RunnerWithPool, SequentialPool}; +pub use num_spawned::NumSpawned; pub use parallel_runner::ParallelRunner; -pub(crate) use parallel_runner_compute::ParallelRunnerCompute; -pub use thread_runner::ThreadRunner; -/// Default parallel runner. +#[cfg(feature = "pond")] +pub use implementations::PondPool; + +#[cfg(feature = "std")] +pub use implementations::StdDefaultPool; + +#[cfg(feature = "yastl")] +pub use implementations::YastlPool; + +// DEFAULT + +/// Default pool used by orx-parallel computations: +/// +/// * [`StdDefaultPool`] when "std" feature is enabled, +/// * [`SequentialPool`] otherwise. +#[cfg(feature = "std")] +pub type DefaultPool = StdDefaultPool; +/// Default pool used by orx-parallel computations: /// -/// Unless explicitly set to another parallel runner by [`with_runner`] method, -/// parallel computations will be executed using the default parallel runner. +/// * `StdDefaultPool` when "std" feature is enabled, +/// * [`SequentialPool`] otherwise. +#[cfg(not(feature = "std"))] +pub type DefaultPool = SequentialPool; + +/// Default runner used by orx-parallel computations, using the [`DefaultPool`]: /// -/// [`with_runner`]: crate::ParIter::with_runner -pub type DefaultRunner = fixed_chunk_runner::FixedChunkRunner; +/// * [`RunnerWithPool`] with [`StdDefaultPool`] when "std" feature is enabled, +/// * [`RunnerWithPool`] with [`SequentialPool`] otherwise. +pub type DefaultRunner = RunnerWithPool; diff --git a/src/runner/num_spawned.rs b/src/runner/num_spawned.rs new file mode 100644 index 00000000..5773feab --- /dev/null +++ b/src/runner/num_spawned.rs @@ -0,0 +1,28 @@ +/// Number of spawned threads to execute a parallel computation. +#[derive(Clone, Copy)] +pub struct NumSpawned(usize); + +impl NumSpawned { + /// Zero. + pub fn zero() -> Self { + Self(0) + } + + /// Adds one to the spawned thread count. + pub fn increment(&mut self) { + self.0 += 1; + } + + /// Converts into usize. + pub fn into_inner(self) -> usize { + self.0 + } +} + +impl core::ops::Rem for NumSpawned { + type Output = usize; + + fn rem(self, rhs: Self) -> Self::Output { + self.0 % rhs.0 + } +} diff --git a/src/runner/parallel_runner.rs b/src/runner/parallel_runner.rs index 175b5971..b3b8f09a 100644 --- a/src/runner/parallel_runner.rs +++ b/src/runner/parallel_runner.rs @@ -1,38 +1,151 @@ -use super::{computation_kind::ComputationKind, thread_runner::ThreadRunner}; -use crate::parameters::Params; +use crate::{ + NumThreads, ParallelExecutor, Params, + generic_values::runner_results::{Fallibility, Infallible, Never}, + par_thread_pool::{ParThreadPool, ParThreadPoolCompute}, + runner::{ComputationKind, NumSpawned}, +}; +use alloc::vec::Vec; +use core::num::NonZeroUsize; use orx_concurrent_iter::ConcurrentIter; -/// A parallel runner which is responsible for taking a computation defined as a composition -/// of iterator methods, spawns threads, shares tasks and returns the result of the parallel -/// execution. -pub trait ParallelRunner: Sized + Sync + 'static { - /// Data shared to the thread runners. - type SharedState: Send + Sync; +/// Parallel runner defining how the threads must be spawned and job must be distributed. +pub trait ParallelRunner { + /// Parallel executor responsible for distribution of tasks to the threads. + type Executor: ParallelExecutor; - /// Thread runner that is responsible for executing the tasks allocated to a thread. - type ThreadRunner: ThreadRunner; + /// Thread pool responsible for providing threads to the parallel computation. + type ThreadPool: ParThreadPool; - /// Creates a new parallel runner for the given computation `kind`, parallelization `params` - /// and `initial_input_len`. - fn new(kind: ComputationKind, params: Params, initial_input_len: Option) -> Self; + /// Creates a new parallel executor for a parallel computation. + fn new_executor( + &self, + kind: ComputationKind, + params: Params, + initial_input_len: Option, + ) -> Self::Executor { + let max_num_threads = self.max_num_threads_for_computation(params, initial_input_len); + ::new(kind, params, initial_input_len, max_num_threads) + } - /// Creates an initial shared state. - fn new_shared_state(&self) -> Self::SharedState; + /// Reference to the underlying thread pool. + fn thread_pool(&self) -> &Self::ThreadPool; - /// Returns true if it is beneficial to spawn a new thread provided that: - /// - /// * `num_spawned` threads are already been spawned, and - /// * `shared_state` is the current parallel execution state. - fn do_spawn_new( - &self, - num_spawned: usize, - shared_state: &Self::SharedState, - iter: &I, - ) -> bool + /// Mutable reference to the underlying thread pool. + fn thread_pool_mut(&mut self) -> &mut Self::ThreadPool; + + // derived + + /// Runs `thread_do` using threads provided by the thread pool. + fn run_all( + &mut self, + params: Params, + iter: I, + kind: ComputationKind, + thread_do: F, + ) -> NumSpawned + where + I: ConcurrentIter, + F: Fn(NumSpawned, &I, &SharedStateOf, ThreadRunnerOf) + Sync, + { + let runner = self.new_executor(kind, params, iter.try_get_len()); + let state = runner.new_shared_state(); + let do_spawn = |num_spawned| runner.do_spawn_new(num_spawned, &state, &iter); + let work = |num_spawned| { + thread_do( + num_spawned, + &iter, + &state, + runner.new_thread_executor(&state), + ); + }; + self.thread_pool_mut().run_in_pool(do_spawn, work) + } + + /// Runs `thread_map` using threads provided by the thread pool. + fn map_all( + &mut self, + params: Params, + iter: I, + kind: ComputationKind, + thread_map: M, + ) -> (NumSpawned, Result, F::Error>) + where + F: Fallibility, + I: ConcurrentIter, + M: Fn(NumSpawned, &I, &SharedStateOf, ThreadRunnerOf) -> Result + + Sync, + T: Send, + F::Error: Send, + { + let iter_len = iter.try_get_len(); + let runner = self.new_executor(kind, params, iter_len); + let state = runner.new_shared_state(); + let do_spawn = |num_spawned| runner.do_spawn_new(num_spawned, &state, &iter); + let work = |nt| thread_map(nt, &iter, &state, runner.new_thread_executor(&state)); + let max_num_threads = self.max_num_threads_for_computation(params, iter_len); + self.thread_pool_mut() + .map_in_pool::(do_spawn, work, max_num_threads) + } + + /// Runs infallible `thread_map` using threads provided by the thread pool. + fn map_infallible( + &mut self, + params: Params, + iter: I, + kind: ComputationKind, + thread_map: M, + ) -> (NumSpawned, Result, Never>) where - I: ConcurrentIter; + I: ConcurrentIter, + M: Fn(NumSpawned, &I, &SharedStateOf, ThreadRunnerOf) -> Result + + Sync, + T: Send, + { + self.map_all::(params, iter, kind, thread_map) + } + + /// Returns the maximum number of threads that can be used for the computation defined by + /// the `params` and input `iter_len`. + fn max_num_threads_for_computation( + &self, + params: Params, + iter_len: Option, + ) -> NonZeroUsize { + let pool = self.thread_pool().max_num_threads(); + + let env = crate::env::max_num_threads_by_env_variable().unwrap_or(NonZeroUsize::MAX); + + let req = match (iter_len, params.num_threads) { + (Some(len), NumThreads::Auto) => NonZeroUsize::new(len.max(1)).expect(">0"), + (Some(len), NumThreads::Max(nt)) => NonZeroUsize::new(len.max(1)).expect(">0").min(nt), + (None, NumThreads::Auto) => NonZeroUsize::MAX, + (None, NumThreads::Max(nt)) => nt, + }; + + req.min(pool.min(env)) + } +} + +pub(crate) type SharedStateOf = + <::Executor as ParallelExecutor>::SharedState; +pub(crate) type ThreadRunnerOf = + <::Executor as ParallelExecutor>::ThreadExecutor; + +// auto impl for &mut pool + +impl ParallelRunner for &'_ mut O +where + O: ParallelRunner, +{ + type Executor = O::Executor; + + type ThreadPool = O::ThreadPool; + + fn thread_pool(&self) -> &Self::ThreadPool { + O::thread_pool(self) + } - /// Creates a new thread runner provided that the current parallel execution state is - /// `shared_state`. - fn new_thread_runner(&self, shared_state: &Self::SharedState) -> Self::ThreadRunner; + fn thread_pool_mut(&mut self) -> &mut Self::ThreadPool { + O::thread_pool_mut(self) + } } diff --git a/src/runner/parallel_runner_compute/collect_arbitrary.rs b/src/runner/parallel_runner_compute/collect_arbitrary.rs deleted file mode 100644 index 7f7595c4..00000000 --- a/src/runner/parallel_runner_compute/collect_arbitrary.rs +++ /dev/null @@ -1,148 +0,0 @@ -#[cfg(test)] -use crate::computations::M; -use crate::generic_values::Values; -use crate::generic_values::runner_results::{ParallelCollectArbitrary, ThreadCollectArbitrary}; -use crate::runner::thread_runner_compute as thread; -use crate::{computations::X, runner::ParallelRunnerCompute}; -use orx_concurrent_bag::ConcurrentBag; -use orx_concurrent_iter::ConcurrentIter; -use orx_fixed_vec::IntoConcurrentPinnedVec; - -// m - -#[cfg(test)] -pub fn m(runner: C, m: M, pinned_vec: P) -> (usize, P) -where - C: ParallelRunnerCompute, - I: ConcurrentIter, - O: Send, - M1: Fn(I::Item) -> O + Sync, - P: IntoConcurrentPinnedVec, -{ - let capacity_bound = pinned_vec.capacity_bound(); - let offset = pinned_vec.len(); - let (_, iter, map1) = m.destruct(); - - let mut bag: ConcurrentBag = pinned_vec.into(); - match iter.try_get_len() { - Some(iter_len) => bag.reserve_maximum_capacity(offset + iter_len), - None => bag.reserve_maximum_capacity(capacity_bound), - }; - - // compute - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - std::thread::scope(|s| { - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - num_spawned += 1; - s.spawn(|| { - thread::collect_arbitrary::m( - runner.new_thread_runner(shared_state), - &iter, - shared_state, - &map1, - &bag, - ); - }); - } - }); - let values = bag.into_inner(); - (num_spawned, values) -} - -// x - -pub fn x( - runner: C, - x: X, - pinned_vec: P, -) -> (usize, ParallelCollectArbitrary) -where - C: ParallelRunnerCompute, - I: ConcurrentIter, - Vo: Values, - Vo::Item: Send, - M1: Fn(I::Item) -> Vo + Sync, - P: IntoConcurrentPinnedVec, -{ - let capacity_bound = pinned_vec.capacity_bound(); - let offset = pinned_vec.len(); - let (_, iter, xap1) = x.destruct(); - - let mut bag: ConcurrentBag = pinned_vec.into(); - match iter.try_get_len() { - Some(iter_len) => bag.reserve_maximum_capacity(offset + iter_len), - None => bag.reserve_maximum_capacity(capacity_bound), - }; - - // compute - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let result: ThreadCollectArbitrary = std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - num_spawned += 1; - handles.push(s.spawn(|| { - thread::collect_arbitrary::x( - runner.new_thread_runner(shared_state), - &iter, - shared_state, - &xap1, - &bag, - ) - })); - } - - let mut early_exit_result = None; - while !handles.is_empty() { - let mut finished_idx = None; - for (h, handle) in handles.iter().enumerate() { - if handle.is_finished() { - finished_idx = Some(h); - break; - } - } - - if let Some(h) = finished_idx { - let handle = handles.remove(h); - let result = handle.join().expect("failed to join the thread"); - match &result { - ThreadCollectArbitrary::AllCollected => {} - ThreadCollectArbitrary::StoppedByError { error: _ } => { - early_exit_result = Some(result); - break; - } - ThreadCollectArbitrary::StoppedByWhileCondition => { - early_exit_result = Some(result); - } - } - } - } - - early_exit_result.unwrap_or(ThreadCollectArbitrary::AllCollected) - }); - - ( - num_spawned, - match result { - ThreadCollectArbitrary::AllCollected => ParallelCollectArbitrary::AllCollected { - pinned_vec: bag.into_inner(), - }, - ThreadCollectArbitrary::StoppedByWhileCondition => { - ParallelCollectArbitrary::StoppedByWhileCondition { - pinned_vec: bag.into_inner(), - } - } - ThreadCollectArbitrary::StoppedByError { error } => { - ParallelCollectArbitrary::StoppedByError { error } - } - }, - ) -} diff --git a/src/runner/parallel_runner_compute/collect_ordered.rs b/src/runner/parallel_runner_compute/collect_ordered.rs deleted file mode 100644 index f33d00f4..00000000 --- a/src/runner/parallel_runner_compute/collect_ordered.rs +++ /dev/null @@ -1,128 +0,0 @@ -use crate::generic_values::Values; -use crate::generic_values::runner_results::{Fallibility, ParallelCollect, ThreadCollect}; -use crate::runner::thread_runner_compute as thread; -use crate::{ - computations::{M, X}, - runner::ParallelRunnerCompute, -}; -use orx_concurrent_iter::ConcurrentIter; -use orx_concurrent_ordered_bag::ConcurrentOrderedBag; -use orx_fixed_vec::IntoConcurrentPinnedVec; - -// m - -pub fn m(runner: C, m: M, pinned_vec: P) -> (usize, P) -where - C: ParallelRunnerCompute, - I: ConcurrentIter, - O: Send, - M1: Fn(I::Item) -> O + Sync, - P: IntoConcurrentPinnedVec, -{ - let offset = pinned_vec.len(); - let (_, iter, map1) = m.destruct(); - - let o_bag: ConcurrentOrderedBag = pinned_vec.into(); - - // compute - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - std::thread::scope(|s| { - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - num_spawned += 1; - s.spawn(|| { - thread::collect_ordered::m( - runner.new_thread_runner(shared_state), - &iter, - shared_state, - &map1, - &o_bag, - offset, - ); - }); - } - }); - - let values = unsafe { o_bag.into_inner().unwrap_only_if_counts_match() }; - (num_spawned, values) -} - -// x - -pub fn x( - runner: C, - x: X, - pinned_vec: P, -) -> (usize, ParallelCollect) -where - C: ParallelRunnerCompute, - I: ConcurrentIter, - Vo: Values, - Vo::Item: Send, - ::Error: Send, - M1: Fn(I::Item) -> Vo + Sync, - P: IntoConcurrentPinnedVec, -{ - let (_, iter, xap1) = x.destruct(); - - // compute - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let result: Result>, ::Error> = - std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - num_spawned += 1; - handles.push(s.spawn(|| { - thread::collect_ordered::x( - runner.new_thread_runner(shared_state), - &iter, - shared_state, - &xap1, - ) - })); - } - - let mut results = Vec::with_capacity(handles.len()); - - let mut error = None; - while !handles.is_empty() { - let mut finished_idx = None; - for (h, handle) in handles.iter().enumerate() { - if handle.is_finished() { - finished_idx = Some(h); - break; - } - } - - if let Some(h) = finished_idx { - let handle = handles.remove(h); - let result = handle.join().expect("failed to join the thread"); - match result.into_result() { - Ok(result) => results.push(result), - Err(e) => { - error = Some(e); - break; - } - } - } - } - - match error { - Some(error) => Err(error), - None => Ok(results), - } - }); - - let result = match result { - Err(error) => ParallelCollect::StoppedByError { error }, - Ok(results) => ParallelCollect::reduce(results, pinned_vec), - }; - - (num_spawned, result) -} diff --git a/src/runner/parallel_runner_compute/compute.rs b/src/runner/parallel_runner_compute/compute.rs deleted file mode 100644 index 6591b85a..00000000 --- a/src/runner/parallel_runner_compute/compute.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::{ParallelRunner, Params, runner::ComputationKind}; - -pub trait ParallelRunnerCompute: ParallelRunner { - fn collection(params: Params, len: Option) -> Self { - Self::new(ComputationKind::Collect, params, len) - } - - fn early_return(params: Params, len: Option) -> Self { - Self::new(ComputationKind::EarlyReturn, params, len) - } - - fn reduce(params: Params, len: Option) -> Self { - Self::new(ComputationKind::Reduce, params, len) - } -} - -impl ParallelRunnerCompute for X {} diff --git a/src/runner/parallel_runner_compute/next.rs b/src/runner/parallel_runner_compute/next.rs deleted file mode 100644 index b75d9d2b..00000000 --- a/src/runner/parallel_runner_compute/next.rs +++ /dev/null @@ -1,123 +0,0 @@ -use crate::computations::{M, X}; -use crate::generic_values::runner_results::{Fallibility, NextSuccess, NextWithIdx}; -use crate::runner::thread_runner_compute as thread; -use crate::{generic_values::Values, runner::ParallelRunnerCompute}; -use orx_concurrent_iter::ConcurrentIter; - -pub fn m(runner: C, m: M) -> (usize, Option) -where - C: ParallelRunnerCompute, - I: ConcurrentIter, - O: Send, - M1: Fn(I::Item) -> O + Sync, -{ - let (_, iter, xap1) = m.destruct(); - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let results = std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - num_spawned += 1; - handles.push(s.spawn(|| { - thread::next::m( - runner.new_thread_runner(shared_state), - &iter, - shared_state, - &xap1, - ) - })) - } - - let mut results: Vec<(usize, O)> = Vec::with_capacity(handles.len()); - for x in handles { - if let Some(x) = x.join().expect("failed to join the thread") { - results.push(x); - } - } - results - }); - - let acc = results.into_iter().min_by_key(|x| x.0).map(|x| x.1); - - (num_spawned, acc) -} - -type ResultNext = Result< - Option<(usize, ::Item)>, - <::Fallibility as Fallibility>::Error, ->; - -pub fn x(runner: C, x: X) -> (usize, ResultNext) -where - C: ParallelRunnerCompute, - I: ConcurrentIter, - Vo: Values, - Vo::Item: Send, - X1: Fn(I::Item) -> Vo + Sync, -{ - let (_, iter, xap1) = x.destruct(); - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let result: Result>, _> = std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - num_spawned += 1; - handles.push(s.spawn(|| { - thread::next::x( - runner.new_thread_runner(shared_state), - &iter, - shared_state, - &xap1, - ) - })) - } - - let mut results = Vec::with_capacity(handles.len()); - - let mut error = None; - while !handles.is_empty() { - let mut finished_idx = None; - for (h, handle) in handles.iter().enumerate() { - if handle.is_finished() { - finished_idx = Some(h); - break; - } - } - - if let Some(h) = finished_idx { - let handle = handles.remove(h); - let result = handle.join().expect("failed to join the thread"); - match result { - NextWithIdx::Found { idx, value } => { - results.push(NextSuccess::Found { idx, value }) - } - NextWithIdx::NotFound => {} - NextWithIdx::StoppedByWhileCondition { idx } => { - results.push(NextSuccess::StoppedByWhileCondition { idx }); - } - NextWithIdx::StoppedByError { error: e } => { - error = Some(e); - break; - } - } - } - } - - match error { - Some(error) => Err(error), - None => Ok(results), - } - }); - - let next = result.map(NextSuccess::reduce); - - (num_spawned, next) -} diff --git a/src/runner/parallel_runner_compute/next_any.rs b/src/runner/parallel_runner_compute/next_any.rs deleted file mode 100644 index 181e573c..00000000 --- a/src/runner/parallel_runner_compute/next_any.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::computations::{M, X}; -use crate::generic_values::runner_results::Fallibility; -use crate::runner::thread_runner_compute as thread; -use crate::{generic_values::Values, runner::ParallelRunnerCompute}; -use orx_concurrent_iter::ConcurrentIter; - -pub fn m(runner: C, m: M) -> (usize, Option) -where - C: ParallelRunnerCompute, - I: ConcurrentIter, - O: Send, - M1: Fn(I::Item) -> O + Sync, -{ - let (_, iter, xap1) = m.destruct(); - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let result = std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - num_spawned += 1; - handles.push(s.spawn(|| { - thread::next_any::m( - runner.new_thread_runner(shared_state), - &iter, - shared_state, - &xap1, - ) - })); - } - - // do not wait to join other threads - handles - .into_iter() - .find_map(|x| x.join().expect("failed to join the thread")) - }); - - (num_spawned, result) -} - -type ResultNextAny = - Result::Item>, <::Fallibility as Fallibility>::Error>; - -pub fn x(runner: C, x: X) -> (usize, ResultNextAny) -where - C: ParallelRunnerCompute, - I: ConcurrentIter, - Vo: Values, - Vo::Item: Send, - X1: Fn(I::Item) -> Vo + Sync, -{ - let (_, iter, xap1) = x.destruct(); - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let result = std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - num_spawned += 1; - handles.push(s.spawn(|| { - thread::next_any::x( - runner.new_thread_runner(shared_state), - &iter, - shared_state, - &xap1, - ) - })); - } - - let mut result = Ok(None); - while !handles.is_empty() { - let mut finished_idx = None; - for (h, handle) in handles.iter().enumerate() { - if handle.is_finished() { - finished_idx = Some(h); - break; - } - } - - if let Some(h) = finished_idx { - let handle = handles.remove(h); - match handle.join().expect("failed to join the thread") { - Ok(Some(x)) => { - result = Ok(Some(x)); - break; - } - Err(error) => { - result = Err(error); - break; - } - Ok(None) => {} - } - } - } - - result - }); - - (num_spawned, result) -} diff --git a/src/runner/parallel_runner_compute/reduce.rs b/src/runner/parallel_runner_compute/reduce.rs deleted file mode 100644 index 80f685e1..00000000 --- a/src/runner/parallel_runner_compute/reduce.rs +++ /dev/null @@ -1,125 +0,0 @@ -use crate::computations::{M, X}; -use crate::generic_values::runner_results::{Fallibility, Reduce}; -use crate::runner::thread_runner_compute as thread; -use crate::{generic_values::Values, runner::ParallelRunnerCompute}; -use orx_concurrent_iter::ConcurrentIter; - -// m - -pub fn m(runner: C, m: M, reduce: Red) -> (usize, Option) -where - C: ParallelRunnerCompute, - I: ConcurrentIter, - M1: Fn(I::Item) -> O + Sync, - Red: Fn(O, O) -> O + Sync, - O: Send, -{ - let (_, iter, map1) = m.destruct(); - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let results = std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - num_spawned += 1; - handles.push(s.spawn(|| { - thread::reduce::m( - runner.new_thread_runner(shared_state), - &iter, - shared_state, - &map1, - &reduce, - ) - })); - } - - let mut results = Vec::with_capacity(handles.len()); - for x in handles { - if let Some(x) = x.join().expect("failed to join the thread") { - results.push(x); - } - } - results - }); - - let acc = results.into_iter().reduce(reduce); - - (num_spawned, acc) -} - -// x - -type ResultReduce = - Result::Item>, <::Fallibility as Fallibility>::Error>; - -pub fn x(runner: C, x: X, reduce: Red) -> (usize, ResultReduce) -where - C: ParallelRunnerCompute, - I: ConcurrentIter, - Vo: Values, - Vo::Item: Send, - M1: Fn(I::Item) -> Vo + Sync, - Red: Fn(Vo::Item, Vo::Item) -> Vo::Item + Sync, -{ - let (_, iter, xap1) = x.destruct(); - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let result: Result, _> = std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - num_spawned += 1; - handles.push(s.spawn(|| { - thread::reduce::x( - runner.new_thread_runner(shared_state), - &iter, - shared_state, - &xap1, - &reduce, - ) - })); - } - - let mut results = Vec::with_capacity(handles.len()); - - let mut error = None; - while !handles.is_empty() { - let mut finished_idx = None; - for (h, handle) in handles.iter().enumerate() { - if handle.is_finished() { - finished_idx = Some(h); - break; - } - } - - if let Some(h) = finished_idx { - let handle = handles.remove(h); - let result = handle.join().expect("failed to join the thread"); - match result { - Reduce::Done { acc: Some(acc) } => results.push(acc), - Reduce::StoppedByWhileCondition { acc: Some(acc) } => results.push(acc), - Reduce::StoppedByError { error: e } => { - error = Some(e); - break; - } - _ => {} - } - } - } - - match error { - Some(error) => Err(error), - None => Ok(results), - } - }); - - let acc = result.map(|results| results.into_iter().reduce(reduce)); - - (num_spawned, acc) -} diff --git a/src/special_type_sets/sum.rs b/src/special_type_sets/sum.rs index 73fbd632..6a627529 100644 --- a/src/special_type_sets/sum.rs +++ b/src/special_type_sets/sum.rs @@ -1,4 +1,4 @@ -use std::ops::Add; +use core::ops::Add; /// Number that can be summed over. pub trait Sum { diff --git a/src/using/collect_into/collect.rs b/src/using/collect_into/collect.rs new file mode 100644 index 00000000..c80f0baf --- /dev/null +++ b/src/using/collect_into/collect.rs @@ -0,0 +1,122 @@ +use crate::Params; +use crate::generic_values::runner_results::{ + Infallible, ParallelCollect, ParallelCollectArbitrary, +}; +use crate::runner::{NumSpawned, ParallelRunner}; +use crate::using::executor::parallel_compute as prc; +use crate::using::using_variants::Using; +use crate::{IterationOrder, generic_values::Values}; +use orx_concurrent_iter::ConcurrentIter; +use orx_fixed_vec::IntoConcurrentPinnedVec; + +pub fn map_collect_into( + using: U, + orchestrator: R, + params: Params, + iter: I, + map1: M1, + pinned_vec: P, +) -> (NumSpawned, P) +where + U: Using, + R: ParallelRunner, + I: ConcurrentIter, + M1: Fn(&mut U::Item, I::Item) -> O + Sync, + O: Send, + P: IntoConcurrentPinnedVec, +{ + match (params.is_sequential(), params.iteration_order) { + (true, _) => ( + NumSpawned::zero(), + map_collect_into_seq(using, iter, map1, pinned_vec), + ), + #[cfg(test)] + (false, IterationOrder::Arbitrary) => { + prc::collect_arbitrary::m(using, orchestrator, params, iter, map1, pinned_vec) + } + (false, _) => prc::collect_ordered::m(using, orchestrator, params, iter, map1, pinned_vec), + } +} + +fn map_collect_into_seq(using: U, iter: I, map1: M1, mut pinned_vec: P) -> P +where + U: Using, + I: ConcurrentIter, + M1: Fn(&mut U::Item, I::Item) -> O + Sync, + O: Send, + P: IntoConcurrentPinnedVec, +{ + let mut u = using.into_inner(); + let iter = iter.into_seq_iter(); + for i in iter { + pinned_vec.push(map1(&mut u, i)); + } + pinned_vec +} + +pub fn xap_collect_into( + using: U, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, + pinned_vec: P, +) -> (NumSpawned, P) +where + U: Using, + R: ParallelRunner, + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, + P: IntoConcurrentPinnedVec, +{ + match (params.is_sequential(), params.iteration_order) { + (true, _) => ( + NumSpawned::zero(), + xap_collect_into_seq(using, iter, xap1, pinned_vec), + ), + (false, IterationOrder::Arbitrary) => { + let (num_threads, result) = + prc::collect_arbitrary::x(using, orchestrator, params, iter, xap1, pinned_vec); + let pinned_vec = match result { + ParallelCollectArbitrary::AllOrUntilWhileCollected { pinned_vec } => pinned_vec, + }; + (num_threads, pinned_vec) + } + (false, IterationOrder::Ordered) => { + let (num_threads, result) = + prc::collect_ordered::x(using, orchestrator, params, iter, xap1, pinned_vec); + let pinned_vec = match result { + ParallelCollect::AllCollected { pinned_vec } => pinned_vec, + ParallelCollect::StoppedByWhileCondition { + pinned_vec, + stopped_idx: _, + } => pinned_vec, + }; + (num_threads, pinned_vec) + } + } +} + +fn xap_collect_into_seq(using: U, iter: I, xap1: X1, mut pinned_vec: P) -> P +where + U: Using, + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, + P: IntoConcurrentPinnedVec, +{ + let mut u = using.into_inner(); + let iter = iter.into_seq_iter(); + for i in iter { + let vt = xap1(&mut u, i); + let done = vt.push_to_pinned_vec(&mut pinned_vec); + if Vo::sequential_push_to_stop(done).is_some() { + break; + } + } + + pinned_vec +} diff --git a/src/using/collect_into/fixed_vec.rs b/src/using/collect_into/fixed_vec.rs index 2243727c..f478d20c 100644 --- a/src/using/collect_into/fixed_vec.rs +++ b/src/using/collect_into/fixed_vec.rs @@ -1,9 +1,9 @@ -use crate::generic_values::Values; +use crate::Params; +use crate::generic_values::TransformableValues; use crate::generic_values::runner_results::Infallible; use crate::runner::ParallelRunner; -use crate::using::Using; use crate::using::collect_into::u_par_collect_into::UParCollectIntoCore; -use crate::using::computations::{UM, UX}; +use alloc::vec::Vec; use orx_concurrent_iter::ConcurrentIter; use orx_fixed_vec::FixedVec; @@ -11,27 +11,40 @@ impl UParCollectIntoCore for FixedVec where O: Send + Sync, { - fn u_m_collect_into(self, m: UM) -> Self + fn u_m_collect_into( + self, + using: U, + orchestrator: R, + params: Params, + iter: I, + map1: M1, + ) -> Self where + U: crate::using::using_variants::Using, R: ParallelRunner, - U: Using, I: ConcurrentIter, M1: Fn(&mut U::Item, I::Item) -> O + Sync, { let vec = Vec::from(self); - FixedVec::from(vec.u_m_collect_into::(m)) + FixedVec::from(vec.u_m_collect_into(using, orchestrator, params, iter, map1)) } - fn u_x_collect_into(self, x: UX) -> Self + fn u_x_collect_into( + self, + using: U, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, + ) -> Self where + U: crate::using::using_variants::Using, R: ParallelRunner, - U: Using, I: ConcurrentIter, - Vo: Values, - Vo::Item: Send + Sync, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync, + Vo: TransformableValues, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, { let vec = Vec::from(self); - FixedVec::from(vec.u_x_collect_into::(x)) + FixedVec::from(vec.u_x_collect_into(using, orchestrator, params, iter, xap1)) } } diff --git a/src/using/collect_into/mod.rs b/src/using/collect_into/mod.rs index e97e9a73..322d927e 100644 --- a/src/using/collect_into/mod.rs +++ b/src/using/collect_into/mod.rs @@ -1,6 +1,7 @@ +mod collect; mod fixed_vec; mod split_vec; mod u_par_collect_into; mod vec; -pub use u_par_collect_into::UParCollectIntoCore; +pub(crate) use u_par_collect_into::UParCollectIntoCore; diff --git a/src/using/collect_into/split_vec.rs b/src/using/collect_into/split_vec.rs index 63bf6198..6b5466e8 100644 --- a/src/using/collect_into/split_vec.rs +++ b/src/using/collect_into/split_vec.rs @@ -1,10 +1,10 @@ +use crate::Params; use crate::collect_into::utils::split_vec_reserve; -use crate::generic_values::Values; +use crate::generic_values::TransformableValues; use crate::generic_values::runner_results::Infallible; use crate::runner::ParallelRunner; -use crate::using::Using; +use crate::using::collect_into::collect::{map_collect_into, xap_collect_into}; use crate::using::collect_into::u_par_collect_into::UParCollectIntoCore; -use crate::using::computations::{UM, UX}; use orx_concurrent_iter::ConcurrentIter; use orx_split_vec::{GrowthWithConstantTimeAccess, PseudoDefault, SplitVec}; @@ -14,28 +14,43 @@ where G: GrowthWithConstantTimeAccess, Self: PseudoDefault, { - fn u_m_collect_into(mut self, m: UM) -> Self + fn u_m_collect_into( + mut self, + using: U, + orchestrator: R, + params: Params, + iter: I, + map1: M1, + ) -> Self where + U: crate::using::using_variants::Using, R: ParallelRunner, - U: Using, I: ConcurrentIter, M1: Fn(&mut U::Item, I::Item) -> O + Sync, { - split_vec_reserve(&mut self, m.par_len()); - let (_num_spawned, pinned_vec) = m.collect_into::(self); + split_vec_reserve(&mut self, params.is_sequential(), iter.try_get_len()); + let (_, pinned_vec) = map_collect_into(using, orchestrator, params, iter, map1, self); pinned_vec } - fn u_x_collect_into(mut self, x: UX) -> Self + fn u_x_collect_into( + mut self, + using: U, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, + ) -> Self where + U: crate::using::using_variants::Using, R: ParallelRunner, - U: Using, I: ConcurrentIter, - Vo: Values, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync, + Vo: TransformableValues, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, { - split_vec_reserve(&mut self, x.par_len()); - let (_num_spawned, pinned_vec) = x.collect_into::(self); + split_vec_reserve(&mut self, params.is_sequential(), iter.try_get_len()); + let (_num_spawned, pinned_vec) = + xap_collect_into(using, orchestrator, params, iter, xap1, self); pinned_vec } } diff --git a/src/using/collect_into/u_par_collect_into.rs b/src/using/collect_into/u_par_collect_into.rs index 5dfe73d4..9932dbc8 100644 --- a/src/using/collect_into/u_par_collect_into.rs +++ b/src/using/collect_into/u_par_collect_into.rs @@ -1,24 +1,38 @@ +use crate::Params; use crate::collect_into::ParCollectIntoCore; -use crate::generic_values::Values; +use crate::generic_values::TransformableValues; use crate::generic_values::runner_results::Infallible; use crate::runner::ParallelRunner; -use crate::using::Using; -use crate::using::computations::{UM, UX}; +use crate::using::using_variants::Using; use orx_concurrent_iter::ConcurrentIter; pub trait UParCollectIntoCore: ParCollectIntoCore { - fn u_m_collect_into(self, m: UM) -> Self + fn u_m_collect_into( + self, + using: U, + orchestrator: R, + params: Params, + iter: I, + map1: M1, + ) -> Self where - R: ParallelRunner, U: Using, + R: ParallelRunner, I: ConcurrentIter, M1: Fn(&mut U::Item, I::Item) -> O + Sync; - fn u_x_collect_into(self, x: UX) -> Self + fn u_x_collect_into( + self, + using: U, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, + ) -> Self where - R: ParallelRunner, U: Using, + R: ParallelRunner, I: ConcurrentIter, - Vo: Values, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync; + Vo: TransformableValues, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync; } diff --git a/src/using/collect_into/vec.rs b/src/using/collect_into/vec.rs index 1f8babe6..301efd51 100644 --- a/src/using/collect_into/vec.rs +++ b/src/using/collect_into/vec.rs @@ -1,10 +1,12 @@ +use crate::Params; use crate::collect_into::utils::extend_vec_from_split; -use crate::generic_values::Values; +use crate::generic_values::TransformableValues; use crate::generic_values::runner_results::Infallible; use crate::runner::ParallelRunner; -use crate::using::Using; +use crate::using::collect_into::collect::map_collect_into; use crate::using::collect_into::u_par_collect_into::UParCollectIntoCore; -use crate::using::computations::{UM, UX}; +use crate::using::using_variants::Using; +use alloc::vec::Vec; use orx_concurrent_iter::ConcurrentIter; use orx_fixed_vec::FixedVec; use orx_split_vec::SplitVec; @@ -13,38 +15,53 @@ impl UParCollectIntoCore for Vec where O: Send + Sync, { - fn u_m_collect_into(mut self, m: UM) -> Self + fn u_m_collect_into( + mut self, + using: U, + orchestrator: R, + params: Params, + iter: I, + map1: M1, + ) -> Self where - R: ParallelRunner, U: Using, + R: ParallelRunner, I: ConcurrentIter, M1: Fn(&mut U::Item, I::Item) -> O + Sync, { - match m.par_len() { + match iter.try_get_len() { None => { let split_vec = SplitVec::with_doubling_growth_and_max_concurrent_capacity(); - let split_vec = split_vec.u_m_collect_into::(m); + let split_vec = split_vec.u_m_collect_into(using, orchestrator, params, iter, map1); extend_vec_from_split(self, split_vec) } Some(len) => { self.reserve(len); let fixed_vec = FixedVec::from(self); - let (_num_spawned, fixed_vec) = m.collect_into::(fixed_vec); + let (_, fixed_vec) = + map_collect_into(using, orchestrator, params, iter, map1, fixed_vec); Vec::from(fixed_vec) } } } - fn u_x_collect_into(self, x: UX) -> Self + fn u_x_collect_into( + self, + using: U, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, + ) -> Self where - R: ParallelRunner, U: Using, + R: ParallelRunner, I: ConcurrentIter, - Vo: Values, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync, + Vo: TransformableValues, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, { let split_vec = SplitVec::with_doubling_growth_and_max_concurrent_capacity(); - let split_vec = split_vec.u_x_collect_into::(x); + let split_vec = split_vec.u_x_collect_into(using, orchestrator, params, iter, xap1); extend_vec_from_split(self, split_vec) } } diff --git a/src/using/computational_variants/u_map.rs b/src/using/computational_variants/u_map.rs index ae099e49..5eacb806 100644 --- a/src/using/computational_variants/u_map.rs +++ b/src/using/computational_variants/u_map.rs @@ -1,48 +1,59 @@ -use crate::{ - ChunkSize, IterationOrder, NumThreads, ParCollectInto, Params, - generic_values::Vector, - runner::{DefaultRunner, ParallelRunner}, - using::u_par_iter::ParIterUsing, - using::{Using, computational_variants::u_xap::UParXap, computations::UM}, -}; +use crate::ParIterUsing; +use crate::generic_values::Vector; +use crate::runner::{DefaultRunner, ParallelRunner}; +use crate::using::computational_variants::u_xap::UParXap; +use crate::using::executor::parallel_compute as prc; +use crate::using::using_variants::Using; +use crate::{ChunkSize, IterationOrder, NumThreads, ParCollectInto, Params}; use orx_concurrent_iter::ConcurrentIter; -use std::marker::PhantomData; /// A parallel iterator that maps inputs. pub struct UParMap where - R: ParallelRunner, U: Using, + R: ParallelRunner, I: ConcurrentIter, M1: Fn(&mut U::Item, I::Item) -> O + Sync, { - um: UM, - phantom: PhantomData, + using: U, + orchestrator: R, + params: Params, + iter: I, + map1: M1, } impl UParMap where - R: ParallelRunner, U: Using, + R: ParallelRunner, I: ConcurrentIter, M1: Fn(&mut U::Item, I::Item) -> O + Sync, { - pub(crate) fn new(using: U, params: Params, iter: I, m1: M1) -> Self { + pub(crate) fn new(using: U, orchestrator: R, params: Params, iter: I, map1: M1) -> Self { Self { - um: UM::new(using, params, iter, m1), - phantom: PhantomData, + using, + orchestrator, + params, + iter, + map1, } } - fn destruct(self) -> (U, Params, I, M1) { - self.um.destruct() + pub(crate) fn destruct(self) -> (U, R, Params, I, M1) { + ( + self.using, + self.orchestrator, + self.params, + self.iter, + self.map1, + ) } } unsafe impl Send for UParMap where - R: ParallelRunner, U: Using, + R: ParallelRunner, I: ConcurrentIter, M1: Fn(&mut U::Item, I::Item) -> O + Sync, { @@ -50,8 +61,8 @@ where unsafe impl Sync for UParMap where - R: ParallelRunner, U: Using, + R: ParallelRunner, I: ConcurrentIter, M1: Fn(&mut U::Item, I::Item) -> O + Sync, { @@ -59,69 +70,67 @@ where impl ParIterUsing for UParMap where - R: ParallelRunner, U: Using, + R: ParallelRunner, I: ConcurrentIter, M1: Fn(&mut U::Item, I::Item) -> O + Sync, { type Item = O; fn con_iter(&self) -> &impl ConcurrentIter { - self.um.iter() + &self.iter } fn params(&self) -> Params { - self.um.params() + self.params } - // parameter transformations - fn num_threads(mut self, num_threads: impl Into) -> Self { - self.um.num_threads(num_threads); + self.params = self.params.with_num_threads(num_threads); self } fn chunk_size(mut self, chunk_size: impl Into) -> Self { - self.um.chunk_size(chunk_size); + self.params = self.params.with_chunk_size(chunk_size); self } fn iteration_order(mut self, collect: IterationOrder) -> Self { - self.um.iteration_order(collect); + self.params = self.params.with_collect_ordering(collect); self } - fn with_runner(self) -> impl ParIterUsing { - let (using, params, iter, map) = self.destruct(); - UParMap::new(using, params, iter, map) + fn with_runner( + self, + orchestrator: Q, + ) -> impl ParIterUsing { + let (using, _, params, iter, x1) = self.destruct(); + UParMap::new(using, orchestrator, params, iter, x1) } - // computation transformations - fn map(self, map: Map) -> impl ParIterUsing where Map: Fn(&mut U::Item, Self::Item) -> Out + Sync + Clone, { - let (using, params, iter, m1) = self.destruct(); + let (using, orchestrator, params, iter, m1) = self.destruct(); let m1 = move |u: &mut U::Item, x: I::Item| { let v1 = m1(u, x); map(u, v1) }; - UParMap::new(using, params, iter, m1) + UParMap::new(using, orchestrator, params, iter, m1) } fn filter(self, filter: Filter) -> impl ParIterUsing where Filter: Fn(&mut U::Item, &Self::Item) -> bool + Sync + Clone, { - let (using, params, iter, m1) = self.destruct(); + let (using, orchestrator, params, iter, m1) = self.destruct(); let x1 = move |u: &mut U::Item, i: I::Item| { let value = m1(u, i); filter(u, &value).then_some(value) }; - - UParXap::new(using, params, iter, x1) + UParXap::new(using, orchestrator, params, iter, x1) } fn flat_map( @@ -132,12 +141,12 @@ where IOut: IntoIterator, FlatMap: Fn(&mut U::Item, Self::Item) -> IOut + Sync + Clone, { - let (using, params, iter, m1) = self.destruct(); + let (using, orchestrator, params, iter, m1) = self.destruct(); let x1 = move |u: &mut U::Item, i: I::Item| { let a = m1(u, i); Vector(flat_map(u, a)) }; - UParXap::new(using, params, iter, x1) + UParXap::new(using, orchestrator, params, iter, x1) } fn filter_map( @@ -147,39 +156,39 @@ where where FilterMap: Fn(&mut U::Item, Self::Item) -> Option + Sync + Clone, { - let (using, params, iter, m1) = self.destruct(); + let (using, orchestrator, params, iter, m1) = self.destruct(); let x1 = move |u: &mut U::Item, i: I::Item| { let a = m1(u, i); filter_map(u, a) }; - UParXap::new(using, params, iter, x1) + UParXap::new(using, orchestrator, params, iter, x1) } - // collect - fn collect_into(self, output: C) -> C where C: ParCollectInto, { - output.u_m_collect_into::(self.um) + let (using, orchestrator, params, iter, m1) = self.destruct(); + output.u_m_collect_into(using, orchestrator, params, iter, m1) } - // reduce - fn reduce(self, reduce: Reduce) -> Option where Self::Item: Send, Reduce: Fn(&mut U::Item, Self::Item, Self::Item) -> Self::Item + Sync, { - self.um.reduce::(reduce).1 + let (using, orchestrator, params, iter, m1) = self.destruct(); + prc::reduce::m(using, orchestrator, params, iter, m1, reduce).1 } - // early exit - fn first(self) -> Option where Self::Item: Send, { - self.um.next::().1 + let (using, orchestrator, params, iter, m1) = self.destruct(); + match params.iteration_order { + IterationOrder::Ordered => prc::next::m(using, orchestrator, params, iter, m1).1, + IterationOrder::Arbitrary => prc::next_any::m(using, orchestrator, params, iter, m1).1, + } } } diff --git a/src/using/computational_variants/u_par.rs b/src/using/computational_variants/u_par.rs index bdb0dfcc..d9d12327 100644 --- a/src/using/computational_variants/u_par.rs +++ b/src/using/computational_variants/u_par.rs @@ -1,16 +1,13 @@ -use crate::{ - ChunkSize, IterationOrder, NumThreads, ParCollectInto, Params, - generic_values::Vector, - runner::{DefaultRunner, ParallelRunner}, - using::u_par_iter::ParIterUsing, - using::{ - Using, - computational_variants::{u_map::UParMap, u_xap::UParXap}, - computations::{UM, u_map_self}, - }, -}; +use crate::ParIterUsing; +use crate::default_fns::u_map_self; +use crate::generic_values::Vector; +use crate::runner::{DefaultRunner, ParallelRunner}; +use crate::using::computational_variants::u_map::UParMap; +use crate::using::computational_variants::u_xap::UParXap; +use crate::using::executor::parallel_compute as prc; +use crate::using::using_variants::Using; +use crate::{ChunkSize, IterationOrder, NumThreads, ParCollectInto, Params}; use orx_concurrent_iter::ConcurrentIter; -use std::marker::PhantomData; /// A parallel iterator. pub struct UPar @@ -20,9 +17,9 @@ where I: ConcurrentIter, { using: U, - iter: I, + orchestrator: R, params: Params, - phantom: PhantomData, + iter: I, } impl UPar @@ -31,23 +28,17 @@ where R: ParallelRunner, I: ConcurrentIter, { - pub(crate) fn new(using: U, params: Params, iter: I) -> Self { + pub(crate) fn new(using: U, orchestrator: R, params: Params, iter: I) -> Self { Self { using, - iter, + orchestrator, params, - phantom: PhantomData, + iter, } } - fn destruct(self) -> (U, Params, I) { - (self.using, self.params, self.iter) - } - - #[allow(clippy::type_complexity)] - fn u_m(self) -> UM I::Item> { - let (using, params, iter) = self.destruct(); - UM::new(using, params, iter, u_map_self) + pub(crate) fn destruct(self) -> (U, R, Params, I) { + (self.using, self.orchestrator, self.params, self.iter) } } @@ -83,8 +74,6 @@ where self.params } - // params transformations - fn num_threads(mut self, num_threads: impl Into) -> Self { self.params = self.params.with_num_threads(num_threads); self @@ -100,28 +89,29 @@ where self } - fn with_runner(self) -> impl ParIterUsing { - UPar::new(self.using, self.params, self.iter) + fn with_runner( + self, + orchestrator: Q, + ) -> impl ParIterUsing { + let (using, _, params, iter) = self.destruct(); + UPar::new(using, orchestrator, params, iter) } - // computational transformations - fn map(self, map: Map) -> impl ParIterUsing where Map: Fn(&mut ::Item, Self::Item) -> Out + Sync + Clone, { - let (using, params, iter) = self.destruct(); - let map = move |u: &mut U::Item, x: Self::Item| map(u, x); - UParMap::new(using, params, iter, map) + let (using, orchestrator, params, iter) = self.destruct(); + UParMap::new(using, orchestrator, params, iter, map) } fn filter(self, filter: Filter) -> impl ParIterUsing where - Filter: Fn(&mut U::Item, &Self::Item) -> bool + Sync + Clone, + Filter: Fn(&mut ::Item, &Self::Item) -> bool + Sync + Clone, { - let (using, params, iter) = self.destruct(); + let (using, orchestrator, params, iter) = self.destruct(); let x1 = move |u: &mut U::Item, i: Self::Item| filter(u, &i).then_some(i); - UParXap::new(using, params, iter, x1) + UParXap::new(using, orchestrator, params, iter, x1) } fn flat_map( @@ -130,11 +120,11 @@ where ) -> impl ParIterUsing where IOut: IntoIterator, - FlatMap: Fn(&mut U::Item, Self::Item) -> IOut + Sync + Clone, + FlatMap: Fn(&mut ::Item, Self::Item) -> IOut + Sync + Clone, { - let (using, params, iter) = self.destruct(); + let (using, orchestrator, params, iter) = self.destruct(); let x1 = move |u: &mut U::Item, i: Self::Item| Vector(flat_map(u, i)); - UParXap::new(using, params, iter, x1) + UParXap::new(using, orchestrator, params, iter, x1) } fn filter_map( @@ -144,36 +134,39 @@ where where FilterMap: Fn(&mut ::Item, Self::Item) -> Option + Sync + Clone, { - let (using, params, iter) = self.destruct(); - let x1 = move |u: &mut U::Item, x: Self::Item| filter_map(u, x); - UParXap::new(using, params, iter, x1) + let (using, orchestrator, params, iter) = self.destruct(); + UParXap::new(using, orchestrator, params, iter, filter_map) } - // collect - fn collect_into(self, output: C) -> C where C: ParCollectInto, { - output.u_m_collect_into::(self.u_m()) + let (using, orchestrator, params, iter) = self.destruct(); + output.u_m_collect_into(using, orchestrator, params, iter, u_map_self) } - // reduce - fn reduce(self, reduce: Reduce) -> Option where Self::Item: Send, - Reduce: Fn(&mut U::Item, Self::Item, Self::Item) -> Self::Item + Sync, + Reduce: Fn(&mut ::Item, Self::Item, Self::Item) -> Self::Item + Sync, { - self.u_m().reduce::(reduce).1 + let (using, orchestrator, params, iter) = self.destruct(); + prc::reduce::m(using, orchestrator, params, iter, u_map_self, reduce).1 } - // early exit - fn first(self) -> Option where Self::Item: Send, { - self.u_m().next::().1 + let (using, orchestrator, params, iter) = self.destruct(); + match params.iteration_order { + IterationOrder::Ordered => { + prc::next::m(using, orchestrator, params, iter, u_map_self).1 + } + IterationOrder::Arbitrary => { + prc::next_any::m(using, orchestrator, params, iter, u_map_self).1 + } + } } } diff --git a/src/using/computational_variants/u_xap.rs b/src/using/computational_variants/u_xap.rs index cc10d4cc..c3906831 100644 --- a/src/using/computational_variants/u_xap.rs +++ b/src/using/computational_variants/u_xap.rs @@ -1,114 +1,123 @@ +use crate::using::executor::parallel_compute as prc; use crate::{ - ChunkSize, IterationOrder, NumThreads, ParCollectInto, Params, + ChunkSize, IterationOrder, NumThreads, ParCollectInto, ParIterUsing, Params, generic_values::{TransformableValues, runner_results::Infallible}, runner::{DefaultRunner, ParallelRunner}, - using::{Using, computations::UX, u_par_iter::ParIterUsing}, + using::using_variants::Using, }; use orx_concurrent_iter::ConcurrentIter; -use std::marker::PhantomData; +// use crate::runner::parallel_runner_compute as prc; -/// A parallel iterator that xaps inputs. -/// -/// *xap* is a generalization of one-to-one map, filter-map and flat-map operations. -pub struct UParXap +pub struct UParXap where - R: ParallelRunner, U: Using, + R: ParallelRunner, I: ConcurrentIter, Vo: TransformableValues, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, { - ux: UX, - phantom: PhantomData, + using: U, + orchestrator: R, + params: Params, + iter: I, + xap1: X1, } -impl UParXap +impl UParXap where - R: ParallelRunner, U: Using, + R: ParallelRunner, I: ConcurrentIter, Vo: TransformableValues, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, { - pub(crate) fn new(using: U, params: Params, iter: I, x1: M1) -> Self { + pub(crate) fn new(using: U, orchestrator: R, params: Params, iter: I, xap1: X1) -> Self { Self { - ux: UX::new(using, params, iter, x1), - phantom: PhantomData, + using, + orchestrator, + params, + iter, + xap1, } } - fn destruct(self) -> (U, Params, I, M1) { - self.ux.destruct() + pub(crate) fn destruct(self) -> (U, R, Params, I, X1) { + ( + self.using, + self.orchestrator, + self.params, + self.iter, + self.xap1, + ) } } -unsafe impl Send for UParXap +unsafe impl Send for UParXap where - R: ParallelRunner, U: Using, + R: ParallelRunner, I: ConcurrentIter, Vo: TransformableValues, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, { } -unsafe impl Sync for UParXap +unsafe impl Sync for UParXap where - R: ParallelRunner, U: Using, + R: ParallelRunner, I: ConcurrentIter, Vo: TransformableValues, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, { } -impl ParIterUsing for UParXap +impl ParIterUsing for UParXap where - R: ParallelRunner, U: Using, + R: ParallelRunner, I: ConcurrentIter, Vo: TransformableValues, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, { type Item = Vo::Item; fn con_iter(&self) -> &impl ConcurrentIter { - self.ux.iter() + &self.iter } fn params(&self) -> Params { - self.ux.params() + self.params } - // params transformations - fn num_threads(mut self, num_threads: impl Into) -> Self { - self.ux.num_threads(num_threads); + self.params = self.params.with_num_threads(num_threads); self } fn chunk_size(mut self, chunk_size: impl Into) -> Self { - self.ux.chunk_size(chunk_size); + self.params = self.params.with_chunk_size(chunk_size); self } fn iteration_order(mut self, collect: IterationOrder) -> Self { - self.ux.iteration_order(collect); + self.params = self.params.with_collect_ordering(collect); self } - fn with_runner(self) -> impl ParIterUsing { - let (using, params, iter, map1) = self.destruct(); - UParXap::new(using, params, iter, map1) + fn with_runner( + self, + orchestrator: Q, + ) -> impl ParIterUsing { + let (using, _, params, iter, x1) = self.destruct(); + UParXap::new(using, orchestrator, params, iter, x1) } - // computation transformations - fn map(self, map: Map) -> impl ParIterUsing where - Map: Fn(&mut U::Item, Self::Item) -> Out + Sync + Clone, + Map: Fn(&mut ::Item, Self::Item) -> Out + Sync + Clone, { - let (using, params, iter, x1) = self.destruct(); + let (using, orchestrator, params, iter, x1) = self.destruct(); let x1 = move |u: &mut U::Item, i: I::Item| { let vo = x1(u, i); @@ -124,14 +133,14 @@ where vo.u_map(u, map.clone()) }; - UParXap::new(using, params, iter, x1) + UParXap::new(using, orchestrator, params, iter, x1) } fn filter(self, filter: Filter) -> impl ParIterUsing where - Filter: Fn(&mut U::Item, &Self::Item) -> bool + Sync + Clone, + Filter: Fn(&mut ::Item, &Self::Item) -> bool + Sync + Clone, { - let (using, params, iter, x1) = self.destruct(); + let (using, orchestrator, params, iter, x1) = self.destruct(); let x1 = move |u: &mut U::Item, i: I::Item| { let vo = x1(u, i); // SAFETY: all threads are guaranteed to have its own Using::Item value that is not shared with other threads. @@ -145,7 +154,7 @@ where }; vo.u_filter(u, filter.clone()) }; - UParXap::new(using, params, iter, x1) + UParXap::new(using, orchestrator, params, iter, x1) } fn flat_map( @@ -154,9 +163,9 @@ where ) -> impl ParIterUsing where IOut: IntoIterator, - FlatMap: Fn(&mut U::Item, Self::Item) -> IOut + Sync + Clone, + FlatMap: Fn(&mut ::Item, Self::Item) -> IOut + Sync + Clone, { - let (using, params, iter, x1) = self.destruct(); + let (using, orchestrator, params, iter, x1) = self.destruct(); let x1 = move |u: &mut U::Item, i: I::Item| { let vo = x1(u, i); // SAFETY: all threads are guaranteed to have its own Using::Item value that is not shared with other threads. @@ -170,7 +179,7 @@ where }; vo.u_flat_map(u, flat_map.clone()) }; - UParXap::new(using, params, iter, x1) + UParXap::new(using, orchestrator, params, iter, x1) } fn filter_map( @@ -178,9 +187,9 @@ where filter_map: FilterMap, ) -> impl ParIterUsing where - FilterMap: Fn(&mut U::Item, Self::Item) -> Option + Sync + Clone, + FilterMap: Fn(&mut ::Item, Self::Item) -> Option + Sync + Clone, { - let (using, params, iter, x1) = self.destruct(); + let (using, orchestrator, params, iter, x1) = self.destruct(); let x1 = move |u: &mut U::Item, i: I::Item| { let vo = x1(u, i); // SAFETY: all threads are guaranteed to have its own Using::Item value that is not shared with other threads. @@ -194,34 +203,43 @@ where }; vo.u_filter_map(u, filter_map.clone()) }; - UParXap::new(using, params, iter, x1) + UParXap::new(using, orchestrator, params, iter, x1) } - // collect - fn collect_into(self, output: C) -> C where C: ParCollectInto, { - output.u_x_collect_into::(self.ux) + let (using, orchestrator, params, iter, x1) = self.destruct(); + output.u_x_collect_into(using, orchestrator, params, iter, x1) } - // reduce - fn reduce(self, reduce: Reduce) -> Option where Self::Item: Send, - Reduce: Fn(&mut U::Item, Self::Item, Self::Item) -> Self::Item + Sync, + Reduce: Fn(&mut ::Item, Self::Item, Self::Item) -> Self::Item + Sync, { - self.ux.reduce::(reduce).1 + let (using, orchestrator, params, iter, x1) = self.destruct(); + let (_, Ok(acc)) = prc::reduce::x(using, orchestrator, params, iter, x1, reduce); + acc } - // early exit - fn first(self) -> Option where Self::Item: Send, { - self.ux.next::().1 + let (using, orchestrator, params, iter, x1) = self.destruct(); + match params.iteration_order { + IterationOrder::Ordered => { + let (_num_threads, Ok(result)) = + prc::next::x(using, orchestrator, params, iter, x1); + result.map(|x| x.1) + } + IterationOrder::Arbitrary => { + let (_num_threads, Ok(result)) = + prc::next_any::x(using, orchestrator, params, iter, x1); + result + } + } } } diff --git a/src/using/computational_variants/u_xap_filter_xap.rs b/src/using/computational_variants/u_xap_filter_xap.rs deleted file mode 100644 index de87f632..00000000 --- a/src/using/computational_variants/u_xap_filter_xap.rs +++ /dev/null @@ -1,223 +0,0 @@ -use crate::{ - ChunkSize, IterationOrder, NumThreads, ParCollectInto, Params, - computations::{Values, Vector}, - runner::{DefaultRunner, ParallelRunner}, - using::Using, - using::computations::UXfx, - using::u_par_iter::ParIterUsing, -}; -use orx_concurrent_iter::ConcurrentIter; -use std::marker::PhantomData; - -/// A parallel iterator that xaps, then filters and finally xaps again. -/// -/// *xap* is a generalization of one-to-one map, filter-map and flat-map operations. -pub struct UParXapFilterXap -where - R: ParallelRunner, - U: Using, - I: ConcurrentIter, - Vt: Values, - Vo: Values, - M1: Fn(&mut U::Item, I::Item) -> Vt + Sync, - F: Fn(&mut U::Item, &Vt::Item) -> bool + Sync, - M2: Fn(&mut U::Item, Vt::Item) -> Vo + Sync, -{ - u_xfx: UXfx, - phantom: PhantomData, -} - -impl UParXapFilterXap -where - R: ParallelRunner, - U: Using, - I: ConcurrentIter, - Vt: Values, - Vo: Values, - M1: Fn(&mut U::Item, I::Item) -> Vt + Sync, - F: Fn(&mut U::Item, &Vt::Item) -> bool + Sync, - M2: Fn(&mut U::Item, Vt::Item) -> Vo + Sync, -{ - pub(crate) fn new(using: U, params: Params, iter: I, x1: M1, f: F, x2: M2) -> Self { - Self { - u_xfx: UXfx::new(using, params, iter, x1, f, x2), - phantom: PhantomData, - } - } - - fn destruct(self) -> (U, Params, I, M1, F, M2) { - self.u_xfx.destruct() - } -} - -unsafe impl Send for UParXapFilterXap -where - R: ParallelRunner, - U: Using, - I: ConcurrentIter, - Vt: Values, - Vo: Values, - M1: Fn(&mut U::Item, I::Item) -> Vt + Sync, - F: Fn(&mut U::Item, &Vt::Item) -> bool + Sync, - M2: Fn(&mut U::Item, Vt::Item) -> Vo + Sync, -{ -} - -unsafe impl Sync for UParXapFilterXap -where - R: ParallelRunner, - U: Using, - I: ConcurrentIter, - Vt: Values, - Vo: Values, - M1: Fn(&mut U::Item, I::Item) -> Vt + Sync, - F: Fn(&mut U::Item, &Vt::Item) -> bool + Sync, - M2: Fn(&mut U::Item, Vt::Item) -> Vo + Sync, -{ -} - -impl ParIterUsing for UParXapFilterXap -where - R: ParallelRunner, - U: Using, - I: ConcurrentIter, - Vt: Values, - Vo: Values, - M1: Fn(&mut U::Item, I::Item) -> Vt + Sync, - F: Fn(&mut U::Item, &Vt::Item) -> bool + Sync, - M2: Fn(&mut U::Item, Vt::Item) -> Vo + Sync, -{ - type Item = Vo::Item; - - fn con_iter(&self) -> &impl ConcurrentIter { - self.u_xfx.iter() - } - - fn params(&self) -> Params { - self.u_xfx.params() - } - - // params transformations - - fn num_threads(mut self, num_threads: impl Into) -> Self { - self.u_xfx.num_threads(num_threads); - self - } - - fn chunk_size(mut self, chunk_size: impl Into) -> Self { - self.u_xfx.chunk_size(chunk_size); - self - } - - fn iteration_order(mut self, collect: IterationOrder) -> Self { - self.u_xfx.iteration_order(collect); - self - } - - fn with_runner(self) -> impl ParIterUsing { - let (using, params, iter, map1, filter, map2) = self.destruct(); - UParXapFilterXap::new(using, params, iter, map1, filter, map2) - } - - // computation transformations - - fn map(self, map: Map) -> impl ParIterUsing - where - Map: Fn(&mut U::Item, Self::Item) -> Out + Sync + Clone, - { - let (using, params, iter, x1, f, x2) = self.destruct(); - let x2 = move |u: &mut U::Item, t: Vt::Item| { - // TODO: avoid allocation - let vo: Vec<_> = x2(u, t).values().into_iter().map(|x| map(u, x)).collect(); - Vector(vo) - }; - - UParXapFilterXap::new(using, params, iter, x1, f, x2) - } - - fn filter(self, filter: Filter) -> impl ParIterUsing - where - Filter: Fn(&mut U::Item, &Self::Item) -> bool + Sync + Clone, - { - let (using, params, iter, x1, f, x2) = self.destruct(); - let x2 = move |u: &mut U::Item, t: Vt::Item| { - // TODO: avoid allocation - let vo: Vec<_> = x2(u, t) - .values() - .into_iter() - .filter(|x| filter(u, x)) - .collect(); - Vector(vo) - }; - - UParXapFilterXap::new(using, params, iter, x1, f, x2) - } - - fn flat_map( - self, - flat_map: FlatMap, - ) -> impl ParIterUsing - where - IOut: IntoIterator, - FlatMap: Fn(&mut U::Item, Self::Item) -> IOut + Sync + Clone, - { - let (using, params, iter, x1, f, x2) = self.destruct(); - let x2 = move |u: &mut U::Item, t: Vt::Item| { - // TODO: avoid allocation - let vo: Vec<_> = x2(u, t).values().into_iter().collect(); - let vo: Vec<_> = vo.into_iter().flat_map(|x| flat_map(u, x)).collect(); - Vector(vo) - }; - - UParXapFilterXap::new(using, params, iter, x1, f, x2) - } - - fn filter_map( - self, - filter_map: FilterMap, - ) -> impl ParIterUsing - where - FilterMap: Fn(&mut U::Item, Self::Item) -> Option + Sync + Clone, - { - let (using, params, iter, x1, f, x2) = self.destruct(); - let x2 = move |u: &mut U::Item, t: Vt::Item| { - // TODO: avoid allocation - let vo: Vec<_> = x2(u, t).values().into_iter().collect(); - let vo: Vec<_> = vo.into_iter().filter_map(|x| filter_map(u, x)).collect(); - Vector(vo) - }; - - UParXapFilterXap::new(using, params, iter, x1, f, x2) - } - - // collect - - fn collect_into(self, output: C) -> C - where - C: ParCollectInto, - { - output.u_xfx_collect_into::(self.u_xfx) - } - - // reduce - - fn reduce(self, reduce: Reduce) -> Option - where - Self::Item: Send, - Reduce: Fn(&mut U::Item, Self::Item, Self::Item) -> Self::Item + Sync, - { - self.u_xfx.reduce::(reduce).1 - } - - // early exit - - fn first(self) -> Option - where - Self::Item: Send, - { - match self.params().iteration_order { - IterationOrder::Ordered => self.u_xfx.next::().1, - IterationOrder::Arbitrary => self.u_xfx.next_any::().1, - } - } -} diff --git a/src/using/computations/mod.rs b/src/using/computations/mod.rs deleted file mode 100644 index 879c0514..00000000 --- a/src/using/computations/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod default_fns; -mod u_map; -mod u_xap; - -pub(super) use default_fns::*; -pub(crate) use u_map::UM; -pub(crate) use u_xap::UX; diff --git a/src/using/computations/u_map/collect.rs b/src/using/computations/u_map/collect.rs deleted file mode 100644 index 83e07018..00000000 --- a/src/using/computations/u_map/collect.rs +++ /dev/null @@ -1,50 +0,0 @@ -use super::m::UM; -#[cfg(test)] -use crate::IterationOrder; -use crate::runner::{ParallelRunner, ParallelRunnerCompute}; -use crate::using::Using; -#[cfg(test)] -use crate::using::runner::parallel_runner_compute::u_collect_arbitrary; -use crate::using::runner::parallel_runner_compute::u_collect_ordered; -use orx_concurrent_iter::ConcurrentIter; -use orx_pinned_vec::IntoConcurrentPinnedVec; - -impl UM -where - U: Using, - I: ConcurrentIter, - O: Send, - M1: Fn(&mut U::Item, I::Item) -> O + Sync, -{ - pub fn collect_into(self, pinned_vec: P) -> (usize, P) - where - R: ParallelRunner, - P: IntoConcurrentPinnedVec, - { - let len = self.iter().try_get_len(); - let p = self.params(); - match (p.is_sequential(), p.iteration_order) { - (true, _) => (0, self.sequential(pinned_vec)), - #[cfg(test)] - (false, IterationOrder::Arbitrary) => { - u_collect_arbitrary::u_m(R::collection(p, len), self, pinned_vec) - } - (false, _) => u_collect_ordered::u_m(R::collection(p, len), self, pinned_vec), - } - } - - fn sequential

(self, mut pinned_vec: P) -> P - where - P: IntoConcurrentPinnedVec, - { - let (using, _, iter, map1) = self.destruct(); - let mut u = using.into_inner(); - - let iter = iter.into_seq_iter(); - for i in iter { - pinned_vec.push(map1(&mut u, i)); - } - - pinned_vec - } -} diff --git a/src/using/computations/u_map/m.rs b/src/using/computations/u_map/m.rs deleted file mode 100644 index dc46da03..00000000 --- a/src/using/computations/u_map/m.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::{ChunkSize, IterationOrder, NumThreads, Params, using::Using}; -use orx_concurrent_iter::ConcurrentIter; - -pub struct UM -where - U: Using, - I: ConcurrentIter, - M1: Fn(&mut U::Item, I::Item) -> O, -{ - using: U, - params: Params, - iter: I, - map1: M1, -} - -impl UM -where - U: Using, - I: ConcurrentIter, - M1: Fn(&mut U::Item, I::Item) -> O, -{ - pub fn new(using: U, params: Params, iter: I, map1: M1) -> Self { - Self { - using, - params, - iter, - map1, - } - } - - pub fn destruct(self) -> (U, Params, I, M1) { - (self.using, self.params, self.iter, self.map1) - } - - pub fn params(&self) -> Params { - self.params - } - - pub fn len_and_params(&self) -> (Option, Params) { - (self.iter.try_get_len(), self.params) - } - - pub fn num_threads(&mut self, num_threads: impl Into) { - self.params = self.params().with_num_threads(num_threads); - } - - pub fn chunk_size(&mut self, chunk_size: impl Into) { - self.params = self.params.with_chunk_size(chunk_size); - } - - pub fn iteration_order(&mut self, collect: IterationOrder) { - self.params = self.params.with_collect_ordering(collect); - } - - pub fn iter(&self) -> &I { - &self.iter - } - - pub fn par_len(&self) -> Option { - match (self.params.is_sequential(), self.iter.try_get_len()) { - (true, _) => None, // not required to concurrent reserve when seq - (false, x) => x, - } - } -} diff --git a/src/using/computations/u_map/mod.rs b/src/using/computations/u_map/mod.rs deleted file mode 100644 index dcd4f851..00000000 --- a/src/using/computations/u_map/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[cfg(test)] -mod tests; - -mod collect; -mod m; -mod next; -mod reduce; -mod transformations; - -pub use m::UM; diff --git a/src/using/computations/u_map/next.rs b/src/using/computations/u_map/next.rs deleted file mode 100644 index 0fdaf08c..00000000 --- a/src/using/computations/u_map/next.rs +++ /dev/null @@ -1,29 +0,0 @@ -use super::m::UM; -use crate::runner::{ParallelRunner, ParallelRunnerCompute}; -use crate::using::Using; -use crate::using::runner::parallel_runner_compute::{u_next, u_next_any}; -use orx_concurrent_iter::ConcurrentIter; - -impl UM -where - U: Using, - I: ConcurrentIter, - M1: Fn(&mut U::Item, I::Item) -> O + Sync, - O: Send, -{ - pub fn next(self) -> (usize, Option) - where - R: ParallelRunner, - { - let (len, p) = self.len_and_params(); - u_next::u_m(R::early_return(p, len), self) - } - - pub fn next_any(self) -> (usize, Option) - where - R: ParallelRunner, - { - let (len, p) = self.len_and_params(); - u_next_any::u_m(R::early_return(p, len), self) - } -} diff --git a/src/using/computations/u_map/reduce.rs b/src/using/computations/u_map/reduce.rs deleted file mode 100644 index 4c7acee2..00000000 --- a/src/using/computations/u_map/reduce.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::runner::{ParallelRunner, ParallelRunnerCompute}; -use crate::using::Using; -use crate::using::computations::UM; -use crate::using::runner::parallel_runner_compute::u_reduce; -use orx_concurrent_iter::ConcurrentIter; - -impl UM -where - U: Using, - I: ConcurrentIter, - O: Send, - M1: Fn(&mut U::Item, I::Item) -> O + Sync, -{ - pub fn reduce(self, reduce: X) -> (usize, Option) - where - R: ParallelRunner, - X: Fn(&mut U::Item, O, O) -> O + Sync, - { - let len = self.iter().try_get_len(); - let p = self.params(); - u_reduce::u_m(R::reduce(p, len), self, reduce) - } -} diff --git a/src/using/computations/u_map/tests/collect.rs b/src/using/computations/u_map/tests/collect.rs deleted file mode 100644 index 0e89c93a..00000000 --- a/src/using/computations/u_map/tests/collect.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::{ - IterationOrder, Params, runner::DefaultRunner, using::UsingClone, using::computations::UM, -}; -use orx_concurrent_iter::IntoConcurrentIter; -use orx_pinned_vec::PinnedVec; -use orx_split_vec::SplitVec; -use test_case::test_matrix; - -#[cfg(miri)] -const N: [usize; 2] = [37, 125]; -#[cfg(not(miri))] -const N: [usize; 2] = [1025, 4735]; - -#[test_matrix( - [0, 1, N[0], N[1]], - [1, 4], - [1, 64], - [IterationOrder::Ordered, IterationOrder::Arbitrary]) -] -fn u_m_map_collect(n: usize, nt: usize, chunk: usize, ordering: IterationOrder) { - let offset = 33; - - let input: Vec<_> = (0..n).map(|x| x.to_string()).collect(); - let map = |u: &mut usize, x: String| { - *u += 1; - format!("{}!", x) - }; - - let mut output = SplitVec::with_doubling_growth_and_max_concurrent_capacity(); - let mut expected = Vec::new(); - - let mut u = 0; - for i in 0..offset { - let value = map(&mut u, i.to_string()); - output.push(value.clone()); - expected.push(value); - } - expected.extend(input.clone().into_iter().map(|x| map(&mut u, x))); - - let params = Params::new(nt, chunk, ordering); - let iter = input.into_con_iter(); - let m = UM::new(UsingClone::new(0), params, iter, map); - - let (_, mut output) = m.collect_into::(output); - - if !params.is_sequential() && matches!(params.iteration_order, IterationOrder::Arbitrary) { - expected.sort(); - output.sort(); - } - - assert_eq!(expected, output.to_vec()); -} diff --git a/src/using/computations/u_map/tests/find.rs b/src/using/computations/u_map/tests/find.rs deleted file mode 100644 index 8227cdb1..00000000 --- a/src/using/computations/u_map/tests/find.rs +++ /dev/null @@ -1,56 +0,0 @@ -use crate::{ - DefaultRunner, Params, - using::{UsingClone, UsingFun, computations::UM}, -}; -use orx_concurrent_iter::IntoConcurrentIter; -use test_case::test_matrix; - -#[cfg(miri)] -const N: [usize; 2] = [37, 125]; -#[cfg(not(miri))] -const N: [usize; 2] = [1025, 4735]; - -#[test_matrix( - [0, 1, N[0], N[1]], - [1, 4], - [1, 64]) -] -fn u_m_find(n: usize, nt: usize, chunk: usize) { - let input: Vec<_> = (0..n).map(|x| x.to_string()).collect(); - - let expected = input.clone().into_iter().next(); - - let params = Params::new(nt, chunk, Default::default()); - let iter = input.into_con_iter(); - let map = |u: &mut usize, x: String| { - *u += 1; - x - }; - let m = UM::new(UsingClone::new(0), params, iter, map); - - let output = m.next::().1; - assert_eq!(expected, output); -} - -#[test_matrix( - [0, 1, N[0], N[1]], - [1, 4], - [1, 64]) -] -fn u_m_map_find(n: usize, nt: usize, chunk: usize) { - let input: Vec<_> = (0..n).map(|x| x.to_string()).collect(); - let map = |u: &mut usize, x: String| { - *u += 1; - format!("{}!", x) - }; - - let mut u = 0; - let expected = input.clone().into_iter().map(|x| map(&mut u, x)).next(); - - let params = Params::new(nt, chunk, Default::default()); - let iter = input.into_con_iter(); - let m = UM::new(UsingFun::new(|idx| idx), params, iter, map); - let output = m.next::().1; - - assert_eq!(expected, output); -} diff --git a/src/using/computations/u_map/tests/mod.rs b/src/using/computations/u_map/tests/mod.rs deleted file mode 100644 index 5493e3c9..00000000 --- a/src/using/computations/u_map/tests/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod collect; -mod find; -mod reduce; diff --git a/src/using/computations/u_map/tests/reduce.rs b/src/using/computations/u_map/tests/reduce.rs deleted file mode 100644 index 222a915f..00000000 --- a/src/using/computations/u_map/tests/reduce.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::{ - Params, - runner::DefaultRunner, - using::computations::UM, - using::{UsingClone, UsingFun}, -}; -use orx_concurrent_iter::IntoConcurrentIter; -use test_case::test_matrix; - -#[cfg(miri)] -const N: [usize; 2] = [37, 125]; -#[cfg(not(miri))] -const N: [usize; 2] = [1025, 4735]; - -#[test_matrix( - [0, 1, N[0], N[1]], - [1, 4], - [1, 64]) -] -fn m_reduce(n: usize, nt: usize, chunk: usize) { - let input: Vec<_> = (0..n).map(|x| x.to_string()).collect(); - let reduce = |u: &mut usize, x: String, y: String| { - *u += 1; - match x < y { - true => y, - false => x, - } - }; - - let mut u = 0; - let expected = input - .clone() - .into_iter() - .reduce(|a, b| reduce(&mut u, a, b)); - - let params = Params::new(nt, chunk, Default::default()); - let iter = input.into_con_iter(); - let map = |u: &mut usize, x: String| { - *u += 1; - x - }; - let m = UM::new(UsingClone::new(0), params, iter, map); - let (_, output) = m.reduce::(reduce); - - assert_eq!(expected, output); -} - -#[test_matrix( - [0, 1, N[0], N[1]], - [1, 4], - [1, 64]) -] -fn m_map_reduce(n: usize, nt: usize, chunk: usize) { - let input: Vec<_> = (0..n).map(|x| x.to_string()).collect(); - let map = |u: &mut usize, x: String| { - *u += 1; - format!("{}!", x) - }; - let reduce = |u: &mut usize, x: String, y: String| { - *u += 1; - match x < y { - true => y, - false => x, - } - }; - - let mut u = 0; - let mut u2 = 0; - let expected = input - .clone() - .into_iter() - .map(|x| map(&mut u, x)) - .reduce(|a, b| reduce(&mut u2, a, b)); - - let params = Params::new(nt, chunk, Default::default()); - let iter = input.into_con_iter(); - let m = UM::new(UsingFun::new(|_| 42), params, iter, map); - let (_, output) = m.reduce::(reduce); - - assert_eq!(expected, output); -} diff --git a/src/using/computations/u_map/transformations.rs b/src/using/computations/u_map/transformations.rs deleted file mode 100644 index ce559de9..00000000 --- a/src/using/computations/u_map/transformations.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::using::Using; -use crate::using::computations::UM; -use orx_concurrent_iter::ConcurrentIter; - -impl UM -where - U: Using, - I: ConcurrentIter, - M1: Fn(&mut U::Item, I::Item) -> O, -{ - #[allow(clippy::type_complexity)] - pub fn map(self, map: M2) -> UM Q> - where - M2: Fn(&mut U::Item, O) -> Q, - { - let (using, params, iter, map1) = self.destruct(); - let map2 = move |u: &mut U::Item, t: ::Item| { - let v1 = map1(u, t); - map(u, v1) - }; - UM::new(using, params, iter, map2) - } -} diff --git a/src/using/computations/u_xap/collect.rs b/src/using/computations/u_xap/collect.rs deleted file mode 100644 index b0399c19..00000000 --- a/src/using/computations/u_xap/collect.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::generic_values::runner_results::{ - Infallible, ParallelCollect, ParallelCollectArbitrary, -}; -use crate::using::Using; -use crate::using::computations::UX; -use crate::using::runner::parallel_runner_compute::{u_collect_arbitrary, u_collect_ordered}; -use crate::{ - IterationOrder, - generic_values::Values, - runner::{ParallelRunner, ParallelRunnerCompute}, -}; -use orx_concurrent_iter::ConcurrentIter; -use orx_fixed_vec::IntoConcurrentPinnedVec; - -impl UX -where - U: Using, - I: ConcurrentIter, - Vo: Values, - Vo::Item: Send, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync, -{ - pub fn collect_into(self, pinned_vec: P) -> (usize, P) - where - R: ParallelRunner, - P: IntoConcurrentPinnedVec, - Vo: Values, - { - let (len, p) = self.len_and_params(); - - match (p.is_sequential(), p.iteration_order) { - (true, _) => (0, self.sequential(pinned_vec)), - (false, IterationOrder::Arbitrary) => { - let (num_threads, result) = - u_collect_arbitrary::u_x(R::collection(p, len), self, pinned_vec); - let pinned_vec = match result { - ParallelCollectArbitrary::AllCollected { pinned_vec } => pinned_vec, - ParallelCollectArbitrary::StoppedByWhileCondition { pinned_vec } => pinned_vec, - }; - (num_threads, pinned_vec) - } - (false, IterationOrder::Ordered) => { - let (num_threads, result) = - u_collect_ordered::u_x(R::collection(p, len), self, pinned_vec); - let pinned_vec = match result { - ParallelCollect::AllCollected { pinned_vec } => pinned_vec, - ParallelCollect::StoppedByWhileCondition { - pinned_vec, - stopped_idx: _, - } => pinned_vec, - }; - (num_threads, pinned_vec) - } - } - } - - fn sequential

(self, mut pinned_vec: P) -> P - where - P: IntoConcurrentPinnedVec, - { - let (using, _, iter, xap1) = self.destruct(); - let mut u = using.into_inner(); - - let iter = iter.into_seq_iter(); - for i in iter { - let vt = xap1(&mut u, i); - let done = vt.push_to_pinned_vec(&mut pinned_vec); - if Vo::sequential_push_to_stop(done).is_some() { - break; - } - } - - pinned_vec - } -} diff --git a/src/using/computations/u_xap/mod.rs b/src/using/computations/u_xap/mod.rs deleted file mode 100644 index 9342c2e1..00000000 --- a/src/using/computations/u_xap/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[cfg(test)] -mod tests; - -mod collect; -mod next; -mod reduce; -mod x; - -pub use x::UX; diff --git a/src/using/computations/u_xap/next.rs b/src/using/computations/u_xap/next.rs deleted file mode 100644 index 87b5479d..00000000 --- a/src/using/computations/u_xap/next.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::generic_values::Values; -use crate::generic_values::runner_results::Infallible; -use crate::runner::{ParallelRunner, ParallelRunnerCompute}; -use crate::using::Using; -use crate::using::computations::UX; -use crate::using::runner::parallel_runner_compute::{u_next, u_next_any}; -use orx_concurrent_iter::ConcurrentIter; - -impl UX -where - U: Using, - I: ConcurrentIter, - Vo: Values, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync, - Vo::Item: Send, -{ - pub fn next(self) -> (usize, Option) - where - R: ParallelRunner, - Vo: Values, - { - let (len, p) = self.len_and_params(); - let (num_threads, Ok(result)) = u_next::u_x(R::early_return(p, len), self); - (num_threads, result.map(|x| x.1)) - } - - pub fn next_any(self) -> (usize, Option) - where - R: ParallelRunner, - Vo: Values, - { - let (len, p) = self.len_and_params(); - let (num_threads, Ok(next)) = u_next_any::u_x(R::early_return(p, len), self); - (num_threads, next) - } -} diff --git a/src/using/computations/u_xap/reduce.rs b/src/using/computations/u_xap/reduce.rs deleted file mode 100644 index 1026014f..00000000 --- a/src/using/computations/u_xap/reduce.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::generic_values::Values; -use crate::generic_values::runner_results::Infallible; -use crate::runner::{ParallelRunner, ParallelRunnerCompute}; -use crate::using::Using; -use crate::using::computations::UX; -use crate::using::runner::parallel_runner_compute::u_reduce; -use orx_concurrent_iter::ConcurrentIter; - -impl UX -where - U: Using, - I: ConcurrentIter, - Vo: Values, - Vo::Item: Send, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync, -{ - pub fn reduce(self, reduce: Red) -> (usize, Option) - where - R: ParallelRunner, - Red: Fn(&mut U::Item, Vo::Item, Vo::Item) -> Vo::Item + Sync, - Vo: Values, - { - let (len, p) = self.len_and_params(); - let (num_threads, Ok(acc)) = u_reduce::u_x(R::reduce(p, len), self, reduce); - (num_threads, acc) - } -} diff --git a/src/using/computations/u_xap/tests/collect.rs b/src/using/computations/u_xap/tests/collect.rs deleted file mode 100644 index 0fbddb0f..00000000 --- a/src/using/computations/u_xap/tests/collect.rs +++ /dev/null @@ -1,99 +0,0 @@ -use crate::{ - IterationOrder, Params, - generic_values::Vector, - runner::DefaultRunner, - using::{UsingClone, computations::UX}, -}; -use orx_concurrent_iter::IntoConcurrentIter; -use orx_pinned_vec::PinnedVec; -use orx_split_vec::SplitVec; -use test_case::test_matrix; - -#[cfg(miri)] -const N: [usize; 2] = [37, 125]; -#[cfg(not(miri))] -const N: [usize; 2] = [1025, 4735]; - -#[test_matrix( - [0, 1, N[0], N[1]], - [1, 4], - [1, 64], - [IterationOrder::Ordered, IterationOrder::Arbitrary]) -] -fn u_x_flat_map_collect(n: usize, nt: usize, chunk: usize, ordering: IterationOrder) { - let offset = 33; - - let input: Vec<_> = (0..n).map(|x| x.to_string()).collect(); - let fmap = |x: String| x.chars().map(|x| x.to_string()).collect::>(); - let xmap = |u: &mut usize, x: String| { - *u += 1; - Vector(fmap(x)) - }; - - let mut output = SplitVec::with_doubling_growth_and_max_concurrent_capacity(); - let mut expected = Vec::new(); - - for i in 0..offset { - let i = i.to_string(); - for x in fmap(i) { - output.push(x.clone()); - expected.push(x); - } - } - expected.extend(input.clone().into_iter().flat_map(&fmap)); - - let params = Params::new(nt, chunk, ordering); - let iter = input.into_con_iter(); - let x = UX::new(UsingClone::new(0), params, iter, xmap); - - let (_, mut output) = x.collect_into::(output); - - if !params.is_sequential() && matches!(params.iteration_order, IterationOrder::Arbitrary) { - expected.sort(); - output.sort(); - } - - assert_eq!(expected, output.to_vec()); -} - -#[test_matrix( - [0, 1, N[0], N[1]], - [1, 4], - [1, 64], - [IterationOrder::Ordered, IterationOrder::Arbitrary]) -] -fn u_x_filter_map_collect(n: usize, nt: usize, chunk: usize, ordering: IterationOrder) { - let offset = 33; - - let input: Vec<_> = (0..n).map(|x| x.to_string()).collect(); - let fmap = |x: String| (!x.starts_with('3')).then_some(format!("{}!", x)); - let xmap = |u: &mut usize, x: String| { - *u += 1; - Vector(fmap(x)) - }; - - let mut output = SplitVec::with_doubling_growth_and_max_concurrent_capacity(); - let mut expected = Vec::new(); - - for i in 0..offset { - let i = i.to_string(); - if let Some(x) = fmap(i) { - output.push(x.clone()); - expected.push(x); - } - } - expected.extend(input.clone().into_iter().flat_map(&fmap)); - - let params = Params::new(nt, chunk, ordering); - let iter = input.into_con_iter(); - let x = UX::new(UsingClone::new(0), params, iter, xmap); - - let (_, mut output) = x.collect_into::(output); - - if !params.is_sequential() && matches!(params.iteration_order, IterationOrder::Arbitrary) { - expected.sort(); - output.sort(); - } - - assert_eq!(expected, output.to_vec()); -} diff --git a/src/using/computations/u_xap/tests/find.rs b/src/using/computations/u_xap/tests/find.rs deleted file mode 100644 index c206f2bb..00000000 --- a/src/using/computations/u_xap/tests/find.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::{ - DefaultRunner, Params, - generic_values::Vector, - using::{UsingClone, computations::UX}, -}; -use orx_concurrent_iter::IntoConcurrentIter; -use test_case::test_matrix; - -#[cfg(miri)] -const N: [usize; 2] = [37, 125]; -#[cfg(not(miri))] -const N: [usize; 2] = [1025, 4735]; - -#[test_matrix( - [0, 1, N[0], N[1]], - [1, 4], - [1, 64]) -] -fn u_x_flat_map_find(n: usize, nt: usize, chunk: usize) { - let input: Vec<_> = (0..n).map(|x| x.to_string()).collect(); - let fmap = |x: String| x.chars().map(|x| x.to_string()).collect::>(); - let xmap = |u: &mut usize, x: String| { - *u += 1; - Vector(fmap(x)) - }; - - let expected = input.clone().into_iter().flat_map(fmap).next(); - - let params = Params::new(nt, chunk, Default::default()); - let iter = input.into_con_iter(); - let x = UX::new(UsingClone::new(0), params, iter, xmap); - - let output = x.next::().1; - assert_eq!(expected, output); -} - -#[test_matrix( - [0, 1, N[0], N[1]], - [1, 4], - [1, 64]) -] -fn u_x_filter_map_find(n: usize, nt: usize, chunk: usize) { - let input: Vec<_> = (0..n).map(|x| x.to_string()).collect(); - let fmap = |x: String| (!x.starts_with('3')).then_some(format!("{}!", x)); - let xmap = |u: &mut usize, x: String| { - *u += 1; - Vector(fmap(x)) - }; - - let expected = input.clone().into_iter().filter_map(fmap).next(); - - let params = Params::new(nt, chunk, Default::default()); - let iter = input.into_con_iter(); - let x = UX::new(UsingClone::new(0), params, iter, xmap); - - let output = x.next::().1; - - assert_eq!(expected, output); -} diff --git a/src/using/computations/u_xap/tests/mod.rs b/src/using/computations/u_xap/tests/mod.rs deleted file mode 100644 index 5493e3c9..00000000 --- a/src/using/computations/u_xap/tests/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod collect; -mod find; -mod reduce; diff --git a/src/using/computations/u_xap/tests/reduce.rs b/src/using/computations/u_xap/tests/reduce.rs deleted file mode 100644 index cd83065f..00000000 --- a/src/using/computations/u_xap/tests/reduce.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::{ - Params, - generic_values::Vector, - runner::DefaultRunner, - using::{UsingClone, computations::UX}, -}; -use orx_concurrent_iter::IntoConcurrentIter; -use test_case::test_matrix; - -#[cfg(miri)] -const N: [usize; 2] = [37, 125]; -#[cfg(not(miri))] -const N: [usize; 2] = [1025, 4735]; - -#[test_matrix( - [0, 1, N[0], N[1]], - [1, 4], - [1, 64]) -] -fn u_x_flat_map_reduce(n: usize, nt: usize, chunk: usize) { - let input: Vec<_> = (0..n).map(|x| x.to_string()).collect(); - let fmap = |x: String| x.chars().map(|x| x.to_string()).collect::>(); - let xmap = |u: &mut usize, x: String| { - *u += 1; - Vector(fmap(x)) - }; - - let reduce = |u: &mut usize, x: String, y: String| { - *u += 1; - match x < y { - true => y, - false => x, - } - }; - - let mut u = 0; - let expected = input - .clone() - .into_iter() - .flat_map(fmap) - .reduce(|a, b| reduce(&mut u, a, b)); - - let params = Params::new(nt, chunk, Default::default()); - let iter = input.into_con_iter(); - let x = UX::new(UsingClone::new(0), params, iter, xmap); - - let (_, output) = x.reduce::(reduce); - - assert_eq!(expected, output); -} - -#[test_matrix( - [0, 1, N[0], N[1]], - [1, 4], - [1, 64]) -] -fn u_x_filter_map_reduce(n: usize, nt: usize, chunk: usize) { - let input: Vec<_> = (0..n).map(|x| x.to_string()).collect(); - let fmap = |x: String| (!x.starts_with('3')).then_some(format!("{}!", x)); - let xmap = |u: &mut usize, x: String| { - *u += 1; - Vector(fmap(x)) - }; - let reduce = |u: &mut usize, x: String, y: String| { - *u += 1; - match x < y { - true => y, - false => x, - } - }; - - let mut u = 0; - let expected = input - .clone() - .into_iter() - .filter_map(fmap) - .reduce(|a, b| reduce(&mut u, a, b)); - - let params = Params::new(nt, chunk, Default::default()); - let iter = input.into_con_iter(); - let x = UX::new(UsingClone::new(0), params, iter, xmap); - - let (_, output) = x.reduce::(reduce); - - assert_eq!(expected, output); -} diff --git a/src/using/computations/u_xap/x.rs b/src/using/computations/u_xap/x.rs deleted file mode 100644 index ea5b9122..00000000 --- a/src/using/computations/u_xap/x.rs +++ /dev/null @@ -1,68 +0,0 @@ -use crate::using::Using; -use crate::{ChunkSize, IterationOrder, NumThreads, Params, generic_values::Values}; -use orx_concurrent_iter::ConcurrentIter; - -pub struct UX -where - U: Using, - I: ConcurrentIter, - Vo: Values, - M1: Fn(&mut U::Item, I::Item) -> Vo, -{ - using: U, - params: Params, - iter: I, - xap1: M1, -} - -impl UX -where - U: Using, - I: ConcurrentIter, - Vo: Values, - M1: Fn(&mut U::Item, I::Item) -> Vo, -{ - pub fn new(using: U, params: Params, iter: I, xap1: M1) -> Self { - Self { - using, - params, - iter, - xap1, - } - } - - pub fn destruct(self) -> (U, Params, I, M1) { - (self.using, self.params, self.iter, self.xap1) - } - - pub fn params(&self) -> Params { - self.params - } - - pub fn len_and_params(&self) -> (Option, Params) { - (self.iter.try_get_len(), self.params) - } - - pub fn num_threads(&mut self, num_threads: impl Into) { - self.params = self.params().with_num_threads(num_threads); - } - - pub fn chunk_size(&mut self, chunk_size: impl Into) { - self.params = self.params.with_chunk_size(chunk_size); - } - - pub fn iteration_order(&mut self, collect: IterationOrder) { - self.params = self.params.with_collect_ordering(collect); - } - - pub fn iter(&self) -> &I { - &self.iter - } - - pub fn par_len(&self) -> Option { - match (self.params.is_sequential(), self.iter.try_get_len()) { - (true, _) => None, // not required to concurrent reserve when seq - (false, x) => x, - } - } -} diff --git a/src/using/executor/mod.rs b/src/using/executor/mod.rs new file mode 100644 index 00000000..9bd9ee3a --- /dev/null +++ b/src/using/executor/mod.rs @@ -0,0 +1,2 @@ +pub(super) mod parallel_compute; +mod thread_compute; diff --git a/src/using/executor/parallel_compute/collect_arbitrary.rs b/src/using/executor/parallel_compute/collect_arbitrary.rs new file mode 100644 index 00000000..74851cd7 --- /dev/null +++ b/src/using/executor/parallel_compute/collect_arbitrary.rs @@ -0,0 +1,92 @@ +use crate::Params; +use crate::generic_values::Values; +use crate::generic_values::runner_results::ParallelCollectArbitrary; +use crate::runner::{ComputationKind, NumSpawned, ParallelRunner, SharedStateOf, ThreadRunnerOf}; +use crate::using::executor::thread_compute as th; +use crate::using::using_variants::Using; +use orx_concurrent_bag::ConcurrentBag; +use orx_concurrent_iter::ConcurrentIter; +use orx_fixed_vec::IntoConcurrentPinnedVec; + +#[cfg(test)] +pub fn m( + using: U, + mut orchestrator: C, + params: Params, + iter: I, + map1: M1, + pinned_vec: P, +) -> (NumSpawned, P) +where + U: Using, + C: ParallelRunner, + I: ConcurrentIter, + O: Send, + M1: Fn(&mut U::Item, I::Item) -> O + Sync, + P: IntoConcurrentPinnedVec, +{ + let capacity_bound = pinned_vec.capacity_bound(); + let offset = pinned_vec.len(); + let mut bag: ConcurrentBag = pinned_vec.into(); + match iter.try_get_len() { + Some(iter_len) => bag.reserve_maximum_capacity(offset + iter_len), + None => bag.reserve_maximum_capacity(capacity_bound), + }; + let thread_work = + |nt: NumSpawned, iter: &I, state: &SharedStateOf, thread_runner: ThreadRunnerOf| { + let u = using.create(nt.into_inner()); + th::collect_arbitrary::m(u, thread_runner, iter, state, &map1, &bag); + }; + let num_spawned = orchestrator.run_all(params, iter, ComputationKind::Collect, thread_work); + + let values = bag.into_inner(); + (num_spawned, values) +} + +pub fn x( + using: U, + mut orchestrator: C, + params: Params, + iter: I, + xap1: X1, + pinned_vec: P, +) -> (NumSpawned, ParallelCollectArbitrary) +where + U: Using, + C: ParallelRunner, + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, + P: IntoConcurrentPinnedVec, +{ + let capacity_bound = pinned_vec.capacity_bound(); + let offset = pinned_vec.len(); + + let mut bag: ConcurrentBag = pinned_vec.into(); + match iter.try_get_len() { + Some(iter_len) => bag.reserve_maximum_capacity(offset + iter_len), + None => bag.reserve_maximum_capacity(capacity_bound), + }; + + let thread_map = + |nt: NumSpawned, iter: &I, state: &SharedStateOf, thread_runner: ThreadRunnerOf| { + let u = using.create(nt.into_inner()); + th::collect_arbitrary::x(u, thread_runner, iter, state, &xap1, &bag).into_result() + }; + let (num_spawned, result) = orchestrator.map_all::( + params, + iter, + ComputationKind::Collect, + thread_map, + ); + + let result = match result { + Err(error) => ParallelCollectArbitrary::StoppedByError { error }, + Ok(_) => ParallelCollectArbitrary::AllOrUntilWhileCollected { + pinned_vec: bag.into_inner(), + }, + }; + + (num_spawned, result) +} diff --git a/src/using/executor/parallel_compute/collect_ordered.rs b/src/using/executor/parallel_compute/collect_ordered.rs new file mode 100644 index 00000000..10ad8187 --- /dev/null +++ b/src/using/executor/parallel_compute/collect_ordered.rs @@ -0,0 +1,76 @@ +use crate::Params; +use crate::generic_values::Values; +use crate::generic_values::runner_results::{Fallibility, ParallelCollect}; +use crate::runner::{ComputationKind, NumSpawned, ParallelRunner, SharedStateOf, ThreadRunnerOf}; +use crate::using::executor::thread_compute as th; +use crate::using::using_variants::Using; +use orx_concurrent_iter::ConcurrentIter; +use orx_concurrent_ordered_bag::ConcurrentOrderedBag; +use orx_fixed_vec::IntoConcurrentPinnedVec; + +pub fn m( + using: U, + mut orchestrator: C, + params: Params, + iter: I, + map1: M1, + pinned_vec: P, +) -> (NumSpawned, P) +where + U: Using, + C: ParallelRunner, + I: ConcurrentIter, + O: Send, + M1: Fn(&mut U::Item, I::Item) -> O + Sync, + P: IntoConcurrentPinnedVec, +{ + let offset = pinned_vec.len(); + let o_bag: ConcurrentOrderedBag = pinned_vec.into(); + + let thread_do = + |nt: NumSpawned, iter: &I, state: &SharedStateOf, thread_runner: ThreadRunnerOf| { + let u = using.create(nt.into_inner()); + th::collect_ordered::m(u, thread_runner, iter, state, &map1, &o_bag, offset); + }; + let num_spawned = orchestrator.run_all(params, iter, ComputationKind::Collect, thread_do); + + let values = unsafe { o_bag.into_inner().unwrap_only_if_counts_match() }; + (num_spawned, values) +} + +pub fn x( + using: U, + mut orchestrator: C, + params: Params, + iter: I, + xap1: X1, + pinned_vec: P, +) -> (NumSpawned, ParallelCollect) +where + U: Using, + C: ParallelRunner, + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + ::Error: Send, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, + P: IntoConcurrentPinnedVec, +{ + let thread_map = + |nt: NumSpawned, iter: &I, state: &SharedStateOf, thread_runner: ThreadRunnerOf| { + let u = using.create(nt.into_inner()); + th::collect_ordered::x(u, thread_runner, iter, state, &xap1).into_result() + }; + let (num_spawned, result) = orchestrator.map_all::( + params, + iter, + ComputationKind::Collect, + thread_map, + ); + + let result = match result { + Err(error) => ParallelCollect::StoppedByError { error }, + Ok(results) => ParallelCollect::reduce(results, pinned_vec), + }; + (num_spawned, result) +} diff --git a/src/runner/parallel_runner_compute/mod.rs b/src/using/executor/parallel_compute/mod.rs similarity index 71% rename from src/runner/parallel_runner_compute/mod.rs rename to src/using/executor/parallel_compute/mod.rs index 7516313a..6ccd3f7b 100644 --- a/src/runner/parallel_runner_compute/mod.rs +++ b/src/using/executor/parallel_compute/mod.rs @@ -3,7 +3,3 @@ pub(crate) mod collect_ordered; pub(crate) mod next; pub(crate) mod next_any; pub(crate) mod reduce; - -mod compute; - -pub use compute::ParallelRunnerCompute; diff --git a/src/using/executor/parallel_compute/next.rs b/src/using/executor/parallel_compute/next.rs new file mode 100644 index 00000000..434b3e74 --- /dev/null +++ b/src/using/executor/parallel_compute/next.rs @@ -0,0 +1,79 @@ +use crate::Params; +use crate::generic_values::Values; +use crate::generic_values::runner_results::{Fallibility, NextSuccess, NextWithIdx}; +use crate::runner::{ComputationKind, NumSpawned, ParallelRunner, SharedStateOf}; +use crate::using::executor::thread_compute as th; +use crate::using::using_variants::Using; +use orx_concurrent_iter::ConcurrentIter; + +pub fn m( + using: U, + mut orchestrator: C, + params: Params, + iter: I, + map1: M1, +) -> (NumSpawned, Option) +where + U: Using, + C: ParallelRunner, + I: ConcurrentIter, + O: Send, + M1: Fn(&mut U::Item, I::Item) -> O + Sync, +{ + let thread_map = |nt: NumSpawned, iter: &I, state: &SharedStateOf, thread_runner| { + let u = using.create(nt.into_inner()); + Ok(th::next::m(u, thread_runner, iter, state, &map1)) + }; + let (num_spawned, result) = + orchestrator.map_infallible(params, iter, ComputationKind::Collect, thread_map); + + let next = match result { + Ok(results) => results + .into_iter() + .flatten() + .min_by_key(|x| x.0) + .map(|x| x.1), + }; + (num_spawned, next) +} + +type ResultNext = Result< + Option<(usize, ::Item)>, + <::Fallibility as Fallibility>::Error, +>; + +pub fn x( + using: U, + mut orchestrator: C, + params: Params, + iter: I, + xap1: X1, +) -> (NumSpawned, ResultNext) +where + U: Using, + C: ParallelRunner, + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, +{ + let thread_map = |nt: NumSpawned, iter: &I, state: &SharedStateOf, th_runner| { + let u = using.create(nt.into_inner()); + match th::next::x(u, th_runner, iter, state, &xap1) { + NextWithIdx::Found { idx, value } => Ok(Some(NextSuccess::Found { idx, value })), + NextWithIdx::NotFound => Ok(None), + NextWithIdx::StoppedByWhileCondition { idx } => { + Ok(Some(NextSuccess::StoppedByWhileCondition { idx })) + } + NextWithIdx::StoppedByError { error } => Err(error), + } + }; + let (num_spawned, result) = orchestrator.map_all::( + params, + iter, + ComputationKind::Collect, + thread_map, + ); + let next = result.map(|results| NextSuccess::reduce(results.into_iter().flatten())); + (num_spawned, next) +} diff --git a/src/using/executor/parallel_compute/next_any.rs b/src/using/executor/parallel_compute/next_any.rs new file mode 100644 index 00000000..2ff83899 --- /dev/null +++ b/src/using/executor/parallel_compute/next_any.rs @@ -0,0 +1,66 @@ +use crate::Params; +use crate::generic_values::Values; +use crate::generic_values::runner_results::Fallibility; +use crate::runner::{ComputationKind, NumSpawned, ParallelRunner, SharedStateOf}; +use crate::using::executor::thread_compute as th; +use crate::using::using_variants::Using; +use orx_concurrent_iter::ConcurrentIter; + +pub fn m( + using: U, + mut orchestrator: C, + params: Params, + iter: I, + map1: M1, +) -> (NumSpawned, Option) +where + U: Using, + C: ParallelRunner, + I: ConcurrentIter, + O: Send, + M1: Fn(&mut U::Item, I::Item) -> O + Sync, +{ + let thread_map = |nt: NumSpawned, iter: &I, state: &SharedStateOf, thread_runner| { + let u = using.create(nt.into_inner()); + Ok(th::next_any::m(u, thread_runner, iter, state, &map1)) + }; + let (num_spawned, result) = + orchestrator.map_infallible(params, iter, ComputationKind::Collect, thread_map); + + let next = match result { + Ok(results) => results.into_iter().flatten().next(), + }; + (num_spawned, next) +} + +type ResultNextAny = + Result::Item>, <::Fallibility as Fallibility>::Error>; + +pub fn x( + using: U, + mut orchestrator: C, + params: Params, + iter: I, + xap1: X1, +) -> (NumSpawned, ResultNextAny) +where + U: Using, + C: ParallelRunner, + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, +{ + let thread_map = |nt: NumSpawned, iter: &I, state: &SharedStateOf, th_runner| { + let u = using.create(nt.into_inner()); + th::next_any::x(u, th_runner, iter, state, &xap1) + }; + let (num_spawned, result) = orchestrator.map_all::( + params, + iter, + ComputationKind::Collect, + thread_map, + ); + let next = result.map(|results| results.into_iter().flatten().next()); + (num_spawned, next) +} diff --git a/src/using/executor/parallel_compute/reduce.rs b/src/using/executor/parallel_compute/reduce.rs new file mode 100644 index 00000000..4f3d90b7 --- /dev/null +++ b/src/using/executor/parallel_compute/reduce.rs @@ -0,0 +1,83 @@ +use crate::Params; +use crate::generic_values::Values; +use crate::generic_values::runner_results::Fallibility; +use crate::runner::{ComputationKind, NumSpawned, ParallelRunner, SharedStateOf, ThreadRunnerOf}; +use crate::using::executor::thread_compute as th; +use crate::using::using_variants::Using; +use orx_concurrent_iter::ConcurrentIter; + +pub fn m( + using: U, + mut orchestrator: C, + params: Params, + iter: I, + map1: M1, + reduce: Red, +) -> (NumSpawned, Option) +where + U: Using, + C: ParallelRunner, + I: ConcurrentIter, + M1: Fn(&mut U::Item, I::Item) -> O + Sync, + Red: Fn(&mut U::Item, O, O) -> O + Sync, + O: Send, +{ + let thread_map = + |nt: NumSpawned, iter: &I, state: &SharedStateOf, thread_runner: ThreadRunnerOf| { + let u = using.create(nt.into_inner()); + Ok(th::reduce::m(u, thread_runner, iter, state, &map1, &reduce)) + }; + let (num_spawned, result) = + orchestrator.map_infallible(params, iter, ComputationKind::Collect, thread_map); + + let mut u = using.into_inner(); + let acc = match result { + Ok(results) => results + .into_iter() + .flatten() + .reduce(|a, b| reduce(&mut u, a, b)), + }; + + (num_spawned, acc) +} + +type ResultReduce = + Result::Item>, <::Fallibility as Fallibility>::Error>; + +pub fn x( + using: U, + mut orchestrator: C, + params: Params, + iter: I, + xap1: X1, + reduce: Red, +) -> (NumSpawned, ResultReduce) +where + U: Using, + C: ParallelRunner, + I: ConcurrentIter, + Vo: Values, + Vo::Item: Send, + X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, + Red: Fn(&mut U::Item, Vo::Item, Vo::Item) -> Vo::Item + Sync, +{ + let thread_map = + |nt: NumSpawned, iter: &I, state: &SharedStateOf, thread_runner: ThreadRunnerOf| { + let u = using.create(nt.into_inner()); + th::reduce::x(u, thread_runner, iter, state, &xap1, &reduce).into_result() + }; + let (num_spawned, result) = orchestrator.map_all::( + params, + iter, + ComputationKind::Collect, + thread_map, + ); + let mut u = using.into_inner(); + let acc = result.map(|results| { + results + .into_iter() + .flatten() + .reduce(|a, b| reduce(&mut u, a, b)) + }); + (num_spawned, acc) +} diff --git a/src/using/runner/thread_runner_compute/u_collect_arbitrary.rs b/src/using/executor/thread_compute/collect_arbitrary.rs similarity index 88% rename from src/using/runner/thread_runner_compute/u_collect_arbitrary.rs rename to src/using/executor/thread_compute/collect_arbitrary.rs index b25fb0be..dcc5802e 100644 --- a/src/using/runner/thread_runner_compute/u_collect_arbitrary.rs +++ b/src/using/executor/thread_compute/collect_arbitrary.rs @@ -1,6 +1,6 @@ +use crate::ThreadExecutor; use crate::generic_values::Values; -use crate::generic_values::runner_results::Stop; -use crate::{ThreadRunner, generic_values::runner_results::ThreadCollectArbitrary}; +use crate::generic_values::runner_results::{Stop, ThreadCollectArbitrary}; use orx_concurrent_bag::ConcurrentBag; use orx_concurrent_iter::{ChunkPuller, ConcurrentIter}; use orx_fixed_vec::IntoConcurrentPinnedVec; @@ -8,20 +8,21 @@ use orx_fixed_vec::IntoConcurrentPinnedVec; // m #[cfg(test)] -pub fn u_m( +pub fn m( + mut using: U, mut runner: C, - mut u: U, iter: &I, shared_state: &C::SharedState, map1: &M1, bag: &ConcurrentBag, ) where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, M1: Fn(&mut U, I::Item) -> O, P: IntoConcurrentPinnedVec, O: Send, { + let u = &mut using; let mut chunk_puller = iter.chunk_puller(0); let mut item_puller = iter.item_puller(); @@ -32,7 +33,7 @@ pub fn u_m( match chunk_size { 0 | 1 => match item_puller.next() { - Some(value) => _ = bag.push(map1(&mut u, value)), + Some(value) => _ = bag.push(map1(u, value)), None => break, }, c => { @@ -41,7 +42,7 @@ pub fn u_m( } match chunk_puller.pull() { - Some(chunk) => _ = bag.extend(chunk.map(|value| map1(&mut u, value))), + Some(chunk) => _ = bag.extend(chunk.map(|x| map1(u, x))), None => break, } } @@ -55,22 +56,23 @@ pub fn u_m( // x -pub fn u_x( +pub fn x( + mut using: U, mut runner: C, - mut u: U, iter: &I, shared_state: &C::SharedState, xap1: &X1, bag: &ConcurrentBag, ) -> ThreadCollectArbitrary where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, Vo: Values, X1: Fn(&mut U, I::Item) -> Vo, P: IntoConcurrentPinnedVec, Vo::Item: Send, { + let u = &mut using; let mut chunk_puller = iter.chunk_puller(0); let mut item_puller = iter.item_puller(); @@ -83,7 +85,7 @@ where 0 | 1 => match item_puller.next() { Some(value) => { // TODO: possible to try to get len and bag.extend(values_vt.values()) when available, same holds for chunk below - let vo = xap1(&mut u, value); + let vo = xap1(u, value); let done = vo.push_to_bag(bag); if let Some(stop) = Vo::arbitrary_push_to_stop(done) { @@ -110,7 +112,7 @@ where match chunk_puller.pull() { Some(chunk) => { for value in chunk { - let vo = xap1(&mut u, value); + let vo = xap1(u, value); let done = vo.push_to_bag(bag); if let Some(stop) = Vo::arbitrary_push_to_stop(done) { diff --git a/src/using/runner/thread_runner_compute/u_collect_ordered.rs b/src/using/executor/thread_compute/collect_ordered.rs similarity index 86% rename from src/using/runner/thread_runner_compute/u_collect_ordered.rs rename to src/using/executor/thread_compute/collect_ordered.rs index ce998a3f..698a187a 100644 --- a/src/using/runner/thread_runner_compute/u_collect_ordered.rs +++ b/src/using/executor/thread_compute/collect_ordered.rs @@ -1,42 +1,37 @@ -use crate::{ - ThreadRunner, - generic_values::{ - Values, - runner_results::{StopWithIdx, ThreadCollect}, - }, -}; +use crate::ThreadExecutor; +use crate::generic_values::Values; +use crate::generic_values::runner_results::{StopWithIdx, ThreadCollect}; +use alloc::vec::Vec; use orx_concurrent_iter::{ChunkPuller, ConcurrentIter}; use orx_concurrent_ordered_bag::ConcurrentOrderedBag; use orx_fixed_vec::IntoConcurrentPinnedVec; -// m - -pub fn u_m( +pub fn m( + mut using: U, mut runner: C, - mut u: U, iter: &I, shared_state: &C::SharedState, map1: &M1, o_bag: &ConcurrentOrderedBag, offset: usize, ) where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, M1: Fn(&mut U, I::Item) -> O, P: IntoConcurrentPinnedVec, O: Send, { + let u = &mut using; let mut chunk_puller = iter.chunk_puller(0); let mut item_puller = iter.item_puller_with_idx(); loop { let chunk_size = runner.next_chunk_size(shared_state, iter); - runner.begin_chunk(chunk_size); match chunk_size { 0 | 1 => match item_puller.next() { - Some((idx, value)) => unsafe { o_bag.set_value(offset + idx, map1(&mut u, value)) }, + Some((idx, value)) => unsafe { o_bag.set_value(offset + idx, map1(u, value)) }, None => break, }, c => { @@ -46,7 +41,7 @@ pub fn u_m( match chunk_puller.pull_with_idx() { Some((begin_idx, chunk)) => { - let values = chunk.map(|value| map1(&mut u, value)); + let values = chunk.map(|x| map1(u, x)); unsafe { o_bag.set_values(offset + begin_idx, values) }; } None => break, @@ -60,21 +55,20 @@ pub fn u_m( runner.complete_task(shared_state); } -// x - -pub fn u_x( +pub fn x( + mut using: U, mut runner: C, - mut u: U, iter: &I, shared_state: &C::SharedState, xap1: &X1, ) -> ThreadCollect where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, Vo: Values, X1: Fn(&mut U, I::Item) -> Vo, { + let u = &mut using; let mut collected = Vec::new(); let out_vec = &mut collected; @@ -89,7 +83,7 @@ where match chunk_size { 0 | 1 => match item_puller.next() { Some((idx, i)) => { - let vo = xap1(&mut u, i); + let vo = xap1(u, i); let done = vo.push_to_vec_with_idx(idx, out_vec); if let Some(stop) = Vo::ordered_push_to_stop(done) { iter.skip_to_end(); @@ -117,8 +111,8 @@ where match chunk_puller.pull_with_idx() { Some((chunk_begin_idx, chunk)) => { - for i in chunk { - let vo = xap1(&mut u, i); + for (within_chunk_idx, value) in chunk.enumerate() { + let vo = xap1(u, value); let done = vo.push_to_vec_with_idx(chunk_begin_idx, out_vec); if let Some(stop) = Vo::ordered_push_to_stop(done) { iter.skip_to_end(); @@ -128,7 +122,7 @@ where StopWithIdx::DueToWhile { idx } => { return ThreadCollect::StoppedByWhileCondition { vec: collected, - stopped_idx: idx, + stopped_idx: idx + within_chunk_idx, }; } StopWithIdx::DueToError { idx: _, error } => { diff --git a/src/using/executor/thread_compute/mod.rs b/src/using/executor/thread_compute/mod.rs new file mode 100644 index 00000000..ddc6e7e6 --- /dev/null +++ b/src/using/executor/thread_compute/mod.rs @@ -0,0 +1,5 @@ +pub(super) mod collect_arbitrary; +pub(super) mod collect_ordered; +pub(super) mod next; +pub(super) mod next_any; +pub(super) mod reduce; diff --git a/src/using/runner/thread_runner_compute/u_next.rs b/src/using/executor/thread_compute/next.rs similarity index 95% rename from src/using/runner/thread_runner_compute/u_next.rs rename to src/using/executor/thread_compute/next.rs index c19bfae4..6ad9f075 100644 --- a/src/using/runner/thread_runner_compute/u_next.rs +++ b/src/using/executor/thread_compute/next.rs @@ -1,25 +1,23 @@ use crate::{ - ThreadRunner, - generic_values::{ - Values, - runner_results::{Next, NextWithIdx}, - }, + ThreadExecutor, + generic_values::Values, + generic_values::runner_results::{Next, NextWithIdx}, }; use orx_concurrent_iter::{ChunkPuller, ConcurrentIter}; -pub fn u_m( +pub fn m( + mut using: U, mut runner: C, - mut u: U, iter: &I, shared_state: &C::SharedState, map1: &M1, ) -> Option<(usize, O)> where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, M1: Fn(&mut U, I::Item) -> O, { - let u = &mut u; + let u = &mut using; let mut chunk_puller = iter.chunk_puller(0); let mut item_puller = iter.item_puller_with_idx(); @@ -66,20 +64,20 @@ where None } -pub fn u_x( +pub fn x( + mut using: U, mut runner: C, - mut u: U, iter: &I, shared_state: &C::SharedState, xap1: &X1, ) -> NextWithIdx where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, Vo: Values, X1: Fn(&mut U, I::Item) -> Vo, { - let u = &mut u; + let u = &mut using; let mut chunk_puller = iter.chunk_puller(0); let mut item_puller = iter.item_puller_with_idx(); diff --git a/src/using/runner/thread_runner_compute/u_next_any.rs b/src/using/executor/thread_compute/next_any.rs similarity index 96% rename from src/using/runner/thread_runner_compute/u_next_any.rs rename to src/using/executor/thread_compute/next_any.rs index c90b7f84..cf19973f 100644 --- a/src/using/runner/thread_runner_compute/u_next_any.rs +++ b/src/using/executor/thread_compute/next_any.rs @@ -1,24 +1,24 @@ use crate::{ - ThreadRunner, + ThreadExecutor, generic_values::Values, generic_values::runner_results::{Fallibility, Next}, }; use orx_concurrent_iter::{ChunkPuller, ConcurrentIter}; -pub fn u_m( +pub fn m( + mut using: U, mut runner: C, - mut u: U, iter: &I, shared_state: &C::SharedState, map1: &M1, ) -> Option where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, O: Send, M1: Fn(&mut U, I::Item) -> O, { - let u = &mut u; + let u = &mut using; let mut chunk_puller = iter.chunk_puller(0); let mut item_puller = iter.item_puller(); @@ -65,21 +65,21 @@ where None } -pub fn u_x( +pub fn x( + mut using: U, mut runner: C, - mut u: U, iter: &I, shared_state: &C::SharedState, xap1: &X1, ) -> Result, ::Error> where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, Vo: Values, Vo::Item: Send, X1: Fn(&mut U, I::Item) -> Vo, { - let u = &mut u; + let u = &mut using; let mut chunk_puller = iter.chunk_puller(0); let mut item_puller = iter.item_puller(); diff --git a/src/using/runner/thread_runner_compute/u_reduce.rs b/src/using/executor/thread_compute/reduce.rs similarity index 83% rename from src/using/runner/thread_runner_compute/u_reduce.rs rename to src/using/executor/thread_compute/reduce.rs index bb138914..8e1c8e55 100644 --- a/src/using/runner/thread_runner_compute/u_reduce.rs +++ b/src/using/executor/thread_compute/reduce.rs @@ -1,5 +1,5 @@ use crate::{ - ThreadRunner, + ThreadExecutor, generic_values::{ Values, runner_results::{Reduce, StopReduce}, @@ -9,16 +9,16 @@ use orx_concurrent_iter::{ChunkPuller, ConcurrentIter}; // m -pub fn u_m( - mut runner: C, +pub fn m( mut u: U, + mut runner: C, iter: &I, shared_state: &C::SharedState, map1: &M1, reduce: &Red, ) -> Option where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, M1: Fn(&mut U, I::Item) -> O, Red: Fn(&mut U, O, O) -> O, @@ -50,26 +50,21 @@ where } match chunk_puller.pull() { - Some(mut chunk) => { - acc = match acc { - Some(mut acc) => { - for a in chunk { - let a = map1(u, a); - acc = reduce(u, acc, a); - } - Some(acc) + Some(chunk) => { + let mut res = None; + for x in chunk { + let b = map1(u, x); + res = match res { + Some(a) => Some(reduce(u, a, b)), + None => Some(b), } - None => match chunk.next() { - Some(a) => { - let mut acc = map1(u, a); - for a in chunk { - let a = map1(u, a); - acc = reduce(u, acc, a); - } - Some(acc) - } - None => None, + } + acc = match acc { + Some(x) => match res { + Some(y) => Some(reduce(u, x, y)), + None => Some(x), }, + None => res, }; } None => break, @@ -86,23 +81,22 @@ where // x -pub fn u_x( - mut runner: C, +pub fn x( mut u: U, + mut runner: C, iter: &I, shared_state: &C::SharedState, xap1: &X1, reduce: &Red, ) -> Reduce where - C: ThreadRunner, + C: ThreadExecutor, I: ConcurrentIter, Vo: Values, X1: Fn(&mut U, I::Item) -> Vo, Red: Fn(&mut U, Vo::Item, Vo::Item) -> Vo::Item, { let u = &mut u; - let mut chunk_puller = iter.chunk_puller(0); let mut item_puller = iter.item_puller(); diff --git a/src/using/mod.rs b/src/using/mod.rs index 2298834a..9e261baf 100644 --- a/src/using/mod.rs +++ b/src/using/mod.rs @@ -1,11 +1,10 @@ mod collect_into; -/// Module containing variants of parallel iterators using a mutable variable. -pub mod computational_variants; -mod computations; -mod runner; +mod computational_variants; +mod executor; mod u_par_iter; mod using_variants; pub(crate) use collect_into::UParCollectIntoCore; +pub(crate) use computational_variants::{UPar, UParMap, UParXap}; pub use u_par_iter::ParIterUsing; pub use using_variants::{Using, UsingClone, UsingFun}; diff --git a/src/using/runner/mod.rs b/src/using/runner/mod.rs deleted file mode 100644 index 2fd84c79..00000000 --- a/src/using/runner/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub(crate) mod parallel_runner_compute; -mod thread_runner_compute; diff --git a/src/using/runner/parallel_runner_compute/mod.rs b/src/using/runner/parallel_runner_compute/mod.rs deleted file mode 100644 index 25f59bcc..00000000 --- a/src/using/runner/parallel_runner_compute/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub(crate) mod u_collect_arbitrary; -pub(crate) mod u_collect_ordered; -pub(crate) mod u_next; -pub(crate) mod u_next_any; -pub(crate) mod u_reduce; diff --git a/src/using/runner/parallel_runner_compute/u_collect_arbitrary.rs b/src/using/runner/parallel_runner_compute/u_collect_arbitrary.rs deleted file mode 100644 index a122fb0a..00000000 --- a/src/using/runner/parallel_runner_compute/u_collect_arbitrary.rs +++ /dev/null @@ -1,156 +0,0 @@ -use super::super::thread_runner_compute as thread; -use crate::generic_values::Values; -use crate::generic_values::runner_results::{ParallelCollectArbitrary, ThreadCollectArbitrary}; -use crate::runner::ParallelRunnerCompute; -use crate::using::Using; -#[cfg(test)] -use crate::using::computations::UM; -use crate::using::computations::UX; -use orx_concurrent_bag::ConcurrentBag; -use orx_concurrent_iter::ConcurrentIter; -use orx_fixed_vec::IntoConcurrentPinnedVec; - -// m - -#[cfg(test)] -pub fn u_m(runner: C, m: UM, pinned_vec: P) -> (usize, P) -where - C: ParallelRunnerCompute, - U: Using, - I: ConcurrentIter, - M1: Fn(&mut U::Item, I::Item) -> O + Sync, - P: IntoConcurrentPinnedVec, - O: Send, -{ - let capacity_bound = pinned_vec.capacity_bound(); - let offset = pinned_vec.len(); - let (mut using, _, iter, map1) = m.destruct(); - - let mut bag: ConcurrentBag = pinned_vec.into(); - match iter.try_get_len() { - Some(iter_len) => bag.reserve_maximum_capacity(offset + iter_len), - None => bag.reserve_maximum_capacity(capacity_bound), - }; - - // compute - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - std::thread::scope(|s| { - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - let u = using.create(num_spawned); - num_spawned += 1; - s.spawn(|| { - thread::u_collect_arbitrary::u_m( - runner.new_thread_runner(shared_state), - u, - &iter, - shared_state, - &map1, - &bag, - ); - }); - } - }); - let values = bag.into_inner(); - (num_spawned, values) -} - -// x - -pub fn u_x( - runner: C, - x: UX, - pinned_vec: P, -) -> (usize, ParallelCollectArbitrary) -where - C: ParallelRunnerCompute, - U: Using, - I: ConcurrentIter, - Vo: Values, - Vo::Item: Send, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync, - P: IntoConcurrentPinnedVec, -{ - let capacity_bound = pinned_vec.capacity_bound(); - let offset = pinned_vec.len(); - let (mut using, _, iter, xap1) = x.destruct(); - - let mut bag: ConcurrentBag = pinned_vec.into(); - match iter.try_get_len() { - Some(iter_len) => bag.reserve_maximum_capacity(offset + iter_len), - None => bag.reserve_maximum_capacity(capacity_bound), - }; - - // compute - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let result: ThreadCollectArbitrary = std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - let u = using.create(num_spawned); - num_spawned += 1; - handles.push(s.spawn(|| { - thread::u_collect_arbitrary::u_x( - runner.new_thread_runner(shared_state), - u, - &iter, - shared_state, - &xap1, - &bag, - ) - })); - } - - let mut early_exit_result = None; - while !handles.is_empty() { - let mut finished_idx = None; - for (h, handle) in handles.iter().enumerate() { - if handle.is_finished() { - finished_idx = Some(h); - break; - } - } - - if let Some(h) = finished_idx { - let handle = handles.remove(h); - let result = handle.join().expect("failed to join the thread"); - match &result { - ThreadCollectArbitrary::AllCollected => {} - ThreadCollectArbitrary::StoppedByError { error: _ } => { - early_exit_result = Some(result); - break; - } - ThreadCollectArbitrary::StoppedByWhileCondition => { - early_exit_result = Some(result); - } - } - } - } - - early_exit_result.unwrap_or(ThreadCollectArbitrary::AllCollected) - }); - - ( - num_spawned, - match result { - ThreadCollectArbitrary::AllCollected => ParallelCollectArbitrary::AllCollected { - pinned_vec: bag.into_inner(), - }, - ThreadCollectArbitrary::StoppedByWhileCondition => { - ParallelCollectArbitrary::StoppedByWhileCondition { - pinned_vec: bag.into_inner(), - } - } - ThreadCollectArbitrary::StoppedByError { error } => { - ParallelCollectArbitrary::StoppedByError { error } - } - }, - ) -} diff --git a/src/using/runner/parallel_runner_compute/u_collect_ordered.rs b/src/using/runner/parallel_runner_compute/u_collect_ordered.rs deleted file mode 100644 index 5050d859..00000000 --- a/src/using/runner/parallel_runner_compute/u_collect_ordered.rs +++ /dev/null @@ -1,132 +0,0 @@ -use super::super::thread_runner_compute as thread; -use crate::generic_values::Values; -use crate::generic_values::runner_results::{Fallibility, ParallelCollect, ThreadCollect}; -use crate::runner::ParallelRunnerCompute; -use crate::using::Using; -use crate::using::computations::{UM, UX}; -use orx_concurrent_iter::ConcurrentIter; -use orx_concurrent_ordered_bag::ConcurrentOrderedBag; -use orx_fixed_vec::IntoConcurrentPinnedVec; - -// m - -pub fn u_m(runner: C, m: UM, pinned_vec: P) -> (usize, P) -where - C: ParallelRunnerCompute, - U: Using, - I: ConcurrentIter, - O: Send, - M1: Fn(&mut U::Item, I::Item) -> O + Sync, - P: IntoConcurrentPinnedVec, -{ - let offset = pinned_vec.len(); - let (mut using, _, iter, map1) = m.destruct(); - - let o_bag: ConcurrentOrderedBag = pinned_vec.into(); - - // compute - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - std::thread::scope(|s| { - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - let u = using.create(num_spawned); - num_spawned += 1; - s.spawn(|| { - thread::u_collect_ordered::u_m( - runner.new_thread_runner(shared_state), - u, - &iter, - shared_state, - &map1, - &o_bag, - offset, - ); - }); - } - }); - - let values = unsafe { o_bag.into_inner().unwrap_only_if_counts_match() }; - (num_spawned, values) -} - -// x - -pub fn u_x( - runner: C, - x: UX, - pinned_vec: P, -) -> (usize, ParallelCollect) -where - C: ParallelRunnerCompute, - U: Using, - I: ConcurrentIter, - Vo: Values, - Vo::Item: Send, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync, - P: IntoConcurrentPinnedVec, -{ - let (mut using, _, iter, xap1) = x.destruct(); - - // compute - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let result: Result>, ::Error> = - std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - let u = using.create(num_spawned); - num_spawned += 1; - handles.push(s.spawn(|| { - thread::u_collect_ordered::u_x( - runner.new_thread_runner(shared_state), - u, - &iter, - shared_state, - &xap1, - ) - })); - } - - let mut results = Vec::with_capacity(handles.len()); - - let mut error = None; - while !handles.is_empty() { - let mut finished_idx = None; - for (h, handle) in handles.iter().enumerate() { - if handle.is_finished() { - finished_idx = Some(h); - break; - } - } - - if let Some(h) = finished_idx { - let handle = handles.remove(h); - let result = handle.join().expect("failed to join the thread"); - match result.into_result() { - Ok(result) => results.push(result), - Err(e) => { - error = Some(e); - break; - } - } - } - } - - match error { - Some(error) => Err(error), - None => Ok(results), - } - }); - - let result = match result { - Err(error) => ParallelCollect::StoppedByError { error }, - Ok(results) => ParallelCollect::reduce(results, pinned_vec), - }; - - (num_spawned, result) -} diff --git a/src/using/runner/parallel_runner_compute/u_next.rs b/src/using/runner/parallel_runner_compute/u_next.rs deleted file mode 100644 index b2ef9e3b..00000000 --- a/src/using/runner/parallel_runner_compute/u_next.rs +++ /dev/null @@ -1,131 +0,0 @@ -use super::super::thread_runner_compute as thread; -use crate::generic_values::Values; -use crate::generic_values::runner_results::{Fallibility, NextSuccess, NextWithIdx}; -use crate::runner::ParallelRunnerCompute; -use crate::using::Using; -use crate::using::computations::{UM, UX}; -use orx_concurrent_iter::ConcurrentIter; - -pub fn u_m(runner: C, m: UM) -> (usize, Option) -where - C: ParallelRunnerCompute, - U: Using, - I: ConcurrentIter, - O: Send, - M1: Fn(&mut U::Item, I::Item) -> O + Sync, -{ - let (mut using, _, iter, map1) = m.destruct(); - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let results = std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - let u = using.create(num_spawned); - num_spawned += 1; - handles.push(s.spawn(|| { - thread::u_next::u_m( - runner.new_thread_runner(shared_state), - u, - &iter, - shared_state, - &map1, - ) - })) - } - - let mut results: Vec<(usize, O)> = Vec::with_capacity(handles.len()); - for x in handles { - if let Some(x) = x.join().expect("failed to join the thread") { - results.push(x); - } - } - results - }); - - let acc = results.into_iter().min_by_key(|x| x.0).map(|x| x.1); - - (num_spawned, acc) -} - -type ResultNext = Result< - Option<(usize, ::Item)>, - <::Fallibility as Fallibility>::Error, ->; - -pub fn u_x(runner: C, x: UX) -> (usize, ResultNext) -where - C: ParallelRunnerCompute, - U: Using, - I: ConcurrentIter, - Vo: Values, - Vo::Item: Send, - X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, -{ - let (mut using, _, iter, xap1) = x.destruct(); - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let result: Result>, _> = std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - let u = using.create(num_spawned); - num_spawned += 1; - handles.push(s.spawn(|| { - thread::u_next::u_x( - runner.new_thread_runner(shared_state), - u, - &iter, - shared_state, - &xap1, - ) - })) - } - - let mut results = Vec::with_capacity(handles.len()); - - let mut error = None; - while !handles.is_empty() { - let mut finished_idx = None; - for (h, handle) in handles.iter().enumerate() { - if handle.is_finished() { - finished_idx = Some(h); - break; - } - } - - if let Some(h) = finished_idx { - let handle = handles.remove(h); - let result = handle.join().expect("failed to join the thread"); - match result { - NextWithIdx::Found { idx, value } => { - results.push(NextSuccess::Found { idx, value }) - } - NextWithIdx::NotFound => {} - NextWithIdx::StoppedByWhileCondition { idx } => { - results.push(NextSuccess::StoppedByWhileCondition { idx }); - } - NextWithIdx::StoppedByError { error: e } => { - error = Some(e); - break; - } - } - } - } - - match error { - Some(error) => Err(error), - None => Ok(results), - } - }); - - let next = result.map(NextSuccess::reduce); - - (num_spawned, next) -} diff --git a/src/using/runner/parallel_runner_compute/u_next_any.rs b/src/using/runner/parallel_runner_compute/u_next_any.rs deleted file mode 100644 index 42f022a8..00000000 --- a/src/using/runner/parallel_runner_compute/u_next_any.rs +++ /dev/null @@ -1,113 +0,0 @@ -use super::super::thread_runner_compute as thread; -use crate::generic_values::runner_results::Fallibility; -use crate::using::Using; -use crate::using::computations::{UM, UX}; -use crate::{generic_values::Values, runner::ParallelRunnerCompute}; -use orx_concurrent_iter::ConcurrentIter; - -pub fn u_m(runner: C, m: UM) -> (usize, Option) -where - C: ParallelRunnerCompute, - U: Using, - I: ConcurrentIter, - O: Send, - M1: Fn(&mut U::Item, I::Item) -> O + Sync, -{ - let (mut using, _, iter, xap1) = m.destruct(); - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let result = std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - let u = using.create(num_spawned); - num_spawned += 1; - handles.push(s.spawn(|| { - thread::u_next_any::u_m( - runner.new_thread_runner(shared_state), - u, - &iter, - shared_state, - &xap1, - ) - })); - } - - // do not wait to join other threads - handles - .into_iter() - .find_map(|x| x.join().expect("failed to join the thread")) - }); - - (num_spawned, result) -} - -type ResultNextAny = - Result::Item>, <::Fallibility as Fallibility>::Error>; - -pub fn u_x(runner: C, x: UX) -> (usize, ResultNextAny) -where - C: ParallelRunnerCompute, - U: Using, - I: ConcurrentIter, - Vo: Values, - Vo::Item: Send, - X1: Fn(&mut U::Item, I::Item) -> Vo + Sync, -{ - let (mut using, _, iter, xap1) = x.destruct(); - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let result = std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - let u = using.create(num_spawned); - num_spawned += 1; - handles.push(s.spawn(|| { - thread::u_next_any::u_x( - runner.new_thread_runner(shared_state), - u, - &iter, - shared_state, - &xap1, - ) - })); - } - - let mut result = Ok(None); - while !handles.is_empty() { - let mut finished_idx = None; - for (h, handle) in handles.iter().enumerate() { - if handle.is_finished() { - finished_idx = Some(h); - break; - } - } - - if let Some(h) = finished_idx { - let handle = handles.remove(h); - match handle.join().expect("failed to join the thread") { - Ok(Some(x)) => { - result = Ok(Some(x)); - break; - } - Err(error) => { - result = Err(error); - break; - } - Ok(None) => {} - } - } - } - - result - }); - - (num_spawned, result) -} diff --git a/src/using/runner/parallel_runner_compute/u_reduce.rs b/src/using/runner/parallel_runner_compute/u_reduce.rs deleted file mode 100644 index 2032fbfc..00000000 --- a/src/using/runner/parallel_runner_compute/u_reduce.rs +++ /dev/null @@ -1,139 +0,0 @@ -use super::super::thread_runner_compute as thread; -use crate::generic_values::Values; -use crate::generic_values::runner_results::{Fallibility, Reduce}; -use crate::runner::ParallelRunnerCompute; -use crate::using::Using; -use crate::using::computations::{UM, UX}; -use orx_concurrent_iter::ConcurrentIter; - -// m - -pub fn u_m(runner: C, m: UM, reduce: Red) -> (usize, Option) -where - C: ParallelRunnerCompute, - U: Using, - I: ConcurrentIter, - O: Send, - M1: Fn(&mut U::Item, I::Item) -> O + Sync, - Red: Fn(&mut U::Item, O, O) -> O + Sync, -{ - let (mut using, _, iter, map1) = m.destruct(); - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let results = std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - let u = using.create(num_spawned); - num_spawned += 1; - handles.push(s.spawn(|| { - thread::u_reduce::u_m( - runner.new_thread_runner(shared_state), - u, - &iter, - shared_state, - &map1, - &reduce, - ) - })); - } - - let mut results = Vec::with_capacity(handles.len()); - for x in handles { - if let Some(x) = x.join().expect("failed to join the thread") { - results.push(x); - } - } - results - }); - - let mut u = using.into_inner(); - let acc = results.into_iter().reduce(|a, b| reduce(&mut u, a, b)); - - (num_spawned, acc) -} - -// x - -type ResultReduce = - Result::Item>, <::Fallibility as Fallibility>::Error>; - -pub fn u_x( - runner: C, - x: UX, - reduce: Red, -) -> (usize, ResultReduce) -where - C: ParallelRunnerCompute, - U: Using, - I: ConcurrentIter, - Vo: Values, - Vo::Item: Send, - M1: Fn(&mut U::Item, I::Item) -> Vo + Sync, - Red: Fn(&mut U::Item, Vo::Item, Vo::Item) -> Vo::Item + Sync, -{ - let (mut using, _, iter, xap1) = x.destruct(); - - let state = runner.new_shared_state(); - let shared_state = &state; - - let mut num_spawned = 0; - let result: Result, _> = std::thread::scope(|s| { - let mut handles = vec![]; - - while runner.do_spawn_new(num_spawned, shared_state, &iter) { - let u = using.create(num_spawned); - num_spawned += 1; - handles.push(s.spawn(|| { - thread::u_reduce::u_x( - runner.new_thread_runner(shared_state), - u, - &iter, - shared_state, - &xap1, - &reduce, - ) - })); - } - - let mut results = Vec::with_capacity(handles.len()); - - let mut error = None; - while !handles.is_empty() { - let mut finished_idx = None; - for (h, handle) in handles.iter().enumerate() { - if handle.is_finished() { - finished_idx = Some(h); - break; - } - } - - if let Some(h) = finished_idx { - let handle = handles.remove(h); - let result = handle.join().expect("failed to join the thread"); - match result { - Reduce::Done { acc: Some(acc) } => results.push(acc), - Reduce::StoppedByWhileCondition { acc: Some(acc) } => results.push(acc), - Reduce::StoppedByError { error: e } => { - error = Some(e); - break; - } - _ => {} - } - } - } - - match error { - Some(error) => Err(error), - None => Ok(results), - } - }); - - let mut u = using.into_inner(); - let acc = result.map(|results| results.into_iter().reduce(|a, b| reduce(&mut u, a, b))); - - (num_spawned, acc) -} diff --git a/src/using/runner/thread_runner_compute/mod.rs b/src/using/runner/thread_runner_compute/mod.rs deleted file mode 100644 index 25f59bcc..00000000 --- a/src/using/runner/thread_runner_compute/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub(crate) mod u_collect_arbitrary; -pub(crate) mod u_collect_ordered; -pub(crate) mod u_next; -pub(crate) mod u_next_any; -pub(crate) mod u_reduce; diff --git a/src/using/u_par_iter.rs b/src/using/u_par_iter.rs index 315a9291..75094637 100644 --- a/src/using/u_par_iter.rs +++ b/src/using/u_par_iter.rs @@ -1,11 +1,9 @@ use crate::{ - ChunkSize, DefaultRunner, IterationOrder, NumThreads, ParCollectInto, ParallelRunner, Params, - Sum, - using::{ - Using, - computations::{u_map_clone, u_map_copy, u_map_count, u_reduce_sum, u_reduce_unit}, - }, + ChunkSize, IterationOrder, NumThreads, ParCollectInto, Params, RunnerWithPool, Sum, + runner::{DefaultRunner, ParallelRunner}, + using::using_variants::Using, }; +use crate::{ParThreadPool, default_fns::*}; use core::cmp::Ordering; use orx_concurrent_iter::ConcurrentIter; @@ -60,8 +58,32 @@ where /// Rather than the [`DefaultRunner`], uses the parallel runner `Q` which implements [`ParallelRunner`]. /// - /// See [crate::ParIter::with_runner] for details. - fn with_runner(self) -> impl ParIterUsing; + /// See [`ParIter::with_runner`] for details. + /// + /// [`DefaultRunner`]: crate::DefaultRunner + /// [`ParIter::with_runner`]: crate::ParIter::with_runner + fn with_runner( + self, + orchestrator: Q, + ) -> impl ParIterUsing; + + /// Rather than [`DefaultPool`], uses the parallel runner with the given `pool` implementing + /// [`ParThreadPool`]. + /// + /// See [`ParIter::with_pool`] for details. + /// + /// [`DefaultPool`]: crate::DefaultPool + /// [`ParIter::with_pool`]: crate::ParIter::with_pool + fn with_pool( + self, + pool: P, + ) -> impl ParIterUsing, Item = Self::Item> + where + Self: Sized, + { + let runner = RunnerWithPool::from(pool).with_executor::(); + self.with_runner(runner) + } // computation transformations diff --git a/src/using/using_variants.rs b/src/using/using_variants.rs index 5489d235..bdb7484e 100644 --- a/src/using/using_variants.rs +++ b/src/using/using_variants.rs @@ -2,30 +2,30 @@ /// and used mutable by the defined computation. /// /// [`create`]: crate::using::Using::create -pub trait Using { +pub trait Using: Sync { /// Item to be used mutably by each threads used in parallel computation. - type Item: Send + 'static; + type Item: 'static; /// Creates an instance of the variable to be used by the `thread_idx`-th thread. - fn create(&mut self, thread_idx: usize) -> Self::Item; + fn create(&self, thread_idx: usize) -> Self::Item; /// Consumes self and creates exactly one instance of the variable. fn into_inner(self) -> Self::Item; } /// Using variant that creates instances of each thread by cloning an initial value. -pub struct UsingClone(T); +pub struct UsingClone(T); -impl UsingClone { +impl UsingClone { pub(crate) fn new(value: T) -> Self { Self(value) } } -impl Using for UsingClone { +impl Using for UsingClone { type Item = T; - fn create(&mut self, _: usize) -> T { + fn create(&self, _: usize) -> T { self.0.clone() } @@ -34,19 +34,21 @@ impl Using for UsingClone { } } +unsafe impl Sync for UsingClone {} + /// Using variant that creates instances of each thread using a closure. pub struct UsingFun where - T: Send + 'static, - F: FnMut(usize) -> T, + T: 'static, + F: Fn(usize) -> T + Sync, { fun: F, } impl UsingFun where - T: Send + 'static, - F: FnMut(usize) -> T, + T: 'static, + F: Fn(usize) -> T + Sync, { pub(crate) fn new(fun: F) -> Self { Self { fun } @@ -55,16 +57,16 @@ where impl Using for UsingFun where - T: Send + 'static, - F: FnMut(usize) -> T, + T: 'static, + F: Fn(usize) -> T + Sync, { type Item = T; - fn create(&mut self, thread_idx: usize) -> Self::Item { + fn create(&self, thread_idx: usize) -> Self::Item { (self.fun)(thread_idx) } - fn into_inner(mut self) -> Self::Item { + fn into_inner(self) -> Self::Item { (self.fun)(0) } } diff --git a/tests/mut_iter.rs b/tests/mut_iter.rs index 50e5a3a2..f945a9b8 100644 --- a/tests/mut_iter.rs +++ b/tests/mut_iter.rs @@ -1,6 +1,6 @@ -use criterion::black_box; use orx_parallel::*; use std::collections::HashMap; +use std::hint::black_box; use test_case::test_matrix; #[cfg(miri)] diff --git a/tests/parallel_drainable.rs b/tests/parallel_drainable.rs index 2112ed6b..c9e10147 100644 --- a/tests/parallel_drainable.rs +++ b/tests/parallel_drainable.rs @@ -1,5 +1,5 @@ +use core::ops::Range; use orx_parallel::*; -use std::ops::Range; use test_case::test_matrix; #[derive(Clone, Debug)] diff --git a/tests/trait_bounds.rs b/tests/trait_bounds.rs index e1d0caf6..5ac87c42 100644 --- a/tests/trait_bounds.rs +++ b/tests/trait_bounds.rs @@ -13,8 +13,8 @@ fn trait_bounds_parallelizable() { fun(&vec![1, 2, 3]); fun(&VecDeque::::new()); fun(0..9); - // fun(&FixedVec::::new(3)); - // fun(&SplitVec::::new()); + fun(&FixedVec::::new(3)); + fun(&SplitVec::::new()); } #[test] @@ -26,8 +26,8 @@ fn trait_bounds_parallelizable_collection() { fun(vec![1, 2, 3]); fun(VecDeque::::new()); - // fun(FixedVec::::new(3)); - // fun(SplitVec::::new()); + fun(FixedVec::::new(3)); + fun(SplitVec::::new()); } #[test] @@ -40,14 +40,14 @@ fn trait_bounds_into_par_iter() { // owned fun(vec![1, 2, 3]); fun(VecDeque::::new()); - // fun(FixedVec::::new(3)); - // fun(SplitVec::::new()); + fun(FixedVec::::new(3)); + fun(SplitVec::::new()); // ref fun(vec![1, 2, 3].as_slice()); fun(&vec![1, 2, 3]); fun(&VecDeque::::new()); fun(0..9); - // fun(&FixedVec::::new(3)); - // fun(&SplitVec::::new()); + fun(&FixedVec::::new(3)); + fun(&SplitVec::::new()); } diff --git a/tests/whilst/collect.rs b/tests/whilst/collect.rs index d1d93bde..32c544bd 100644 --- a/tests/whilst/collect.rs +++ b/tests/whilst/collect.rs @@ -1,6 +1,6 @@ use crate::fibonacci; -use criterion::black_box; use orx_parallel::*; +use std::hint::black_box; use test_case::test_case; #[test_case(0, 0, 0, 0, "0")] diff --git a/tests/whilst/collect_arbitrary.rs b/tests/whilst/collect_arbitrary.rs index 47c0e338..2bebc53e 100644 --- a/tests/whilst/collect_arbitrary.rs +++ b/tests/whilst/collect_arbitrary.rs @@ -1,6 +1,6 @@ use crate::fibonacci; -use criterion::black_box; use orx_parallel::*; +use std::hint::black_box; use test_case::test_case; #[test_case(512, 4, 0, 3, "22", 220)] diff --git a/tests/whilst/find.rs b/tests/whilst/find.rs index 366de817..95e32dcc 100644 --- a/tests/whilst/find.rs +++ b/tests/whilst/find.rs @@ -1,6 +1,6 @@ use crate::fibonacci; -use criterion::black_box; use orx_parallel::*; +use std::hint::black_box; use test_case::test_case; #[test_case(511, 0, 0, &[333], &[333], None)] diff --git a/tests/whilst/reduce.rs b/tests/whilst/reduce.rs index 6a2a3391..7b8cd10d 100644 --- a/tests/whilst/reduce.rs +++ b/tests/whilst/reduce.rs @@ -1,6 +1,6 @@ use crate::fibonacci; -use criterion::black_box; use orx_parallel::*; +use std::hint::black_box; use test_case::test_case; #[test_case(511, 0, 0, 333, 332*331/2)]