Skip to main content

GraphExecutor

Struct GraphExecutor 

Source
pub struct GraphExecutor<B: TensorBackend + 'static> { /* private fields */ }
Expand description

Executes compiled graph programs on a concrete tensor backend.

A graph executor owns backend execution state only: backend runtime caches, extension runtime state, and reusable execution workspace. Compilation state lives in GraphCompiler.

§Examples

use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphCompiler, GraphExecutor, TracedTensor};

let x = TracedTensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap();
let y = (&x + &x).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler.compile(&y).unwrap();

let mut executor = GraphExecutor::new(CpuBackend::new());
let out = executor.run(&program).unwrap();
assert_eq!(out.as_slice::<f64>().unwrap(), &[2.0, 4.0]);

Implementations§

Source§

impl<B: TensorBackend + 'static> GraphExecutor<B>

Source

pub fn new(backend: B) -> Self

Create an executor with the given backend and bounded default caches.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphExecutor};

let executor = GraphExecutor::new(CpuBackend::new());
assert_eq!(executor.cache_stats().extensions.entries, 0);
Source

pub fn backend(&self) -> &B

Borrow the backend used by this executor.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphExecutor};

let executor = GraphExecutor::new(CpuBackend::new());
let _backend = executor.backend();
Source

pub fn reclaim_outputs(&mut self, outputs: Vec<Tensor>)

Return output tensors to the executor backend’s reusable buffer pool.

This is useful for tight benchmark or serving loops that consume an output before the next run and want backend-level output allocation behavior to match caching allocators.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphCompiler, GraphExecutor, TracedTensor};

let x = TracedTensor::from_vec_col_major(vec![1], vec![2.0_f64]).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler.compile(&x.neg()).unwrap();
let mut executor = GraphExecutor::new(CpuBackend::new());

let out = executor.run(&program).unwrap();
assert_eq!(out.as_slice::<f64>().unwrap(), &[-2.0]);
executor.reclaim_outputs(vec![out]);
Source

pub fn reclaim_value_outputs(&mut self, outputs: Vec<TensorValue>)

Return compact value outputs to the backend pool when ownership is unique.

Lazy owned views are intentionally ignored because their base storage may be aliased by view metadata.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphExecutor, Tensor, TensorValue};

let tensor = Tensor::from_vec_col_major(vec![1], vec![3.0_f64]).unwrap();
let mut executor = GraphExecutor::new(CpuBackend::new());
executor.reclaim_value_outputs(vec![TensorValue::from_tensor(tensor)]);
Source

pub fn extension_executor(&self) -> &ExtensionExecutor<B>

Borrow the extension runtime executor owned by this graph executor.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphExecutor};

let executor = GraphExecutor::new(CpuBackend::new());
assert_eq!(executor.extension_executor().cache_stats().entries, 0);
Source

pub fn extension_executor_mut(&mut self) -> &mut ExtensionExecutor<B>

Mutably borrow the extension runtime executor owned by this graph executor.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphExecutor};

let mut executor = GraphExecutor::new(CpuBackend::new());
executor.extension_executor_mut().clear_caches();
Source

pub fn register_extension( &mut self, register: impl FnOnce(&mut ExtensionExecutor<B>) -> Result<(), ExtensionRuntimeRegistryError>, ) -> Result<(), ExtensionRuntimeRegistryError>

Register one extension runtime on this executor.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::GraphExecutor;

let mut executor = GraphExecutor::new(CpuBackend::new());
executor.register_extension(|_| Ok(())).unwrap();
Source

pub fn run(&mut self, program: &GraphProgram) -> Result<Tensor>

Run a one-output program using the program’s default input tensors.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphCompiler, GraphExecutor, TracedTensor};

let x = TracedTensor::from_vec_col_major(vec![1], vec![3.0_f64]).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler.compile(&x.neg()).unwrap();
let mut executor = GraphExecutor::new(CpuBackend::new());
let out = executor.run(&program).unwrap();
assert_eq!(out.as_slice::<f64>().unwrap(), &[-3.0]);
Source

pub fn run_value(&mut self, program: &GraphProgram) -> Result<TensorValue>

Run a one-output program and preserve lazy owned output views.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphCompiler, GraphExecutor, TensorValue, TracedTensor};

let x = TracedTensor::from_vec_col_major(
    vec![2, 3],
    vec![1.0_f64, 2.0, 3.0, 4.0, 5.0, 6.0],
)
.unwrap();
let y = x.transpose(&[1, 0]).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler.compile(&y).unwrap();

let mut executor = GraphExecutor::new(CpuBackend::new());
let value = executor.run_value(&program).unwrap();
assert!(matches!(&value, TensorValue::View(_)));
assert_eq!(value.shape(), &[3, 2]);
assert_eq!(
    value.to_tensor().unwrap().as_slice::<f64>().unwrap(),
    &[1.0, 3.0, 5.0, 2.0, 4.0, 6.0]
);
Source

