pub trait Par: Sized + ParCore {
Show 38 methods
// Required methods
fn runner<Q: ParRunner>(
self,
runner: Q,
) -> impl Par<Item = Self::Item, Xap = Self::Xap, Input = Self::Input>;
fn runner_with_diagnostics(
self,
) -> impl Par<Item = Self::Item, Xap = Self::Xap, Input = Self::Input>;
fn num_threads(self, num_threads: impl Into<NumThreads>) -> Self;
fn chunk_size(self, chunk_size: impl Into<ChunkSize>) -> Self;
fn iteration_order(self, collect: IterationOrder) -> Self;
fn map<Q, H>(
self,
h: H,
) -> impl Par<Item = Q, Xap = MapOf<Self::Xap, Q, H>, Input = Self::Input>
where H: Fn(Self::Item) -> Q + Copy + Send;
fn inspect<H>(
self,
h: H,
) -> impl Par<Item = Self::Item, Xap = InsOf<Self::Xap, H>, Input = Self::Input>
where H: Fn(&Self::Item) + Copy + Send;
fn filter<H>(
self,
h: H,
) -> impl Par<Item = Self::Item, Xap = FilOf<Self::Xap, H>, Input = Self::Input>
where H: Fn(&Self::Item) -> bool + Copy + Send;
fn filter_map<Q, H>(
self,
h: H,
) -> impl Par<Item = Q, Xap = FilMapOf<Self::Xap, Q, H>, Input = Self::Input>
where H: Fn(Self::Item) -> Option<Q> + Copy + Send;
fn flat_map<V, H>(
self,
h: H,
) -> impl Par<Item = V::Item, Xap = FlatMapOf<Self::Xap, V, H>, Input = Self::Input>
where V: IntoIterator,
H: Fn(Self::Item) -> V + Copy + Send;
fn flatten(
self,
) -> impl Par<Item = <Self::Item as IntoIterator>::Item, Xap = FlattenOf<Self::Xap>, Input = Self::Input>
where Self::Item: IntoIterator;
fn size_hint(&self) -> (usize, Option<usize>);
fn first(self) -> Option<Self::Item>
where Self::Item: Send;
fn reduce<F>(self, f: F) -> Option<Self::Item>
where F: Fn(Self::Item, Self::Item) -> Self::Item + Send + Copy,
Self::Item: Send;
fn collect_into<P>(self, dst: &mut P)
where P: ParExtend<Self::Item>,
Self::Item: Send;
// Provided methods
fn into_optional<T>(
self,
) -> impl ParOption<Elem = T, Xap1 = Self::Xap, M = T, Xap2 = Id<T>, Input = Self::Input, Size = <<Self::Xap as Xap>::Size as Size>::IntoPair>
where Self::Xap: Xap<O = Option<T>> { ... }
fn into_fallible<T, E>(
self,
) -> impl ParResult<Elem = T, Error = E, Xap1 = Self::Xap, M = T, Xap2 = Id<T>, Input = Self::Input, Size = <<Self::Xap as Xap>::Size as Size>::IntoPair>
where Self::Xap: Xap<O = Result<T, E>> { ... }
fn use_new<U, F>(
self,
f: F,
) -> impl ParUse<Item = Self::Item, Use = U, Xap = IdUse<Self::Xap, U>, Input = Self::Input>
where U: Send,
F: Fn(usize) -> U + Sync { ... }
fn use_vec<U, F>(
self,
use_vec: &mut UseVec<U, F>,
) -> impl ParUse<Item = Self::Item, Use = U, Xap = IdUse<Self::Xap, U>, Input = Self::Input>
where U: Send,
F: Fn(usize) -> U + Sync { ... }
fn use_slice<'a, U>(
self,
slice: &'a mut [U],
) -> impl ParUse<Item = Self::Item, Use = U, Xap = IdUse<Self::Xap, U>, Input = Self::Input>
where U: Send + 'a { ... }
fn copied<'a, O>(
self,
) -> impl Par<Item = O, Xap = MappedOf<Self::Xap, FnCopied<'a, O>>, Input = Self::Input>
where Self: Par<Item = &'a O>,
O: Copy + 'a { ... }
fn cloned<'a, O>(
self,
) -> impl Par<Item = O, Xap = MappedOf<Self::Xap, FnCloned<'a, O>>, Input = Self::Input>
where Self: Par<Item = &'a O>,
O: Clone + 'a { ... }
fn len(&self) -> usize
where Self::Input: ExactSizeConcurrentIter,
Self::Xap: Xap<Size = One> { ... }
fn is_empty(&self) -> bool
where Self::Input: ExactSizeConcurrentIter,
Self::Xap: Xap<Size = One> { ... }
fn collect<P>(self) -> P
where P: ParExtend<Self::Item> + Default,
Self::Item: Send { ... }
fn all<F>(self, f: F) -> bool
where F: Fn(&Self::Item) -> bool + Sync { ... }
fn any<F>(self, f: F) -> bool
where F: Fn(&Self::Item) -> bool + Sync { ... }
fn count(self) -> usize { ... }
fn find<F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send,
F: Fn(&Self::Item) -> bool + Sync { ... }
fn fold<B, I, F>(self, init: I, f: F) -> Vec<B>
where B: Send,
I: Fn() -> B + Sync,
F: Fn(&mut B, Self::Item) + Copy + Send { ... }
fn for_each<F>(self, f: F)
where F: Fn(Self::Item) + Send + Copy { ... }
fn max(self) -> Option<Self::Item>
where Self::Item: Ord + Send { ... }
fn max_by<F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send,
F: Fn(&Self::Item, &Self::Item) -> Ordering + Sync { ... }
fn max_by_key<B, F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send,
B: Ord,
F: Fn(&Self::Item) -> B + Sync { ... }
fn min(self) -> Option<Self::Item>
where Self::Item: Ord + Send { ... }
fn min_by<F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send,
F: Fn(&Self::Item, &Self::Item) -> Ordering + Sync { ... }
fn min_by_key<B, F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send,
B: Ord,
F: Fn(&Self::Item) -> B + Sync { ... }
fn sum<S>(self) -> S
where Self::Item: Sum<S>,
S: Send { ... }
}Expand description
Infallible parallel iterator.
Par is the central trait for describing parallel computations as iterator
pipelines. It mirrors common sequential iterator operations (map,
filter, flat_map, collect, reduce, …) while allowing runtime
configuration of execution details such as number of threads, chunk size,
iteration order, and runner/pool selection.
Related traits:
ParUsefor worker-local mutable state,ParOptionforOption-based fallibility,ParResultforResult-based fallibility.
§Examples
use orx_parallel::*;
let sum_of_even_squares: usize = (1..11)
.into_par()
.map(|x| x * x)
.filter(|x| x % 2 == 0)
.sum();
assert_eq!(sum_of_even_squares, 220);Required Methods§
Sourcefn runner<Q: ParRunner>(
self,
runner: Q,
) -> impl Par<Item = Self::Item, Xap = Self::Xap, Input = Self::Input>
fn runner<Q: ParRunner>( self, runner: Q, ) -> impl Par<Item = Self::Item, Xap = Self::Xap, Input = Self::Input>
Replaces the current parallel runner with runner.
This allows per-computation control over execution strategy.
Please see Runner for parallel runners implemented in this crate.
§Examples
use orx_parallel::*;
let baseline: usize = (0..1000).into_par().sum();
let par = (0..1000).par();
let par = par.runner(Runner::fixed());
let configured: usize = par.sum();
assert_eq!(baseline, configured);Sourcefn runner_with_diagnostics(
self,
) -> impl Par<Item = Self::Item, Xap = Self::Xap, Input = Self::Input>
fn runner_with_diagnostics( self, ) -> impl Par<Item = Self::Item, Xap = Self::Xap, Input = Self::Input>
Wraps the current parallel runner with a diagnostics-enabled runner.
The returned iterator behaves the same, but additionally reports runtime diagnostics at the end of the computation.
§Examples
use orx_parallel::*;
let par = (1..10_001).par().num_threads(4);
#[cfg(feature = "std")]
let par = par.runner_with_diagnostics();
let sum = par.sum();
assert_eq!(sum, 50005000);This will print a summary report which currently looks like the following:
│ # Parallel Executor Diagnostics
│
│ Available threads : 4
│ Used threads : 4
│ Wall time : 1.15 ms
│
│ ## Summary Table
│ thread num_chunks num_tasks min_chunk avg_chunk max_chunk util%
│ ------ ---------- ---------- --------- --------- --------- -------
│ 0 35 27335 781 781 781 100.0%
│ 1 32 24992 781 781 781 91.5%
│ 2 30 23430 781 781 781 85.9%
│ 3 28 21868 781 781 781 77.8%
│
│ ## Workload Balance
│ max/min task ratio : 1.25x (1.00 = perfect balance)
│ coeff. of variation : 8.3% (lower is better)
│
│ ## Thread Active Timeline (each block ≈ 0.02 ms)
│ [ 0] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
│ [ 1] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
│ [ 2] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
│ [ 3] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
│
│ ## Thread Task Distribution (bar length ∝ tasks processed)
│ [ 0] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ (27335)
│ [ 1] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ (24992)
│ [ 2] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ (23430)
│ [ 3] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ (21868)Sourcefn num_threads(self, num_threads: impl Into<NumThreads>) -> Self
fn num_threads(self, num_threads: impl Into<NumThreads>) -> Self
Sets the maximum number of worker threads for this computation.
This method configures the computation layer of the thread count decision. The actual number of threads used is determined by combining:
- Pool constraint (from
pool()method or default pool)- Already includes
ORX_NUM_THREADSenvironment variable constraint
- Already includes
- Computation constraint (this method)
- Your per-computation thread preference
- Input size constraint
- Cannot spawn more threads than input elements
The actual thread count is the minimum of all these constraints.
§Parameter Interpretation
Integer values map as follows:
0=>NumThreads::Auto(use all available threads, spawn only as needed)n > 0=>NumThreads::Max(n)(cap atnthreads)
§Thread Count Decision Logic
available = pool.max_num_threads() // Pool maximum (includes env variable)
requested = match num_threads {
0 | Auto => input_size.max(1), // Limited by input size
Max(n) => min(input_size, n), // Limited by input size and this param
};
actual_threads = min(requested, available)§Examples
use orx_parallel::*;
// Sequential execution
let sum: usize = (1..11).into_par().num_threads(1).sum();
assert_eq!(sum, 55);
// Cap at 4 threads
let sum: usize = (1..1001).into_par().num_threads(4).sum();
// Auto: uses available threads (respects ORX_NUM_THREADS)
let sum: usize = (1..11).into_par().num_threads(0).sum();§See Also
NumThreads- Type for thread configurationthread_usage.md- Complete threading guide
Sourcefn chunk_size(self, chunk_size: impl Into<ChunkSize>) -> Self
fn chunk_size(self, chunk_size: impl Into<ChunkSize>) -> Self
Sets chunk size used when pulling items from the concurrent input.
Integer values map as follows:
0=> automatic (default)n > 0=> exact chunk sizen
§Examples
use orx_parallel::*;
let values: Vec<_> = (0..32)
.into_par()
.chunk_size(8)
.map(|x| x + 1)
.collect();
assert_eq!(values.len(), 32);
assert_eq!(values[0], 1);
assert_eq!(values[31], 32);§Rules of Thumb
- Automatic chunk size (default) is efficient in general. Parallel runner aims to find best chunk sizes to balance between minimizing parallelization overhead and maximizing resource utilization.
- While tuning a specific computation, we aim to find the smallest chunk size that is large enough to mitigate the impact of parallelization overhead.
- If the individual tasks are large enough, parallelization overhead becomes insignificant making
chunk_size = 1the optimal choice.
Sourcefn iteration_order(self, collect: IterationOrder) -> Self
fn iteration_order(self, collect: IterationOrder) -> Self
Sets iteration order semantics for operations sensitive to ordering.
Ordered (default) preserves positional meaning (for example, first returns the
earliest matching element in input order). Arbitrary allows any matching
element that is reached first in parallel execution.
§Examples
use orx_parallel::*;
let ordered = (1..10_000)
.into_par()
.iteration_order(IterationOrder::Ordered)
.find(|x| x % 3421 == 0);
assert_eq!(ordered, Some(3421));
let any = (1..10_000)
.into_par()
.iteration_order(IterationOrder::Arbitrary)
.find(|x| x % 3421 == 0)
.unwrap();
assert!([3421, 6842].contains(&any));Sourcefn map<Q, H>(
self,
h: H,
) -> impl Par<Item = Q, Xap = MapOf<Self::Xap, Q, H>, Input = Self::Input>
fn map<Q, H>( self, h: H, ) -> impl Par<Item = Q, Xap = MapOf<Self::Xap, Q, H>, Input = Self::Input>
Maps each element with closure h.
§Examples
use orx_parallel::*;
let doubled: Vec<_> = (1..4).into_par().map(|x| 2 * x).collect();
assert_eq!(doubled, vec![2, 4, 6]);Sourcefn inspect<H>(
self,
h: H,
) -> impl Par<Item = Self::Item, Xap = InsOf<Self::Xap, H>, Input = Self::Input>
fn inspect<H>( self, h: H, ) -> impl Par<Item = Self::Item, Xap = InsOf<Self::Xap, H>, Input = Self::Input>
Runs h on each element and forwards the item unchanged.
Useful for logging or debugging pipelines.
§Examples
use orx_parallel::*;
let out: Vec<_> = (1..5)
.into_par()
.inspect(|x| {
println!("observed {x}");
})
.collect();
assert_eq!(out, vec![1, 2, 3, 4]);Sourcefn filter<H>(
self,
h: H,
) -> impl Par<Item = Self::Item, Xap = FilOf<Self::Xap, H>, Input = Self::Input>
fn filter<H>( self, h: H, ) -> impl Par<Item = Self::Item, Xap = FilOf<Self::Xap, H>, Input = Self::Input>
Keeps only elements satisfying predicate h.
§Examples
use orx_parallel::*;
let odds: Vec<_> = (1..7).into_par().filter(|x| x % 2 == 1).collect();
assert_eq!(odds, vec![1, 3, 5]);Sourcefn filter_map<Q, H>(
self,
h: H,
) -> impl Par<Item = Q, Xap = FilMapOf<Self::Xap, Q, H>, Input = Self::Input>
fn filter_map<Q, H>( self, h: H, ) -> impl Par<Item = Q, Xap = FilMapOf<Self::Xap, Q, H>, Input = Self::Input>
Maps and filters in a single pass.
Returns mapped values for elements where h returns Some(_).
§Examples
use orx_parallel::*;
let numbers: Vec<_> = ["1", "x", "5"]
.into_par()
.filter_map(|s| s.parse::<usize>().ok())
.collect();
assert_eq!(numbers, vec![1, 5]);Sourcefn flat_map<V, H>(
self,
h: H,
) -> impl Par<Item = V::Item, Xap = FlatMapOf<Self::Xap, V, H>, Input = Self::Input>
fn flat_map<V, H>( self, h: H, ) -> impl Par<Item = V::Item, Xap = FlatMapOf<Self::Xap, V, H>, Input = Self::Input>
Maps each element to an iterator and flattens one level.
§Examples
use orx_parallel::*;
let out: Vec<_> = (1..4).into_par().flat_map(|x| [x, x + 10]).collect();
assert_eq!(out, vec![1, 11, 2, 12, 3, 13]);Sourcefn flatten(
self,
) -> impl Par<Item = <Self::Item as IntoIterator>::Item, Xap = FlattenOf<Self::Xap>, Input = Self::Input>where
Self::Item: IntoIterator,
fn flatten(
self,
) -> impl Par<Item = <Self::Item as IntoIterator>::Item, Xap = FlattenOf<Self::Xap>, Input = Self::Input>where
Self::Item: IntoIterator,
Flattens one level of nested iterables.
§Examples
use orx_parallel::*;
let nested = vec![vec![1, 2], vec![3, 4]];
let flat: Vec<_> = nested.into_par().flatten().collect();
assert_eq!(flat, vec![1, 2, 3, 4]);Sourcefn size_hint(&self) -> (usize, Option<usize>)
fn size_hint(&self) -> (usize, Option<usize>)
Returns a lower and optional upper bound on the number of output items.
The bounds follow the usual Iterator::size_hint convention. For an
exact-size input and a one-to-one transformation, both bounds are exact.
Transformations such as filter may reduce the lower bound while keeping
the input length as the upper bound.
§Examples
use orx_parallel::*;
let mapped = (0..4).into_par().map(|x| x * 2);
assert_eq!(mapped.size_hint(), (4, Some(4)));
let filtered = (0..4).into_par().filter(|x| x % 2 == 0);
assert_eq!(filtered.size_hint(), (0, Some(4)));Sourcefn first(self) -> Option<Self::Item>
fn first(self) -> Option<Self::Item>
Returns the first item according to iteration order, or None if empty.
With IterationOrder::Ordered (default), this is the earliest matching item by
input position. With IterationOrder::Arbitrary, this may be any
matching item reached first in parallel execution.
This operation is short-circuiting: once a first candidate is determined, remaining work is cancelled.
§Examples
use orx_parallel::*;
assert_eq!(Vec::<usize>::new().into_par().first(), None);
assert_eq!((1..4).into_par().first(), Some(1));Provided Methods§
Sourcefn into_optional<T>(
self,
) -> impl ParOption<Elem = T, Xap1 = Self::Xap, M = T, Xap2 = Id<T>, Input = Self::Input, Size = <<Self::Xap as Xap>::Size as Size>::IntoPair>
fn into_optional<T>( self, ) -> impl ParOption<Elem = T, Xap1 = Self::Xap, M = T, Xap2 = Id<T>, Input = Self::Input, Size = <<Self::Xap as Xap>::Size as Size>::IntoPair>
Converts Par<Item = Option<T>> into ParOption<Item = T>.
The resulting fallible iterator short-circuits to None if any element is None.
Similar to pattern using the ? operator, fallible iterators allow us to work with
the success path.
§Examples
use orx_parallel::*;
let ok: Option<Vec<_>> = ["1", "2", "3"]
.into_par()
.map(|s| s.parse::<i32>().ok())
.into_optional()
.map(|x| x * 2)
.filter(|x| *x > 3)
.collect();
assert_eq!(ok, Some(vec![4, 6]));
let fail: Option<Vec<_>> = ["1", "x", "3"]
.into_par()
.map(|s| s.parse::<i32>().ok())
.into_optional()
.map(|x| x * 2)
.filter(|x| *x > 3)
.collect();
assert_eq!(fail, None);Notice that x is of type i32, rather than Option<i32>, which allows for concise
expressions.
Without fallible iterators, the above result could be obtained by the following version, which is not only more verbose, but also lacks the short-circuiting mechanism.
use orx_parallel::*;
let ok: Option<Vec<_>> = ["1", "2", "3"]
.into_par()
.map(|s| s.parse::<i32>().ok())
.map(|x| x.map(|x| x * 2))
.filter(|x| x.as_ref().map(|x| *x > 3).unwrap_or(true))
.collect::<Vec<_>>()
.into_iter()
.collect();
assert_eq!(ok, Some(vec![4, 6]));Sourcefn into_fallible<T, E>(
self,
) -> impl ParResult<Elem = T, Error = E, Xap1 = Self::Xap, M = T, Xap2 = Id<T>, Input = Self::Input, Size = <<Self::Xap as Xap>::Size as Size>::IntoPair>
fn into_fallible<T, E>( self, ) -> impl ParResult<Elem = T, Error = E, Xap1 = Self::Xap, M = T, Xap2 = Id<T>, Input = Self::Input, Size = <<Self::Xap as Xap>::Size as Size>::IntoPair>
Converts Par<Item = Result<T, E>> into ParResult<Item = T, Error = E>.
The resulting fallible iterator short-circuits and returns the first observed error.
Similar to pattern using the ? operator, fallible iterators allow us to
work with the success path.
§Examples
use orx_parallel::*;
let ok: Result<Vec<_>, _> = ["1", "2", "3"]
.into_par()
.map(|s| s.parse::<i32>())
.into_fallible()
.map(|x| x * 2)
.filter(|x| *x > 3)
.collect();
assert_eq!(ok, Ok(vec![4, 6]));
let fail: Result<Vec<_>, _> = ["1", "x", "3"]
.into_par()
.map(|s| s.parse::<i32>())
.into_fallible()
.map(|x| x * 2)
.filter(|x| *x > 3)
.collect();
assert!(fail.is_err());Notice that x is of type i32, rather than Result<i32, _>, which
allows for concise expressions.
Without fallible iterators, the above result could be obtained by the following version, which is not only more verbose, but also lacks the short-circuiting mechanism.
use orx_parallel::*;
let ok: Result<Vec<_>, _> = ["1", "2", "3"]
.into_par()
.map(|s| s.parse::<i32>())
.map(|x| x.map(|x| x * 2))
.filter(|x| x.as_ref().map(|x| *x > 3).unwrap_or(true))
.collect::<Vec<_>>()
.into_iter()
.collect();
assert_eq!(ok, Ok(vec![4, 6]));Sourcefn use_new<U, F>(
self,
f: F,
) -> impl ParUse<Item = Self::Item, Use = U, Xap = IdUse<Self::Xap, U>, Input = Self::Input>
fn use_new<U, F>( self, f: F, ) -> impl ParUse<Item = Self::Item, Use = U, Xap = IdUse<Self::Xap, U>, Input = Self::Input>
Creates one mutable Use value per participating worker.
The initializer f is called with the worker’s thread index, and the
returned value is then passed as &mut Use to downstream ParUse
operations such as map, filter, flat_map, reduce, and for_each.
This is useful for thread-local scratch buffers, counters, or other mutable state that should not be shared across workers.
§Examples
Reusing one buffer per worker avoids allocating a fresh String for every parsed item.
use orx_parallel::*;
let values: Vec<_> = (1..4)
.into_par()
.num_threads(1)
.use_new(|_| String::new())
.map(|buffer, x| {
buffer.clear();
buffer.push_str(&x.to_string());
buffer.parse::<usize>().unwrap() * 10
})
.collect();
assert_eq!(values, vec![10, 20, 30]);Some pipelines need fast worker-local randomness, for example for sampling, randomized search, or simulation. Seeding one RNG per worker with thread_idx creates independent thread-local random streams without shared mutable state.
use orx_parallel::*;
use rand::{Rng, RngExt, SeedableRng};
use rand_chacha::ChaCha8Rng;
let values: Vec<_> = (0..8)
.into_par()
.num_threads(2)
.use_new(|thread_idx| ChaCha8Rng::seed_from_u64(thread_idx as u64 + 1))
.map(|rng, _| rng.random_range(0..100usize))
.collect();
assert_eq!(values.len(), 8);
assert!(values.into_iter().all(|x| x < 100));Sourcefn use_vec<U, F>(
self,
use_vec: &mut UseVec<U, F>,
) -> impl ParUse<Item = Self::Item, Use = U, Xap = IdUse<Self::Xap, U>, Input = Self::Input>
fn use_vec<U, F>( self, use_vec: &mut UseVec<U, F>, ) -> impl ParUse<Item = Self::Item, Use = U, Xap = IdUse<Self::Xap, U>, Input = Self::Input>
Uses an externally owned UseVec as worker-local mutable state.
Unlike Par::use_new, the state container is provided by the caller,
which allows reading back per-worker values after the computation.
This is practical when we need thread-local accumulation with a final merge step, such as per-thread partial sums or local metrics.
Note that the resulting UseVec length equals the number of worker
threads that actually participated in the computation. Exactly one
element is created per participating thread.
§Examples
use orx_parallel::*;
let n = 10_000usize;
let mut use_vec = UseVec::new(|_| 0usize);
(0..n)
.into_par()
.map(|x| 2 * x)
.use_vec(&mut use_vec)
.for_each(|thread_sum, x| *thread_sum += x);
let partial_sums = use_vec.into_vec();
let total: usize = partial_sums.into_iter().sum();
assert_eq!(total, (n - 1) * n);The following example demonstrates an expensive per-thread state: a pre-allocated scratch buffer.
use core::fmt::Write;
use core::sync::atomic::{AtomicUsize, Ordering};
use orx_parallel::*;
let created = AtomicUsize::new(0);
let mut use_vec = UseVec::new(|_| {
created.fetch_add(1, Ordering::Relaxed);
String::with_capacity(4096)
});
let out: Vec<_> = (0..64)
.into_par()
.num_threads(4)
.use_vec(&mut use_vec)
.map(|buffer, x| {
buffer.clear();
write!(buffer, "{x}").unwrap();
buffer.parse::<usize>().unwrap()
})
.collect();
assert_eq!(out, (0..64).collect::<Vec<_>>());
let buffers = use_vec.into_vec();
assert_eq!(created.load(Ordering::Relaxed), buffers.len());
assert!(buffers.len() <= 4);Sourcefn use_slice<'a, U>(
self,
slice: &'a mut [U],
) -> impl ParUse<Item = Self::Item, Use = U, Xap = IdUse<Self::Xap, U>, Input = Self::Input>where
U: Send + 'a,
fn use_slice<'a, U>(
self,
slice: &'a mut [U],
) -> impl ParUse<Item = Self::Item, Use = U, Xap = IdUse<Self::Xap, U>, Input = Self::Input>where
U: Send + 'a,
Uses a caller-provided mutable slice as worker-local mutable state.
This is similar to Par::use_vec, but the state storage is a
borrowed slice instead of an owned UseVec. Therefore, no per-thread
state objects are created by this method; existing slice elements are
reused as thread-local state.
The number of worker threads that can participate in the computation is
limited by slice.len().
§Examples
use orx_parallel::*;
let n = 10_000usize;
let mut thread_sums = vec![0usize; 4];
(0..n)
.into_par()
.map(|x| 2 * x)
.use_slice(&mut thread_sums) // participating workers are limited to 4
.for_each(|thread_sum, x| *thread_sum += x);
let total: usize = thread_sums.into_iter().sum();
assert_eq!(total, (n - 1) * n);§Panics
Panics if slice is empty.
Sourcefn copied<'a, O>(
self,
) -> impl Par<Item = O, Xap = MappedOf<Self::Xap, FnCopied<'a, O>>, Input = Self::Input>
fn copied<'a, O>( self, ) -> impl Par<Item = O, Xap = MappedOf<Self::Xap, FnCopied<'a, O>>, Input = Self::Input>
Copies elements of a reference iterator.
Equivalent to .map(|&x| x).
§Examples
use orx_parallel::*;
let data = vec![1, 2, 3];
let copied: Vec<_> = data.par().copied().collect();
assert_eq!(copied, vec![1, 2, 3]);Sourcefn cloned<'a, O>(
self,
) -> impl Par<Item = O, Xap = MappedOf<Self::Xap, FnCloned<'a, O>>, Input = Self::Input>
fn cloned<'a, O>( self, ) -> impl Par<Item = O, Xap = MappedOf<Self::Xap, FnCloned<'a, O>>, Input = Self::Input>
Clones elements of a reference iterator.
Equivalent to .map(|x| x.clone()).
§Examples
use orx_parallel::*;
let data = vec!["a".to_string(), "b".to_string()];
let cloned: Vec<_> = data.par().cloned().collect();
assert_eq!(cloned, vec!["a".to_string(), "b".to_string()]);Sourcefn len(&self) -> usizewhere
Self::Input: ExactSizeConcurrentIter,
Self::Xap: Xap<Size = One>,
fn len(&self) -> usizewhere
Self::Input: ExactSizeConcurrentIter,
Self::Xap: Xap<Size = One>,
Returns the exact number of output items.
§Examples
use orx_parallel::*;
assert_eq!((0..10).into_par().len(), 10);
assert_eq!((0..10).into_par().map(|x| x + 2).len(), 10);Sourcefn is_empty(&self) -> boolwhere
Self::Input: ExactSizeConcurrentIter,
Self::Xap: Xap<Size = One>,
fn is_empty(&self) -> boolwhere
Self::Input: ExactSizeConcurrentIter,
Self::Xap: Xap<Size = One>,
Returns true when the parallel iterator has no output items.
§Examples
use orx_parallel::*;
assert!((0..0).into_par().is_empty());
assert!(!(0..1).into_par().is_empty());Sourcefn collect<P>(self) -> P
fn collect<P>(self) -> P
Collects all items into a new collection.
§Examples
use orx_parallel::*;
let out: Vec<_> = (1..4).into_par().map(|x| x * 2).collect();
assert_eq!(out, vec![2, 4, 6]);Sourcefn all<F>(self, f: F) -> bool
fn all<F>(self, f: F) -> bool
Returns true if all items satisfy predicate f.
Empty iterators return true.
This operation is short-circuiting: evaluation stops as soon as one item fails the predicate.
§Examples
use orx_parallel::*;
assert!((1..5).into_par().all(|x| x > &0));
assert!(!(1..5).into_par().all(|x| x % 2 == 0));Sourcefn any<F>(self, f: F) -> bool
fn any<F>(self, f: F) -> bool
Returns true if any item satisfies predicate f.
Empty iterators return false.
This operation is short-circuiting: evaluation stops as soon as one item satisfies the predicate.
§Examples
use orx_parallel::*;
assert!((1..5).into_par().any(|x| x % 2 == 0));
assert!(!(1..5).into_par().any(|x| x > &10));Sourcefn count(self) -> usize
fn count(self) -> usize
Counts elements.
§Examples
use orx_parallel::*;
let n = (1..11).into_par().filter(|x| x % 3 == 0).count();
assert_eq!(n, 3);Sourcefn find<F>(self, f: F) -> Option<Self::Item>
fn find<F>(self, f: F) -> Option<Self::Item>
Finds first (Ordered, default) or any (Arbitrary) item satisfying predicate f.
This is equivalent to self.filter(f).first().
This operation is short-circuiting: once a matching item is found, remaining work is cancelled.
§Examples
use orx_parallel::*;
let found = (1..101).into_par().find(|x| x % 17 == 0);
assert_eq!(found, Some(17));Sourcefn fold<B, I, F>(self, init: I, f: F) -> Vec<B>
fn fold<B, I, F>(self, init: I, f: F) -> Vec<B>
Folds elements into per-thread accumulators and returns them.
The output contains one accumulator for each participating worker.
§Examples
use orx_parallel::*;
let num_threads = 2;
let partials: Vec<usize> = (1..6)
.into_par()
.num_threads(num_threads)
.fold(|| 0usize, |acc, x| *acc += x);
assert!(partials.len() <= num_threads);
assert_eq!(partials.iter().sum::<usize>(), 15);Sourcefn for_each<F>(self, f: F)
fn for_each<F>(self, f: F)
Executes f for each item.
§Examples
use core::sync::atomic::{AtomicUsize, Ordering};
use orx_parallel::*;
let total = AtomicUsize::new(0);
(1..5)
.into_par()
.for_each(|x| {
total.fetch_add(x, Ordering::Relaxed);
});
assert_eq!(total.load(Ordering::Relaxed), 10);Sourcefn max(self) -> Option<Self::Item>
fn max(self) -> Option<Self::Item>
Returns maximum element, or None if empty.
§Examples
use orx_parallel::*;
assert_eq!((1..5).into_par().max(), Some(4));
assert_eq!(Vec::<usize>::new().into_par().max(), None);Sourcefn max_by<F>(self, f: F) -> Option<Self::Item>
fn max_by<F>(self, f: F) -> Option<Self::Item>
Returns element considered maximum by comparator f.
§Examples
use orx_parallel::*;
let x = vec![-3_i32, 0, 1, 5, -10]
.into_par()
.max_by(|a, b| a.cmp(b));
assert_eq!(x, Some(5));Sourcefn max_by_key<B, F>(self, f: F) -> Option<Self::Item>
fn max_by_key<B, F>(self, f: F) -> Option<Self::Item>
Returns element with maximum key value.
§Examples
use orx_parallel::*;
let x = vec![-3_i32, 0, 1, 5, -10]
.into_par()
.max_by_key(|x| x.abs());
assert_eq!(x, Some(-10));Sourcefn min(self) -> Option<Self::Item>
fn min(self) -> Option<Self::Item>
Returns minimum element, or None if empty.
§Examples
use orx_parallel::*;
assert_eq!((1..5).into_par().min(), Some(1));
assert_eq!(Vec::<usize>::new().into_par().min(), None);Sourcefn min_by<F>(self, f: F) -> Option<Self::Item>
fn min_by<F>(self, f: F) -> Option<Self::Item>
Returns element considered minimum by comparator f.
§Examples
use orx_parallel::*;
let x = vec![-3_i32, 0, 1, 5, -10]
.into_par()
.min_by(|a, b| a.cmp(b));
assert_eq!(x, Some(-10));Sourcefn min_by_key<B, F>(self, f: F) -> Option<Self::Item>
fn min_by_key<B, F>(self, f: F) -> Option<Self::Item>
Returns element with minimum key value.
§Examples
use orx_parallel::*;
let x = vec![-3_i32, 0, 1, 5, -10]
.into_par()
.min_by_key(|x| x.abs());
assert_eq!(x, Some(0));Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".