Skip to main content

PipelinedExecutor

Struct PipelinedExecutor 

Source
pub struct PipelinedExecutor;
Expand description

Pipelined executor: walks a graph, compiles each node in a dedicated thread, executes the compiled stream on the calling thread.

§Architectural commitments (Phase 7.6 step 9c)

§Fail-fast dispatch — no runtime CPU fallback

The binding-table lookup is fail-fast. If global_bindings().lookup(op_kind, dtypes, backend) returns None, realize / realize_many surface a typed Error::Msg at compile time. There is no runtime fallback to a CPU sibling when a registered kernel returns Err; we commit to the backend contract that registered kernels must succeed for shapes matching their declared coverage.

Rationale: per-decision-point alternatives (architecture v1.0 §04) already let multiple backends register at the same key. If a backend’s coverage is partial, the right answer is to register only the shapes it actually handles — not to silently fall back at runtime, which masks bugs and slows the hot path. The long-term home for “this op on this device” is graph-level dispatch insertion (Op::Copy edges to a backend that does support the shape), which makes the routing decision visible in the IR rather than hidden in the executor. Picker-arc step 4b landed exactly that: compile_plan’s off-device fallback (PlanOptions::fallback_placements_for) admits a candidate on another device when the pinned device lacks the implementation, and the bridge’s cross-device-copy pass stitches residency around the off-device winner. There is exactly ONE fallback owner — the plan-time picker; this executor still dispatches fail-fast, and ops with no implementation anywhere surface the plan-time NoBackendForOp error.

This is a deliberate departure from the legacy executor’s cpu_fallback(op, inputs, shape, dtype, cache) semantics.

§Optimizer separation — callers compose

The pipelined executor does not run fuel_graph::opt or any RuleRegistry pass itself. Callers that want graph-level optimization compose it at the call site:

ⓘ
let optimized_targets = registry.optimize_to_fixpoint(&graph, &targets);
PipelinedExecutor::realize_many(graph, &optimized_targets, inputs)?;

Rationale: the legacy GraphExecutor<B>::with_optimization(bool) coupling reflected its per-typed-entry API surface (realize_f32, etc.) where the convenience of a single optimize+realize call was load-bearing. The pipelined path has a uniform realize_many that composes trivially — no need to re-couple them at the executor layer. This keeps the executor focused on execution and lets the optimizer evolve independently (different rule registries per call site, mid- pipeline opt+realize+opt+realize patterns, etc.).

Implementations§

Source§

impl PipelinedExecutor

Source

pub fn realize( graph: Arc<RwLock<Graph>>, target: NodeId, inputs: StorageCache, ) -> Result<(Arc<RwLock<Storage>>, Layout)>

Realize target and every transitive dependency. Compilation runs in a worker thread; execution runs on the calling thread.

Pre-conditions:

  • Every reachable Op::Const node must have a corresponding entry in inputs (pre-realized Storage Arc).
  • Every reachable non-Const node must have its target_backend set in the graph (Graph::set_target_backend).
  • The op + dtype must be registered in global_bindings().

Returns the realized Storage Arc for target plus its resolved Layout. The Layout is contiguous for kernel outputs; for graphs whose target is a metadata-only view op (Op::Transpose, Op::Permute, Op::BroadcastTo), the returned Storage shares its bytes with an upstream node and the Layout encodes the view’s strides + offset.

Production-correct: errors on any unmet precondition rather than panicking.

Source

pub fn realize_with_env( graph: Arc<RwLock<Graph>>, target: NodeId, inputs: StorageCache, sym_env: SymEnv, ) -> Result<(Arc<RwLock<Storage>>, Layout)>

Env-carrying sibling of Self::realize: realize target with a per-pass SymEnv supplying the runtime bindings for any DynScalar op params (today: Op::WriteSlice’s dynamic start offset — Phase D symbolic extents). An empty env is byte-identical to Self::realize; the env is consulted only by ops that carry a DynScalar, so a graph with none ignores it. Uses the default (execution_plan) dispatch order.

This is the input-determined path for persistent decode (the per-token cached_len write offset). The OptimizedGraph/route entry points keep an empty env until the realize bridge threads the session env through them (Phase D step 1, fuel-core).

Source

pub fn realize_with_env_producing( graph: Arc<RwLock<Graph>>, target: NodeId, inputs: StorageCache, sym_env: SymEnv, ) -> Result<(Arc<RwLock<Storage>>, Layout, SymEnv)>