pub fn run_many(&mut self, program: &GraphProgram) -> Result<Vec<Tensor>>

Run a program using the program’s default input tensors.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphCompiler, GraphExecutor, TracedTensor};

let x = TracedTensor::from_vec_col_major(vec![1], vec![3.0_f64]).unwrap();
let y = x.neg();
let mut compiler = GraphCompiler::new();
let program = compiler.compile_many(&[&x, &y]).unwrap();
let mut executor = GraphExecutor::new(CpuBackend::new());
let outputs = executor.run_many(&program).unwrap();
assert_eq!(outputs.len(), 2);
Source

pub fn run_many_values( &mut self, program: &GraphProgram, ) -> Result<Vec<TensorValue>>

Run a program and preserve lazy owned output views.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphCompiler, GraphExecutor, TensorValue, TracedTensor};

let x = TracedTensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 2.0, 3.0, 4.0]).unwrap();
let y = x.transpose(&[1, 0]).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler.compile_many(&[&y]).unwrap();

let mut executor = GraphExecutor::new(CpuBackend::new());
let outputs = executor.run_many_values(&program).unwrap();
assert_eq!(outputs.len(), 1);
assert!(matches!(&outputs[0], TensorValue::View(_)));
assert_eq!(outputs[0].shape(), &[2, 2]);
Source

pub fn run_with_inputs( &mut self, program: &GraphProgram, bindings: &[(&TracedTensor, &Tensor)], ) -> Result<Tensor>

Run a one-output program with explicit runtime placeholder bindings.

Explicit bindings override program defaults and are validated against the ordered input specs captured in the compiled program.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{DType, GraphCompiler, GraphExecutor, Tensor, TracedTensor};

let x = TracedTensor::input_symbolic_shape(DType::F64, 1).unwrap();
let y = (&x + &x).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler
    .compile_with_input_specs(&y, &[(&x, DType::F64, &[2])])
    .unwrap();
let bound = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap();
let mut executor = GraphExecutor::new(CpuBackend::new());
let out = executor.run_with_inputs(&program, &[(&x, &bound)]).unwrap();
assert_eq!(out.as_slice::<f64>().unwrap(), &[2.0, 4.0]);
Source

pub fn run_value_with_inputs( &mut self, program: &GraphProgram, bindings: &[(&TracedTensor, &Tensor)], ) -> Result<TensorValue>

Run a one-output program with explicit bindings and preserve lazy output views.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{DType, GraphCompiler, GraphExecutor, Tensor, TensorValue, TracedTensor};

let x = TracedTensor::input_symbolic_shape(DType::F64, 2).unwrap();
let y = x.transpose(&[1, 0]).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler
    .compile_with_input_specs(&y, &[(&x, DType::F64, &[2, 2])])
    .unwrap();
let bound = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 2.0, 3.0, 4.0]).unwrap();
let mut executor = GraphExecutor::new(CpuBackend::new());

let value = executor.run_value_with_inputs(&program, &[(&x, &bound)]).unwrap();
assert!(matches!(&value, TensorValue::View(_)));
assert_eq!(value.to_tensor().unwrap().as_slice::<f64>().unwrap(), &[1.0, 3.0, 2.0, 4.0]);
Source

