Skip to main content

Strategy

Trait Strategy 

Source
pub trait Strategy:
    Clone
    + Send
    + Sync
    + Debug
    + 'static {
Show 18 methods // Required methods fn spawn<F, T>( &self, len: usize, f: F, ) -> impl Future<Output = T> + Send + 'static where F: FnOnce(Self) -> T + Send + 'static, T: Send + 'static; fn run<R, SEQ, PAR>(&self, len: usize, serial: SEQ, parallel: PAR) -> R where R: Send, SEQ: FnOnce() -> R + Send, PAR: FnOnce() -> R + Send; fn try_run<R, E, SEQ, PAR>( &self, len: usize, serial: SEQ, parallel: PAR, ) -> Result<R, E> where R: Send, E: Send, SEQ: FnOnce() -> Result<R, E> + Send, PAR: FnOnce() -> Result<R, E> + Send; fn fold_init<I, INIT, T, R, ID, F, RD>( &self, iter: I, init: INIT, identity: ID, fold_op: F, reduce_op: RD, ) -> R where I: IntoIterator<IntoIter: Send, Item: Send> + Send, INIT: Fn() -> T + Send + Sync, T: Send, R: Send, ID: Fn() -> R + Send + Sync, F: Fn(R, &mut T, I::Item) -> R + Send + Sync, RD: Fn(R, R) -> R + Send + Sync; fn try_fold<I, R, E, ID, F, RD>( &self, iter: I, identity: ID, fold_op: F, reduce_op: RD, ) -> Result<R, E> where I: IntoIterator<IntoIter: Send, Item: Send> + Send, R: Send, E: Send, ID: Fn() -> R + Send + Sync, F: Fn(R, I::Item) -> Result<R, E> + Send + Sync, RD: Fn(R, R) -> R + Send + Sync; fn join<A, B, RA, RB>(&self, a: A, b: B) -> (RA, RB) where A: FnOnce() -> RA + Send, B: FnOnce() -> RB + Send, RA: Send, RB: Send; fn sort_by<T, C>(&self, items: &mut [T], compare: C) where T: Send, C: Fn(&T, &T) -> Ordering + Send + Sync; // Provided methods fn run_batches<R, F>( &self, len: usize, minimum_batch_len: NonZeroUsize, multiplier: usize, run: F, ) -> R where R: Send, F: for<'scope> FnOnce(Batches<'scope, Self>) -> R + Send { ... } fn try_run_batches<R, E, F>( &self, len: usize, _minimum_batch_len: NonZeroUsize, _multiplier: usize, run: F, ) -> Result<R, E> where R: Send, E: Send, F: for<'scope> FnOnce(Batches<'scope, Self>) -> Result<R, E> + Send { ... } fn run_tiles<R, F>( &self, len: usize, grids: &[Grid], _multiplier: usize, run: F, ) -> R where R: Send, F: for<'scope> FnOnce(Tiles<'scope, Self>) -> R + Send { ... } fn fold<I, R, ID, F, RD>( &self, iter: I, identity: ID, fold_op: F, reduce_op: RD, ) -> R where I: IntoIterator<IntoIter: Send, Item: Send> + Send, R: Send, ID: Fn() -> R + Send + Sync, F: Fn(R, I::Item) -> R + Send + Sync, RD: Fn(R, R) -> R + Send + Sync { ... } fn map_collect_vec<I, F, T>(&self, iter: I, map_op: F) -> Vec<T> where I: IntoIterator<IntoIter: Send, Item: Send> + Send, F: Fn(I::Item) -> T + Send + Sync, T: Send { ... } fn try_map_collect_vec<I, F, T, E>( &self, iter: I, map_op: F, ) -> Result<Vec<T>, E> where I: IntoIterator<IntoIter: Send, Item: Send> + Send, F: Fn(I::Item) -> Result<T, E> + Send + Sync, T: Send, E: Send { ... } fn map_init_collect_vec<I, INIT, T, F, R>( &self, iter: I, init: INIT, map_op: F, ) -> Vec<R> where I: IntoIterator<IntoIter: Send, Item: Send> + Send, INIT: Fn() -> T + Send + Sync, T: Send, F: Fn(&mut T, I::Item) -> R + Send + Sync, R: Send { ... } fn map_init_collect_vec_with_multiplier<I, INIT, T, F, R>( &self, iter: I, _multiplier: usize, init: INIT, map_op: F, ) -> Vec<R> where I: IntoIterator<IntoIter: Send, Item: Send> + Send, INIT: Fn() -> T + Send + Sync, T: Send, F: Fn(&mut T, I::Item) -> R + Send + Sync, R: Send { ... } fn map_collect_vec_with_multiplier<I, F, R>( &self, iter: I, multiplier: usize, map_op: F, ) -> Vec<R> where I: IntoIterator<IntoIter: Send, Item: Send> + Send, F: Fn(I::Item) -> R + Send + Sync, R: Send { ... } fn map_partition_collect_vec<I, F, K, U>( &self, iter: I, map_op: F, ) -> (Vec<U>, Vec<K>) where I: IntoIterator<IntoIter: Send, Item: Send> + Send, F: Fn(I::Item) -> (K, Option<U>) + Send + Sync, K: Send, U: Send { ... } fn sort_unstable_by<T, C>(&self, items: &mut [T], compare: C) where T: Send, C: Fn(&T, &T) -> Ordering + Send + Sync { ... }
}
Expand description

