Skip to main content

Par

Trait Par 

Source
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:

  • ParUse for worker-local mutable state,
  • ParOption for Option-based fallibility,
  • ParResult for Result-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§

Source

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);
Source

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)
Source

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:

  1. Pool constraint (from pool() method or default pool)
    • Already includes ORX_NUM_THREADS environment variable constraint
  2. Computation constraint (this method)
    • Your per-computation thread preference
  3. 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 at n threads)
§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
Source

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 size n
§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 = 1 the optimal choice.
Source

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));
Source

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,

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]);
Source

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,

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]);
Source

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,

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]);
Source

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,

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]);
Source

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,

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]);
Source

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]);
Source

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)));
Source

fn first(self) -> Option<Self::Item>
where Self::Item: Send,

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));
Source

fn reduce<F>(self, f: F) -> Option<Self::Item>
where F: Fn(Self::Item, Self::Item) -> Self::Item + Send + Copy, Self::Item: Send,

Reduces items into one value using associative reducer f.

Returns None for an empty iterator.

§Examples
use orx_parallel::*;

let reduced = (1..6).into_par().reduce(|a, b| a + b);
assert_eq!(reduced, Some(15));
Source

fn collect_into<P>(self, dst: &mut P)
where P: ParExtend<Self::Item>, Self::Item: Send,

Collects all items into dst.

§Examples
use orx_parallel::*;

let mut dst = vec![10];
(0..3).into_par().collect_into(&mut dst);
assert_eq!(dst, vec![10, 0, 1, 2]);

Provided Methods§

Source

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>>,

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]));
Source

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>>,

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]));
Source

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,

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));
Source

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,

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);
Source

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.

Source

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,

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]);
Source

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,

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()]);
Source

fn len(&self) -> usize
where 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);
Source

fn is_empty(&self) -> bool
where 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());
Source

fn collect<P>(self) -> P
where P: ParExtend<Self::Item> + Default, Self::Item: Send,

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]);
Source

fn all<F>(self, f: F) -> bool
where F: Fn(&Self::Item) -> bool + Sync,

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));
Source

fn any<F>(self, f: F) -> bool
where F: Fn(&Self::Item) -> bool + Sync,

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));
Source

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);
Source

fn find<F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send, F: Fn(&Self::Item) -> bool + Sync,

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));
Source

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,

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);
Source

fn for_each<F>(self, f: F)
where F: Fn(Self::Item) + Send + Copy,

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);
Source

fn max(self) -> Option<Self::Item>
where Self::Item: Ord + Send,

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);
Source

fn max_by<F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send, F: Fn(&Self::Item, &Self::Item) -> Ordering + Sync,

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));
Source

fn max_by_key<B, F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send, B: Ord, F: Fn(&Self::Item) -> B + Sync,

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));
Source

fn min(self) -> Option<Self::Item>
where Self::Item: Ord + Send,

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);
Source

fn min_by<F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send, F: Fn(&Self::Item, &Self::Item) -> Ordering + Sync,

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));
Source

fn min_by_key<B, F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send, B: Ord, F: Fn(&Self::Item) -> B + Sync,

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));
Source

fn sum<S>(self) -> S
where Self::Item: Sum<S>, S: Send,

Sums elements using Sum implementation of the item type.

Empty iterators return additive identity (zero).

§Examples
use orx_parallel::*;

let sum: usize = (1..5).into_par().sum();
assert_eq!(sum, 10);

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§

Source§

impl<I, X, R> Par for ParIter<I, X, R>
where I: ConcurrentIter, X: Xap<I = I::Item>, R: ParRunner,