pub fn run_with_input_reads<'a>( &mut self, program: &'a GraphProgram, bindings: &[(&TracedTensor, TensorRead<'a>)], ) -> Result<Tensor>

Run a one-output program with explicit borrowed runtime placeholder bindings.

Unlike run_with_inputs, caller-owned input tensors are read through TensorRead and are not cloned into executor slots.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{
    DType, GraphCompiler, GraphExecutor, TensorRead, TensorView, TracedTensor, TypedTensorView,
};

let x = TracedTensor::input_symbolic_shape(DType::F64, 1).unwrap();
let y = (&x + &x).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler
    .compile_with_input_specs(&y, &[(&x, DType::F64, &[2])])
    .unwrap();
let data = [1.0_f64, 99.0, 2.0];
let view = TypedTensorView::from_slice([2], [2], 0, &data).unwrap();
let read = TensorRead::from_view(TensorView::F64(view));
let mut executor = GraphExecutor::new(CpuBackend::new());

let out = executor.run_with_input_reads(&program, &[(&x, read)]).unwrap();
assert_eq!(out.as_slice::<f64>().unwrap(), &[2.0, 4.0]);
Source

pub fn run_value_with_input_reads<'a>( &mut self, program: &'a GraphProgram, bindings: &[(&TracedTensor, TensorRead<'a>)], ) -> Result<TensorValue>

Run a one-output program with borrowed bindings and preserve lazy output views.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{
    DType, GraphCompiler, GraphExecutor, TensorRead, TensorValue, TensorView, TracedTensor,
    TypedTensorView,
};

let x = TracedTensor::input_symbolic_shape(DType::F64, 2).unwrap();
let y = x.transpose(&[1, 0]).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler
    .compile_with_input_specs(&y, &[(&x, DType::F64, &[2, 2])])
    .unwrap();
let data = [1.0_f64, 2.0, 3.0, 4.0];
let view = TypedTensorView::from_slice([2, 2], [1, 2], 0, &data).unwrap();
let read = TensorRead::from_view(TensorView::F64(view));
let mut executor = GraphExecutor::new(CpuBackend::new());

let value = executor
    .run_value_with_input_reads(&program, &[(&x, read)])
    .unwrap();
assert!(matches!(&value, TensorValue::View(_)));
assert_eq!(value.shape(), &[2, 2]);
Source

pub fn run_many_with_inputs( &mut self, program: &GraphProgram, bindings: &[(&TracedTensor, &Tensor)], ) -> Result<Vec<Tensor>>

Run a program with explicit runtime placeholder bindings.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{DType, GraphCompiler, GraphExecutor, Tensor, TracedTensor};

let x = TracedTensor::input_symbolic_shape(DType::F64, 1).unwrap();
let sum = (&x + &x).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler
    .compile_with_input_specs(&sum, &[(&x, DType::F64, &[2])])
    .unwrap();
let bound = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap();
let mut executor = GraphExecutor::new(CpuBackend::new());
let outputs = executor.run_many_with_inputs(&program, &[(&x, &bound)]).unwrap();
assert_eq!(outputs.len(), 1);
assert_eq!(outputs[0].as_slice::<f64>().unwrap(), &[2.0, 4.0]);
Source

pub fn run_many_values_with_inputs( &mut self, program: &GraphProgram, bindings: &[(&TracedTensor, &Tensor)], ) -> Result<Vec<TensorValue>>

Run a program with explicit bindings and preserve lazy output views.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{DType, GraphCompiler, GraphExecutor, Tensor, TensorValue, TracedTensor};

let x = TracedTensor::input_symbolic_shape(DType::F64, 2).unwrap();
let y = x.transpose(&[1, 0]).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler
    .compile_with_input_specs(&y, &[(&x, DType::F64, &[2, 2])])
    .unwrap();
let bound = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 2.0, 3.0, 4.0]).unwrap();
let mut executor = GraphExecutor::new(CpuBackend::new());

let outputs = executor
    .run_many_values_with_inputs(&program, &[(&x, &bound)])
    .unwrap();
assert_eq!(outputs.len(), 1);
assert!(matches!(&outputs[0], TensorValue::View(_)));
Source

pub fn run_many_with_input_reads<'a>( &mut self, program: &'a GraphProgram, bindings: &[(&TracedTensor, TensorRead<'a>)], ) -> Result<Vec<Tensor>>

Run a program with explicit borrowed runtime placeholder bindings.

Bindings override program defaults and are validated against the input specs captured in the compiled program. Bound tensors are borrowed by the executor for this call instead of cloned into input slots.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{
    DType, GraphCompiler, GraphExecutor, TensorRead, TensorView, TracedTensor, TypedTensorView,
};

let x = TracedTensor::input_symbolic_shape(DType::F64, 1).unwrap();
let y = (&x + &x).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler
    .compile_with_input_specs(&y, &[(&x, DType::F64, &[2])])
    .unwrap();
let data = [1.0_f64, 99.0, 2.0];
let view = TypedTensorView::from_slice([2], [2], 0, &data).unwrap();
let read = TensorRead::from_view(TensorView::F64(view));
let mut executor = GraphExecutor::new(CpuBackend::new());

let outputs = executor.run_many_with_input_reads(&program, &[(&x, read)]).unwrap();
assert_eq!(outputs[0].as_slice::<f64>().unwrap(), &[2.0, 4.0]);
Source

pub fn run_many_values_with_input_reads<'a>( &mut self, program: &'a GraphProgram, bindings: &[(&TracedTensor, TensorRead<'a>)], ) -> Result<Vec<TensorValue>>

Run a program with borrowed bindings and preserve lazy output views.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{
    DType, GraphCompiler, GraphExecutor, TensorRead, TensorValue, TensorView, TracedTensor,
    TypedTensorView,
};

let x = TracedTensor::input_symbolic_shape(DType::F64, 2).unwrap();
let y = x.transpose(&[1, 0]).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler
    .compile_with_input_specs(&y, &[(&x, DType::F64, &[2, 2])])
    .unwrap();
let data = [1.0_f64, 2.0, 3.0, 4.0];
let view = TypedTensorView::from_slice([2, 2], [1, 2], 0, &data).unwrap();
let read = TensorRead::from_view(TensorView::F64(view));
let mut executor = GraphExecutor::new(CpuBackend::new());

let outputs = executor
    .run_many_values_with_input_reads(&program, &[(&x, read)])
    .unwrap();
assert_eq!(outputs.len(), 1);
assert!(matches!(&outputs[0], TensorValue::View(_)));
Source

pub fn eval_exec_ir( &mut self, program: &ExecProgram, inputs: Vec<Tensor>, ) -> Result<Vec<Tensor>>

Evaluate an execution program through this executor’s backend state.

This lower-level entry point is intended for code that already owns an execution program and concrete ordered input tensors.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphCompiler, GraphExecutor, TracedTensor};

let x = TracedTensor::from_vec_col_major(vec![1], vec![2.0_f64]).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler.compile(&x.neg()).unwrap();
let mut executor = GraphExecutor::new(CpuBackend::new());
let out = executor.run(&program).unwrap();
assert_eq!(out.as_slice::<f64>().unwrap(), &[-2.0]);
Source

pub fn eval_exec_ir_values( &mut self, program: &ExecProgram, inputs: Vec<Tensor>, ) -> Result<Vec<TensorValue>>

Evaluate an execution program and preserve lazy owned output views.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphCompiler, GraphExecutor, TensorValue, TracedTensor};

let x = TracedTensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 2.0, 3.0, 4.0]).unwrap();
let y = x.transpose(&[1, 0]).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler.compile(&y).unwrap();
let mut executor = GraphExecutor::new(CpuBackend::new());

let value = executor.run_value(&program).unwrap();
assert!(matches!(&value, TensorValue::View(_)));
assert_eq!(value.shape(), &[2, 2]);
Source

pub fn eval_exec_ir_non_consuming( &mut self, program: &ExecProgram, inputs: &[Tensor], ) -> Result<Vec<Tensor>>

Evaluate an execution program without consuming caller-owned inputs.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphCompiler, GraphExecutor, TracedTensor};

let x = TracedTensor::from_vec_col_major(vec![1], vec![2.0_f64]).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler.compile(&x.neg()).unwrap();
let mut executor = GraphExecutor::new(CpuBackend::new());
let out = executor.run(&program).unwrap();
assert_eq!(out.shape(), &[1]);
Source

pub fn eval_exec_ir_non_consuming_values( &mut self, program: &ExecProgram, inputs: &[Tensor], ) -> Result<Vec<TensorValue>>

Evaluate an execution program without consuming inputs and preserve lazy outputs.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphCompiler, GraphExecutor, TensorValue, TracedTensor};