A strategy for executing fold operations.

This trait abstracts over sequential and parallel execution, allowing algorithms to be written generically and then executed with different strategies depending on the use case (e.g., sequential for testing/debugging, parallel for production).

Required Methods§

Source

fn spawn<F, T>( &self, len: usize, f: F, ) -> impl Future<Output = T> + Send + 'static
where F: FnOnce(Self) -> T + Send + 'static, T: Send + 'static,

Submit one CPU-bound job to this strategy, running it inline on the calling task when it is measured cheaper than the round trip of offloading it to the pool.

len groups calls at a call site into size classes for those measurements, so similar len must mean comparable cost. An inline job runs to completion before spawn returns, and jobs whose measured cost exceeds a small time budget offload.

The returned future resolves when the job completes. Blocking on external synchronization or I/O inside the job can occupy execution capacity until it returns. When the polling thread itself belongs to the strategy’s execution resources (e.g. a runtime whose executor thread is registered as a pool worker), the job (and other pending work) may be executed inline on that thread rather than waited on.

If the job panics, the panic is propagated to the caller; it never aborts the process.

Source

fn run<R, SEQ, PAR>(&self, len: usize, serial: SEQ, parallel: PAR) -> R
where R: Send, SEQ: FnOnce() -> R + Send, PAR: FnOnce() -> R + Send,

Runs either a serial or parallel body.

Source

fn try_run<R, E, SEQ, PAR>( &self, len: usize, serial: SEQ, parallel: PAR, ) -> Result<R, E>
where R: Send, E: Send, SEQ: FnOnce() -> Result<R, E> + Send, PAR: FnOnce() -> Result<R, E> + Send,

Like run, but for fallible work.

The strategy chooses and runs either the serial or parallel body, returning the first error produced by the chosen body. Elapsed time is only recorded on success, so abort-early error paths cannot poison the adaptive policy’s estimates.

Source

fn fold_init<I, INIT, T, R, ID, F, RD>( &self, iter: I, init: INIT, identity: ID, fold_op: F, reduce_op: RD, ) -> R
where I: IntoIterator<IntoIter: Send, Item: Send> + Send, INIT: Fn() -> T + Send + Sync, T: Send, R: Send, ID: Fn() -> R + Send + Sync, F: Fn(R, &mut T, I::Item) -> R + Send + Sync, RD: Fn(R, R) -> R + Send + Sync,

Reduces a collection to a single value with per-partition initialization.

Similar to fold, but provides a separate initialization value that is created once per partition. This is useful when the fold operation requires mutable state that should not be shared across partitions (e.g., a scratch buffer, RNG, or expensive-to-clone resource).

§Arguments
  • iter: The collection to fold over
  • init: Creates the per-partition initialization value
  • identity: Creates the identity value for the accumulator
  • fold_op: Combines accumulator with init state and item: (acc, &mut init, item) -> acc
  • reduce_op: Combines two accumulators: (acc1, acc2) -> acc
§Examples
use commonware_parallel::{Strategy, Sequential};

let strategy = Sequential;
let data = vec![1u32, 2, 3, 4, 5];

// Use a scratch buffer to avoid allocations in the inner loop
let result: Vec<String> = strategy.fold_init(
    &data,
    || String::with_capacity(16),  // Per-partition scratch buffer
    Vec::new,                       // Identity for accumulator
    |mut acc, buf, &n| {
        buf.clear();
        use std::fmt::Write;
        write!(buf, "num:{}", n).unwrap();
        acc.push(buf.clone());
        acc
    },
    |mut a, b| { a.extend(b); a },
);

assert_eq!(result, vec!["num:1", "num:2", "num:3", "num:4", "num:5"]);
Source