Producing sibling of Self::realize_with_env: realize target and ALSO return the SymEnv of symbols BOUND BY PRODUCERS during the pass — data-determined dynamic shapes (Op::NonZeroIndices’s runtime count, etc.). The returned env is disjoint from the input sym_env (which is input-determined). Empty when the graph has no data-determined producer. This is the observation point for the increment-2a bind seam; a downstream pass consumes these bindings as dynamic extents.

Source

pub fn realize_with_optimized( graph: Arc<RwLock<Graph>>, target: NodeId, inputs: StorageCache, optimized: &OptimizedGraph, ) -> Result<(Arc<RwLock<Storage>>, Layout)>

PR-A3b-1 entry: realize target driven from optimized’s in-place “plan IS the graph” form. The dispatch order is the OptimizedGraph’s extract_runs/lower_run lowering (computed after safety-copy insertion); per-node kernel resolution uses the binding-table-lookup path (no ExecutionPlan, so no route-picking — A3b-1 is branchless). The optimize-time generation drives the TopologyChanged chunk-boundary check.

Pre-conditions match Self::realize: every reachable kernel-bearing node must have target_backend set (the bridge’s stamp_plan_backends does this) and a registered binding.

Source

pub fn realize_with_optimized_env( graph: Arc<RwLock<Graph>>, target: NodeId, inputs: StorageCache, optimized: &OptimizedGraph, sym_env: SymEnv, ) -> Result<(Arc<RwLock<Storage>>, Layout)>

Env-carrying sibling of Self::realize_with_optimized: same optimized-graph dispatch, but with a per-pass SymEnv supplying the runtime bindings for DynScalar op params (Phase D symbolic extents). An empty env is byte-identical to Self::realize_with_optimized. The realize bridge threads its session env through here for persistent decode.

Source

pub fn realize_with_optimized_route( graph: Arc<RwLock<Graph>>, target: NodeId, inputs: StorageCache, optimized: &OptimizedGraph, route: &PickedRoute, ) -> Result<(Arc<RwLock<Storage>>, Layout)>

PR-C1 entry: realize target driven from optimized’s in-place form following the runtime route picker’s chosen arms. The dispatch order is the route-aware lowering (fuel_graph::lower_picked_route) over route — the per-branch arm the picker (Picker 2) selected by live telemetry. A branch absent from route defaults to arm 0, so an empty route is byte-identical to Self::realize_with_optimized (the no-pressure / no-telemetry contract). A branchless graph has no branches ⇒ the route is empty ⇒ this is exactly the arm-0 path.

Source

pub fn realize_with_optimized_route_env( graph: Arc<RwLock<Graph>>, target: NodeId, inputs: StorageCache, optimized: &OptimizedGraph, route: &PickedRoute, sym_env: SymEnv, ) -> Result<(Arc<RwLock<Storage>>, Layout)>

Env-carrying sibling of Self::realize_with_optimized_route — the route-aware lowering with a per-pass SymEnv. An empty env is byte-identical to Self::realize_with_optimized_route.

Source

pub fn realize_with_optimized_picking_env( graph: Arc<RwLock<Graph>>, target: NodeId, inputs: StorageCache, optimized: &OptimizedGraph, selector: Option<Arc<dyn RuntimeSelector>>, lookup: Option<BackendRuntimeLookup>, sym_env: SymEnv, ) -> Result<(Arc<RwLock<Storage>>, Layout)>

Cleanup Step C/D entry: the executor OWNS Op::Branch arm-selection. Given the runtime selector + the live device lookup (built bridge-side from the realize Device + Judge oracle and passed in, exactly like the residency input_residency provider), pick one arm per branch HERE — at dispatch — then lower the chosen route. The per-arm candidates are re-enumerated from the runtime binding registry + the graph (Step D: no threaded ExecutionPlan).

No selector (the runtime_selector_disabled() opt-out) or a branchless graph yields the arm-0 lowering, byte-identical to Phase B.

Step E Phase C, PR C1 — STREAMING. A branched graph WITH a selector no longer resolves the whole route up front: it routes to the OrderSource::Streaming walk, where the compiler thread resolves each branch lazily as the frontier reaches it (via resolve_branch). C1 keeps the SAME VRAM-only selector chain — so the streamed route equals the one-shot pick_route route byte-for-byte — and only changes when a branch resolves; C2 makes that moment read live device load.

Source