let x = TracedTensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 2.0, 3.0, 4.0]).unwrap();
let y = x.transpose(&[1, 0]).unwrap();
let mut compiler = GraphCompiler::new();
let program = compiler.compile(&y).unwrap();
let mut executor = GraphExecutor::new(CpuBackend::new());

let value = executor.run_value(&program).unwrap();
assert!(matches!(&value, TensorValue::View(_)));
assert_eq!(value.to_tensor().unwrap().shape(), &[2, 2]);
Source

pub fn clear_backend_cache(&mut self)

Clear backend-specific runtime analysis cache entries.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphExecutor};

let mut executor = GraphExecutor::new(CpuBackend::new());
executor.clear_backend_cache();
assert_eq!(executor.cache_stats().backend.entries, 0);
Source

pub fn clear_extension_caches(&mut self)

Clear generic extension runtime cache entries.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphExecutor};

let mut executor = GraphExecutor::new(CpuBackend::new());
executor.clear_extension_caches();
assert_eq!(executor.cache_stats().extensions.entries, 0);
Source

pub fn clear_caches(&mut self)

Clear every executor-owned runtime cache.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphExecutor};

let mut executor = GraphExecutor::new(CpuBackend::new());
executor.clear_caches();
assert_eq!(executor.cache_stats().backend.entries, 0);
Source

pub fn cache_stats(&self) -> GraphExecutorCacheStats

Return executor runtime cache-entry and retained-byte stats.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_runtime::{GraphExecutor};

let executor = GraphExecutor::new(CpuBackend::new());
let stats = executor.cache_stats();
assert_eq!(stats.extensions.entries, 0);

Trait Implementations§

Source§

impl<B: TensorBackend + 'static> Debug for GraphExecutor<B>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<B> Default for GraphExecutor<B>
where B: Default + TensorBackend + 'static,

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<B> Freeze for GraphExecutor<B>

§

impl<B> !RefUnwindSafe for GraphExecutor<B>

§

impl<B> Send for GraphExecutor<B>
where B: Send,

§

impl<B> Sync for GraphExecutor<B>
where B: Sync,

§

impl<B> Unpin for GraphExecutor<B>

§

impl<B> UnsafeUnpin for GraphExecutor<B>

§

impl<B> !UnwindSafe for GraphExecutor<B>

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<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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.