fn try_fold<I, R, E, ID, F, RD>( &self, iter: I, identity: ID, fold_op: F, reduce_op: RD, ) -> Result<R, E>
where I: IntoIterator<IntoIter: Send, Item: Send> + Send, R: Send, E: Send, ID: Fn() -> R + Send + Sync, F: Fn(R, I::Item) -> Result<R, E> + Send + Sync, RD: Fn(R, R) -> R + Send + Sync,

Reduces a collection to a single value using a fallible fold operation.

Similar to fold, but fold_op may fail. Implementations may stop applying fold_op after an error is observed. When more than one partition fails, any error may be returned.

Adaptive strategies must only record elapsed time when the fold succeeds, so abort-early error paths cannot poison the policy’s estimates.

§Arguments
  • iter: The collection to fold over
  • identity: A closure that produces the identity value for the fold.
  • fold_op: Fallibly combines an accumulator with a single item: (acc, item) -> Result<acc, E>
  • reduce_op: Combines two successful accumulators: (acc1, acc2) -> acc.
Source

fn join<A, B, RA, RB>(&self, a: A, b: B) -> (RA, RB)
where A: FnOnce() -> RA + Send, B: FnOnce() -> RB + Send, RA: Send, RB: Send,

Executes two closures, potentially in parallel, and returns both results.

For Sequential, this executes a then b on the current thread. For Rayon, this executes a and b on its thread pool.

§Arguments
  • a: First closure to execute
  • b: Second closure to execute
§Examples
use commonware_parallel::{Strategy, Sequential};

let strategy = Sequential;

let (sum, product) = strategy.join(
    || (1..=5).sum::<i32>(),
    || (1..=5).product::<i32>(),
);

assert_eq!(sum, 15);
assert_eq!(product, 120);
Source

fn sort_by<T, C>(&self, items: &mut [T], compare: C)
where T: Send, C: Fn(&T, &T) -> Ordering + Send + Sync,

Sorts a slice with a comparator, preserving the order of equal elements.

§Examples
use commonware_parallel::{Strategy, Sequential};

let strategy = Sequential;
let mut data = vec![3, 1, 2];
strategy.sort_by(&mut data, |a, b| a.cmp(b));
assert_eq!(data, vec![1, 2, 3]);

Provided Methods§

Source

fn run_batches<R, F>( &self, len: usize, minimum_batch_len: NonZeroUsize, multiplier: usize, run: F, ) -> R
where R: Send, F: for<'scope> FnOnce(Batches<'scope, Self>) -> R + Send,

Run an operation on strategy-provided batches.

run is called once with batches covering 0..len. The strategy either supplies one batch that executes on the calling thread or splits the input into two or more batches no shorter than minimum_batch_len that may execute in parallel. An input too short to split always gets one batch.

multiplier estimates work per input unit. Complete the operation, including preparation and result assembly, inside run. Both execution shapes must produce equivalent results. The default implementation runs the whole input.

§Examples
use commonware_parallel::{Sequential, Strategy};
use core::num::NonZeroUsize;

let values = [1u64, 2, 3, 4];
let total = Sequential.run_batches(values.len(), NonZeroUsize::MIN, 1, |batches| {
    batches
        .map_collect_vec(
            |ranges| ranges.into_iter().map(|range| &values[range]).collect::<Vec<_>>(),
            |batch| batch.iter().sum::<u64>(),
        )
        .into_iter()
        .sum::<u64>()
});
assert_eq!(total, 10);
Source

fn try_run_batches<R, E, F>( &self, len: usize, _minimum_batch_len: NonZeroUsize, _multiplier: usize, run: F, ) -> Result<R, E>
where R: Send, E: Send, F: for<'scope> FnOnce(Batches<'scope, Self>) -> Result<R, E> + Send,

Like run_batches, but for fallible work.

Adaptive strategies record elapsed time only when the complete operation succeeds.

Source

fn run_tiles<R, F>( &self, len: usize, grids: &[Grid], _multiplier: usize, run: F, ) -> R
where R: Send, F: for<'scope> FnOnce(Tiles<'scope, Self>) -> R + Send,

Run an operation on strategy-provided tiles over len columns.

grids are the ways the operation can cut its input. Each Grid has some number of rows that pass over the columns and a fixed cost of finishing a tile. The strategy cuts the grid it estimates its workers will finish first, and Tiles::grid names that grid. run is called once with tiles that cover every (row, column) cell of that grid exactly once, so a grid without cells has no tiles. Each tile is one row over a contiguous range of columns, supplied in pieces and then finished once.

