Implicit Parallelism — Compute Fabric

Bulk transforms and effectful stream steps parallelize implicitly through Compute Fabric. There is no public Parallelism type and no *_with / *_par policy surface — Fabric decides when Rayon or bounded effect concurrency is profitable.

Use the primary method name (map, filter, sort_with, …). Opt out with *_serial when you need FnMut, non-Send closures, or deterministic ordering.

How dispatch works

Every run_blocking / run_async / run_with boundary refreshes the thread-local AdaptiveContext from the installed supervisor. Bulk paths call parallel_if_profitable, which consults should_parallelize_current(len) and runs Rayon via install_parallel when the snapshot says it pays off.

WorkloadAPIParallelism model
Pure bulk (Vec, HashMap, stream chunks, …)map, filter, sort_with, …Rayon per chunk when profitable
Effectful streamStream::map_effectAdmission-bounded async concurrency per chunk
effect! bindsprimary ~ syntaxEDG parallelizes independent bind sets
Deterministic tests*_serial, #[effect(serial)]explicit opt-out

Under memory pressure the supervisor lowers admission and raises the element threshold; under headroom it admits more fibers and parallelizes smaller chunks. See example 120_compute_fabric_memory_cap.rs.

Collections and vec

#![allow(unused)]
fn main() {
use id_effect::vec;

let doubled = vec::map(vec![1, 2, 3], |x| x * 2);
// Fabric may use Rayon when len is large enough and headroom allows.
}

Escape hatch for captured mutable state or non-Send closures:

#![allow(unused)]
fn main() {
use id_effect::vec;

let mut acc = 0;
vec::map_serial(v, |x| { acc += x; acc });
}

The same pattern applies to HashMap::map_values, filter, red-black tree scans, sort_with, and similar bulk APIs.

Streams — pure transforms

Stream::map and Stream::filter apply Fabric-aware Rayon per upstream chunk:

#![allow(unused)]
fn main() {
use id_effect::Stream;

Stream::from_iterable(0..10_000)
    .map(|n| n * 2)
    .filter(|n| *n % 2 == 0)
    .run_collect();
}

For FnMut or ordering-sensitive work, use map_serial / filter_serial:

#![allow(unused)]
fn main() {
let mut offset = 0;
stream.map_serial(move |n| { offset += 1; n + offset });
}

See example 071_stream_map_serial.rs.

Streams — effectful steps

When each element runs an Effect, use Stream::map_effect. Concurrency is capped by the current admission budget (at least one permit):

#![allow(unused)]
fn main() {
use id_effect::{Stream, succeed};

Stream::from_iterable(items)
    .map_effect(|item| process(item))
    .run_collect();
}

Output order matches stream order. Each mapper receives a clone of the environment R, so R must be Clone + Send + Sync.

With Compute Fabric installed, a memory cap or throttle directly reduces how many effect mappers run in flight. See example 122_compute_fabric_effect_parallel.rs.

effect! and the Effect Dependency Graph

Independent ~ steps with no data or capability conflict may run concurrently when the EDG finds a parallel bind set. Dependent steps and overlapping capability borrows stay sequential.

Opt out per block:

#![allow(unused)]
fn main() {
#[effect(serial)]
effect! { |r| {
    ~step_a();
    ~step_b();
}}
}

Example: 122_compute_fabric_effect_parallel.rs (stream effect mapping under Fabric). For multi-step programs, see Compute Fabric § Effect binds.

Migration from 0.3.x

Old (0.3.x)New (0.4.0)
Parallelism::Auto / ForceParallel / Serialremoved — Fabric decides
map_with(Parallelism::…, f)map(f) or map_serial(f)
*_par (deprecated)map(f)
Stream::map_par_n(n, f)map_effect(f)
Stream::map_par_adaptive(f)map_effect(f)
compute::effective_thresholdremoved (internal to Fabric)

See ADR 0008 (supersedes the public surface of ADR 0006).