pub fn realize_with_optimized_picking_cached_env( graph: Arc<RwLock<Graph>>, target: NodeId, inputs: StorageCache, optimized: &OptimizedGraph, order_cache: &OnceLock<Vec<NodeId>>, selector: Option<Arc<dyn RuntimeSelector>>, lookup: Option<BackendRuntimeLookup>, sym_env: SymEnv, ) -> Result<(Arc<RwLock<Storage>>, Layout)>

order_for fix track (docs/design/incremental-consumer-index.md §5) — cached sibling of Self::realize_with_optimized_picking_env for the plan-once persistent-decode caller, which holds order_cache across repeated calls over the SAME graph/roots/optimized (one per DecodeSession, initially empty). On a branchless graph with no selector pressure (the decode shape this is for), dispatches via [OrderSource::Cached] — computed once, reused thereafter, see that variant’s doc for the safety argument. A branched graph WITH a resolved selector pick falls back to the uncached [OrderSource::Streaming] path unchanged (caching doesn’t apply there; this entry point exists for the decode case, not to cover every caller).

Source

pub fn realize_many( graph: Arc<RwLock<Graph>>, targets: &[NodeId], inputs: StorageCache, ) -> Result<Vec<(Arc<RwLock<Storage>>, Layout)>>

Realize multiple targets in one walk. Each target’s transitive dependency set is collapsed into a single execution plan (via execution_plan); shared subgraphs are evaluated once. Returns a Vec<(Storage, Layout)> whose order matches targets.

This is Phase A of Phase 7.6 step 9c — feature-parity with the legacy GraphExecutor::realize_many_f32. Multi-session migration target ([memory: project_phase_7_6_step_9c_parity_audit.md]).

Pre-conditions: every reachable Op::Const must be in inputs; every reachable non-Const must have its target_backend set; the op + dtype must be registered in global_bindings(). Same as single-target Self::realize.

Source

pub fn realize_many_with_optimized( graph: Arc<RwLock<Graph>>, targets: &[NodeId], inputs: StorageCache, optimized: &OptimizedGraph, ) -> Result<Vec<(Arc<RwLock<Storage>>, Layout)>>

Multi-target PR-A3b-1 entry — the realize_many sibling of Self::realize_with_optimized. Drives the executor from the OptimizedGraph’s run lowering via the binding-table-lookup path, with the optimize-time generation keying the TopologyChanged chunk-boundary check.

Source

pub fn realize_many_with_optimized_env( graph: Arc<RwLock<Graph>>, targets: &[NodeId], inputs: StorageCache, optimized: &OptimizedGraph, sym_env: SymEnv, ) -> Result<Vec<(Arc<RwLock<Storage>>, Layout)>>

Env-carrying sibling of Self::realize_many_with_optimized (Phase D symbolic extents). An empty env is byte-identical to it.

Source

pub fn realize_many_with_optimized_route( graph: Arc<RwLock<Graph>>, targets: &[NodeId], inputs: StorageCache, optimized: &OptimizedGraph, route: &PickedRoute, ) -> Result<Vec<(Arc<RwLock<Storage>>, Layout)>>

Multi-target PR-C1 entry — the realize_many sibling of Self::realize_with_optimized_route. Lowers each target’s runs following the picker’s chosen arms via fuel_graph::lower_picked_route.

Source

pub fn realize_many_with_optimized_route_env( graph: Arc<RwLock<Graph>>, targets: &[NodeId], inputs: StorageCache, optimized: &OptimizedGraph, route: &PickedRoute, sym_env: SymEnv, ) -> Result<Vec<(Arc<RwLock<Storage>>, Layout)>>

Env-carrying sibling of Self::realize_many_with_optimized_route (Phase D symbolic extents). An empty env is byte-identical to it.

Source

pub fn realize_many_with_optimized_picking_env( graph: Arc<RwLock<Graph>>, targets: &[NodeId], inputs: StorageCache, optimized: &OptimizedGraph, selector: Option<Arc<dyn RuntimeSelector>>, lookup: Option<BackendRuntimeLookup>, sym_env: SymEnv, ) -> Result<Vec<(Arc<RwLock<Storage>>, Layout)>>

Multi-target sibling of Self::realize_with_optimized_picking_env — the executor resolves one arm per Op::Branch (cleanup Step C/D) over the effective targets. PR C1: a branched graph routes to the OrderSource::Streaming walk (lazy per-branch resolution at the frontier); branchless / no-selector falls to the untouched arm-0 path.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more