This suits several independent passes over the same input where finishing a tile has a fixed cost, such as reducing per-tile state, so cutting a pass is costly. Several grids let an operation trade passes against that cost, as when a wider digit window makes fewer passes with more buckets to reduce.

A serial run cuts the grid with the fewest cells, counting each row’s finish, and makes every row one tile. A parallel run shares a grid among the strategy’s workers as whole rows or as shares of cells that cut rows, and Tiles describes how it chooses and how workers claim.

When rows differ in cost, order them so that consecutive rows mix cheap and expensive ones. rows * len must not overflow usize for any grid, and grids must not be empty.

multiplier estimates work per cell. Complete the operation, including result assembly, inside run. Both execution shapes must produce equivalent results. The default implementation makes every row of the cheapest serial grid one tile on the calling thread.

§Panics

Panics if grids is empty, or if a parallel run cuts a grid whose rows * len overflows usize.

§Examples
use commonware_parallel::{Grid, Sequential, Strategy};
use core::num::NonZeroUsize;

let rows = [[1u64, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]];
let grid = Grid {
    rows: rows.len(),
    tile_cost: NonZeroUsize::MIN,
};
let sums = Sequential.run_tiles(4, &[grid], 1, |tiles| {
    let partials = tiles.fill_collect_vec(
        || 0u64,
        |sum, row, columns| *sum += rows[row][columns].iter().sum::<u64>(),
        |sum, row| (row, core::mem::take(sum)),
    );
    let mut sums = [0u64; 3];
    for (row, partial) in partials {
        sums[row] += partial;
    }
    sums
});
assert_eq!(sums, [10, 26, 42]);
Source

fn fold<I, R, ID, F, RD>( &self, iter: I, identity: ID, fold_op: F, reduce_op: RD, ) -> R
where I: IntoIterator<IntoIter: Send, Item: Send> + Send, R: Send, ID: Fn() -> R + Send + Sync, F: Fn(R, I::Item) -> R + Send + Sync, RD: Fn(R, R) -> R + Send + Sync,

Reduces a collection to a single value using fold and reduce operations.

This method processes elements from the iterator, combining them into a single result.

§Arguments
  • iter: The collection to fold over
  • identity: A closure that produces the identity value for the fold.
  • fold_op: Combines an accumulator with a single item: (acc, item) -> acc
  • reduce_op: Combines two accumulators: (acc1, acc2) -> acc.
§Examples
§Sum of Elements
use commonware_parallel::{Strategy, Sequential};

let strategy = Sequential;
let numbers = vec![1, 2, 3, 4, 5];

let sum = strategy.fold(
    &numbers,
    || 0,                    // identity
    |acc, &n| acc + n,       // fold: add each number
    |a, b| a + b,            // reduce: combine partial sums
);

assert_eq!(sum, 15);
Source

fn map_collect_vec<I, F, T>(&self, iter: I, map_op: F) -> Vec<T>
where I: IntoIterator<IntoIter: Send, Item: Send> + Send, F: Fn(I::Item) -> T + Send + Sync, T: Send,

Maps each element and collects results into a Vec.

This is a convenience method that applies map_op to each element and collects the results. For Sequential, elements are processed in order. For Rayon, elements may be processed out of order but the final vector preserves the original ordering.

§Arguments
  • iter: The collection to map over
  • map_op: The mapping function to apply to each element
§Examples
use commonware_parallel::{Strategy, Sequential};

let strategy = Sequential;
let data = vec![1, 2, 3, 4, 5];

let squared: Vec<i32> = strategy.map_collect_vec(&data, |&x| x * x);
assert_eq!(squared, vec![1, 4, 9, 16, 25]);
Source

fn try_map_collect_vec<I, F, T, E>( &self, iter: I, map_op: F, ) -> Result<Vec<T>, E>
where I: IntoIterator<IntoIter: Send, Item: Send> + Send, F: Fn(I::Item) -> Result<T, E> + Send + Sync, T: Send, E: Send,

Maps each element with a fallible operation and collects results into a Vec.

This is a convenience method that applies map_op to each element and collects the results into a single Result. Output ordering on success matches map_collect_vec. Implementations may stop applying map_op after an error is observed. When more than one element fails, any error may be returned.

§Arguments
  • iter: The collection to map over
  • map_op: The fallible mapping function to apply to each element
§Examples
use commonware_parallel::{Strategy, Sequential};

let strategy = Sequential;
let data = vec![1, 2, 3, 4, 5];

let squared: Result<Vec<i32>, ()> = strategy.try_map_collect_vec(
    &data,
    |&x| Ok(x * x),
);
assert_eq!(squared, Ok(vec![1, 4, 9, 16, 25]));
Source

