#[non_exhaustive]pub enum Exec<'a> {
Serial,
SerialWithWorkspace(&'a dyn WorkspaceProvider),
Rayon {
pool: &'a Pool<'a>,
budget: NonZeroUsize,
},
}Expand description
Execution context passed to every operation that may run in parallel.
§Examples
use tcontract_exec::Exec;
assert_eq!(Exec::serial().budget(), 1);
assert!(!Exec::serial().is_worker());Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
Serial
Everything on the calling thread, with call-local scratch.
SerialWithWorkspace(&'a dyn WorkspaceProvider)
Everything on the calling thread, with caller-owned reusable scratch.
A serial context owns no storage, so a caller that wants a serial steady state to allocate nothing keeps a provider alive across calls and lends it here.
Rayon
A borrowed pool with a thread budget <= pool.size().
Fields
budget: NonZeroUsizeMost threads an operation may occupy.
Implementations§
Source§impl<'a> Exec<'a>
impl<'a> Exec<'a>
Sourcepub fn serial_with_workspace(workspace: &'a dyn WorkspaceProvider) -> Self
pub fn serial_with_workspace(workspace: &'a dyn WorkspaceProvider) -> Self
Serial execution with caller-owned reusable storage.
Sourcepub fn with_budget(self, max_threads: usize) -> Result<Self, ExecError>
pub fn with_budget(self, max_threads: usize) -> Result<Self, ExecError>
Limit the budget; values above the pool size are clamped.
§Errors
ExecError::ZeroBudget when max_threads == 0.
§Examples
use tcontract_exec::{Exec, ExecError};
assert_eq!(Exec::serial().with_budget(0).unwrap_err(), ExecError::ZeroBudget);Sourcepub fn workspace(&self) -> Option<&'a dyn WorkspaceProvider>
pub fn workspace(&self) -> Option<&'a dyn WorkspaceProvider>
Storage the operations on this context may share, or none.
A pool lends its own arena to every operation that runs on it; a serial
context owns nothing, so a caller that wants reuse keeps a provider of
its own and lends it through Exec::serial_with_workspace.
Sourcepub fn install<R: Send>(&self, k: usize, op: impl FnOnce(Par) -> R + Send) -> R
pub fn install<R: Send>(&self, k: usize, op: impl FnOnce(Par) -> R + Send) -> R
Run op with min(k, budget) threads of parallelism.
Width one runs op(Par::Seq) inline on the caller. Otherwise op
runs inside the pool, which is entered once, or in place when the
caller is already one of its workers.
Sourcepub fn for_each_partition(&self, k: usize, f: &(dyn Fn(usize) + Sync))
pub fn for_each_partition(&self, k: usize, f: &(dyn Fn(usize) + Sync))
Barrier-free partition: runs f(i) exactly once for each i < k,
on at most budget workers in contiguous lanes.
Tasks are not guaranteed to run concurrently, so f must never wait
on another index.
§Examples
use std::sync::atomic::{AtomicUsize, Ordering};
let n = AtomicUsize::new(0);
tcontract_exec::Exec::serial().for_each_partition(5, &|_| {
n.fetch_add(1, Ordering::Relaxed);
});
assert_eq!(n.into_inner(), 5);Sourcepub fn broadcast(
&self,
width: usize,
f: &(dyn Fn(usize) + Sync),
) -> Result<(), ExecError>
pub fn broadcast( &self, width: usize, f: &(dyn Fn(usize) + Sync), ) -> Result<(), ExecError>
Co-scheduled SPMD execution: f(t) for every t < width runs
concurrently on distinct workers, so f may use barriers counting
width participants.
Width one runs f(0) inline. On a pool the whole pool is dispatched
(Rayon broadcast); workers with index >= width return at once.
Broadcasts through one Pool wrapper are serialized (use one
wrapper per ThreadPool). A broadcast waits until every worker reaches
a scheduling point, so a worker busy in another host’s long job delays
it. A panic in a barrier-free f propagates to the caller after every
participant has finished; if f panics before a barrier the other
participants wait at it forever, so barrier-bearing f must not panic.
§Errors
ExecError::Unavailable when co-scheduling cannot be guaranteed
(Serial with width > 1, or the caller is already a worker of the
pool, where an outer job may hold workers at a barrier); nothing runs
then, and the caller uses its barrier-free variant.
ExecError::WidthExceedsPool when width exceeds the pool, and
ExecError::WidthExceedsBudget when it exceeds the budget (the
active width is capped by the budget even though the whole pool is
dispatched).
§Examples
use tcontract_exec::{Exec, ExecError};
assert_eq!(Exec::serial().broadcast(2, &|_| {}), Err(ExecError::Unavailable));Sourcepub fn width_for(&self, serial_ns: f64, policy: &WidthPolicy) -> usize
pub fn width_for(&self, serial_ns: f64, policy: &WidthPolicy) -> usize
Partition width for work estimated at serial_ns on one thread,
minimizing the WidthPolicy cost model within the budget.
§Examples
use tcontract_exec::{Exec, WidthPolicy};
assert_eq!(Exec::serial().width_for(1e9, &WidthPolicy::default()), 1);Trait Implementations§
Auto Trait Implementations§
impl<'a> !RefUnwindSafe for Exec<'a>
impl<'a> !UnwindSafe for Exec<'a>
impl<'a> Freeze for Exec<'a>
impl<'a> Send for Exec<'a>
impl<'a> Sync for Exec<'a>
impl<'a> Unpin for Exec<'a>
impl<'a> UnsafeUnpin for Exec<'a>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more