fn map_init_collect_vec<I, INIT, T, F, R>( &self, iter: I, init: INIT, map_op: F, ) -> Vec<R>
where I: IntoIterator<IntoIter: Send, Item: Send> + Send, INIT: Fn() -> T + Send + Sync, T: Send, F: Fn(&mut T, I::Item) -> R + Send + Sync, R: Send,

Maps each element with per-partition state and collects results into a Vec.

Combines map_collect_vec with per-partition initialization like fold_init. Useful when the mapping operation requires mutable state that should not be shared across partitions.

§Arguments
  • iter: The collection to map over
  • init: Creates the per-partition initialization value
  • map_op: The mapping function: (&mut init, item) -> result
§Examples
use commonware_parallel::{Strategy, Sequential};

let strategy = Sequential;
let data = vec![1, 2, 3, 4, 5];

// Use a counter that tracks position within each partition
let indexed: Vec<(usize, i32)> = strategy.map_init_collect_vec(
    &data,
    || 0usize, // Per-partition counter
    |counter, &x| {
        let idx = *counter;
        *counter += 1;
        (idx, x * 2)
    },
);

assert_eq!(indexed, vec![(0, 2), (1, 4), (2, 6), (3, 8), (4, 10)]);
Source

fn map_init_collect_vec_with_multiplier<I, INIT, T, F, R>( &self, iter: I, _multiplier: usize, init: INIT, map_op: F, ) -> Vec<R>
where I: IntoIterator<IntoIter: Send, Item: Send> + Send, INIT: Fn() -> T + Send + Sync, T: Send, F: Fn(&mut T, I::Item) -> R + Send + Sync, R: Send,

Maps each element with per-partition state and a per-item work multiplier.

Source

fn map_collect_vec_with_multiplier<I, F, R>( &self, iter: I, multiplier: usize, map_op: F, ) -> Vec<R>
where I: IntoIterator<IntoIter: Send, Item: Send> + Send, F: Fn(I::Item) -> R + Send + Sync, R: Send,

Maps each element with a per-item work multiplier.

Convenience over map_init_collect_vec_with_multiplier for stateless map operations.

Source

fn map_partition_collect_vec<I, F, K, U>( &self, iter: I, map_op: F, ) -> (Vec<U>, Vec<K>)
where I: IntoIterator<IntoIter: Send, Item: Send> + Send, F: Fn(I::Item) -> (K, Option<U>) + Send + Sync, K: Send, U: Send,

Maps each element, filtering out None results and tracking their keys.

This is a convenience method that applies map_op to each element. The closure returns (key, Option<value>). Elements where the option is Some have their values collected into the first vector. Elements where the option is None have their keys collected into the second vector.

§Arguments
  • iter: The collection to map over
  • map_op: The mapping function returning (K, Option<U>)
§Returns

A tuple of (results, filtered_keys) where:

  • results: Values from successful mappings (where map_op returned Some)
  • filtered_keys: Keys where map_op returned None
§Examples
use commonware_parallel::{Strategy, Sequential};

let strategy = Sequential;
let data = vec![1, 2, 3, 4, 5];

let (evens, odd_values): (Vec<i32>, Vec<i32>) = strategy.map_partition_collect_vec(
    data.iter(),
    |&x| (x, if x % 2 == 0 { Some(x * 10) } else { None }),
);

assert_eq!(evens, vec![20, 40]);
assert_eq!(odd_values, vec![1, 3, 5]);
Source

fn sort_unstable_by<T, C>(&self, items: &mut [T], compare: C)
where T: Send, C: Fn(&T, &T) -> Ordering + Send + Sync,

Sorts a slice with a comparator, without preserving the order of equal elements.

Prefer it to sort_by when equal elements are indistinguishable or absent. Leaving their order unspecified lets a strategy choose a faster algorithm.

§Examples
use commonware_parallel::{Strategy, Sequential};

let strategy = Sequential;
let mut data = vec![3, 1, 2];
strategy.sort_unstable_by(&mut data, |a, b| a.cmp(b));
assert_eq!(data, vec![1, 2, 3]);

Dyn Compatibility§

This trait is not dyn compatible.

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

Implementors§

Source§

impl Strategy for Rayon

Available on (crate features std) and neither commonware_stability_DELTA nor commonware_stability_EPSILON nor commonware_stability_GAMMA nor commonware_stability_RESERVED only.
Source§

impl Strategy for Sequential

Available on neither commonware_stability_DELTA nor commonware_stability_EPSILON nor commonware_stability_GAMMA nor commonware_stability_RESERVED.