Skip to main content

Runtime

Struct Runtime 

Source
pub struct Runtime(/* private fields */);
Expand description

Runtime owner for immutable configuration snapshots.

§Examples

use tenferro_runtime::Runtime;

let runtime = Runtime::builder().build()?;
assert_eq!(runtime.snapshot()?.engine_count(), 0);

Implementations§

Source§

impl Runtime

Source

pub fn builder() -> RuntimeConfigBuilder

Return a consuming runtime configuration builder.

Source

pub fn id(&self) -> RuntimeId

Return this runtime’s opaque identity.

Source

pub fn snapshot(&self) -> Result<Arc<RuntimeConfigSnapshot>, RuntimeStateError>

Clone the current immutable runtime snapshot.

§Errors

Returns RuntimeStateError::Poisoned when the active snapshot lock was poisoned by another thread.

Source

pub fn epoch(&self) -> Result<RuntimeEpoch, RuntimeStateError>

Return the currently published runtime epoch without locking the active snapshot.

§Errors

Returns RuntimeStateError only if runtime state invariants have been violated internally.

Source

pub fn prepared_cache_limits( &self, ) -> Result<PreparedPlanCacheLimits, RuntimeStateError>

Return current prepared-plan cache limits.

§Examples
use tenferro_runtime::Runtime;

let runtime = Runtime::builder().build()?;
assert_eq!(runtime.prepared_cache_limits()?, Default::default());
§Errors

Returns RuntimeStateError when the runtime-owned prepared cache state cannot be accessed.

Source

pub fn set_prepared_cache_limits( &self, limits: PreparedPlanCacheLimits, ) -> Result<(), RuntimeStateError>

Replace current prepared-plan cache limits and evict retained entries until the new limits are satisfied.

§Examples
use std::num::NonZeroUsize;
use tenferro_runtime::{PreparedPlanCacheLimits, Runtime};

let runtime = Runtime::builder().build()?;
runtime.set_prepared_cache_limits(PreparedPlanCacheLimits {
    max_entries: NonZeroUsize::new(1).unwrap(),
    max_retained_bytes: NonZeroUsize::new(1024).unwrap(),
    max_in_flight_entries: NonZeroUsize::new(1).unwrap(),
    max_queued_distinct_keys: NonZeroUsize::new(1).unwrap(),
})?;
assert_eq!(runtime.prepared_cache_limits()?.max_entries.get(), 1);
§Errors

Returns RuntimeStateError when the runtime-owned prepared cache state cannot be accessed.

Source

pub fn clear_prepared_cache(&self) -> Result<(), RuntimeStateError>

Clear the runtime-owned prepared-plan cache.

§Examples
use tenferro_runtime::Runtime;

let runtime = Runtime::builder().build()?;
runtime.clear_prepared_cache()?;
§Errors

Returns RuntimeStateError when the runtime-owned prepared cache state cannot be accessed.

Source

pub fn cache_stats(&self) -> Result<RuntimeCacheStats, RuntimeCacheError>

Return aggregate cache statistics for the runtime and registered cache owners.

§Examples
use tenferro_runtime::Runtime;

let runtime = Runtime::builder().build()?;
assert_eq!(runtime.cache_stats()?.prepared_plans.entries, 0);
§Errors

Returns RuntimeCacheError when the runtime cache or a registered cache owner cannot report statistics.

Source

pub fn clear_caches(&self) -> Result<(), RuntimeCacheError>

Clear runtime-owned prepared plans and all registered engine/extension cache owners.

§Examples
use tenferro_runtime::Runtime;

let runtime = Runtime::builder().build()?;
runtime.clear_caches()?;
§Errors

Returns RuntimeCacheError when the runtime cache or a registered cache owner cannot be cleared.

Source

pub fn prepare_extension_immediate( &self, engine_id: &EngineId, op: &dyn ExtensionOp, signature: &InputSignature, ) -> Result<PrepareCapability>

Resolve and prepare a single extension operation for immediate eager execution against one exact engine, bypassing SemanticProgram planning and the prepared-program cache.

Provider selection is pinned to engine_id (the eager context’s exact engine); the op is prepared only when that engine is executable, owns the family’s extension slot, and accepts every input signature entry. Capability resolution happens before any planning fields are built. Returns PrepareCapability::Unsupported when engine_id is not registered, is not executable, or has no extension slot for the op’s family so callers may fall back to the compiled path.

§Examples
use std::any::Any;
use std::hash::Hasher;
use std::sync::Arc;

use tenferro_cpu::CpuEngine;
use tenferro_ops::ExtensionShapeContext;
use tenferro_runtime::extension::ExtensionOp;
use tenferro_runtime::{
    ExtensionEngine, ExtensionModule, ExtensionModuleError, ExtensionModuleId,
    ExtensionModuleRegistrar, ExtensionPlanningConfig, InputSignature, PrepareCapability,
    PrepareError, PrepareOptions, Runtime, UnsupportedReason, Tensor, TensorRead,
};
use tenferro_tensor::DType;

#[derive(Debug)]
struct Probe;

impl ExtensionOp for Probe {
    fn family_id(&self) -> &'static str { "tenferro-tests.immediate-probe.v1" }
    fn payload_hash(&self, _hasher: &mut dyn Hasher) {}
    fn payload_eq(&self, other: &dyn ExtensionOp) -> bool { other.as_any().downcast_ref::<Self>().is_some() }
    fn clone_arc(&self) -> Arc<dyn ExtensionOp> { Arc::new(Self) }
    fn as_any(&self) -> &dyn Any { self }
    fn input_count(&self) -> usize { 1 }
    fn output_count(&self) -> usize { 1 }
    fn infer_output_meta(&self, ctx: &mut ExtensionShapeContext<'_>) -> tenferro_tensor::Result<Vec<(DType, Vec<tenferro_ops::SymDim>)>> {
        Ok(vec![(ctx.input_dtype(0)?, ctx.input_shape(0)?.to_vec())])
    }
}

#[derive(Debug)]
struct Config;

impl ExtensionPlanningConfig for Config {
    fn family_id(&self) -> &'static str { Probe.family_id() }
    fn as_any(&self) -> &dyn Any { self }
    fn payload_hash(&self, _state: &mut dyn Hasher) {}
    fn payload_eq(&self, other: &dyn ExtensionPlanningConfig) -> bool { other.as_any().downcast_ref::<Self>().is_some() }
    fn retained_bytes(&self) -> usize { 0 }
}

#[derive(Debug)]
struct Module(ExtensionModuleId);

impl ExtensionModule for Module {
    fn module_id(&self) -> &ExtensionModuleId { &self.0 }
    fn configure(&self, registrar: &mut ExtensionModuleRegistrar<'_>) -> Result<(), ExtensionModuleError> {
        let Ok(engine_id) = tenferro_cpu::runtime_engine_id() else {
            // Best-effort: an unresolvable engine id keeps the op unsupported.
            return Ok(());
        };
        registrar.register_engine(Arc::new(NoopEngine { engine_id: engine_id.clone() }))?;
        registrar.register_planning_config(engine_id, Arc::new(Config))?;
        Ok(())
    }
}

#[derive(Debug)]
struct NoopEngine { engine_id: tenferro_runtime::EngineId }

impl ExtensionEngine for NoopEngine {
    fn family_id(&self) -> &'static str { Probe.family_id() }
    fn engine_id(&self) -> &tenferro_runtime::EngineId { &self.engine_id }
    fn context_identity(&self) -> tenferro_runtime::ExecutionContextIdentity { tenferro_runtime::ExecutionContextIdentity::of::<CpuEngine>() }
    fn prepare(&self, _request: tenferro_runtime::ExtensionPrepareRequest<'_>) -> Result<PrepareCapability, PrepareError> {
        Ok(PrepareCapability::Unsupported(UnsupportedReason::Operation { operation: Probe.family_id() }))
    }
}

let engine = CpuEngine::serial();
let mut builder = Runtime::builder();
builder.register_engine(tenferro_cpu::runtime_engine_registration(&engine)?)?;
builder.install_extension_module(Arc::new(Module(ExtensionModuleId::new("tenferro-tests.immediate-probe.module")?)))?;
let runtime = builder.build()?;
let tensor = Tensor::from_vec_col_major(vec![1], vec![1.0_f64])?;
let signature = InputSignature::from_reads(&[TensorRead::from_tensor(&tensor)])?;
let engine_id = tenferro_cpu::runtime_engine_id()?;
let capability = runtime.prepare_extension_immediate(&engine_id, &Probe, &signature)?;
assert!(matches!(capability, PrepareCapability::Unsupported { .. }));
§Errors

Returns crate::Error::RuntimeState when the snapshot cannot be read or an engine’s preparation fails, with the typed PrepareError/RuntimeStateError source retained.

Source

pub fn prepare( &self, program: &CompiledGraph, inputs: &[TensorRead<'_>], targets: &[EngineTarget], ) -> Result<Prepared>

Prepare program to run on engines described by targets.

Preparation does the expensive checks and decisions once: it places each part of the program on one of the targets, plans fusion for those targets, and records what each target slot requires. The prepared program holds no engine; Self::run binds slot i to the i-th engine it receives. Pass the inputs (or none, to use the graph’s bound tensors) whose shapes, types, layouts and memory the program will run on.

§Examples
use tenferro_runtime::{EngineTarget, Prepared, Runtime, TracedTensor, GraphCompiler};

fn prepare_for(
    runtime: &Runtime,
    x: &TracedTensor,
    target: EngineTarget,
) -> tenferro_runtime::Result<Prepared> {
    let program = GraphCompiler::new().compile(x)?;
    runtime.prepare(&program, &[], &[target])
}
§Errors

Returns [crate::Error::InvalidArgument] when targets is empty or names an engine twice, crate::Error::RuntimeState when a target names an engine this runtime has not registered or reports a different context identity than its registration, metadata validation errors for inputs that do not match the graph, and preparation failures (no eligible engine, missing ingress or transfer route) with the typed crate::PrepareError source retained.

Source

pub fn run<'a>( &self, engines: &mut [&mut dyn ExecutionEngine], prepared: &'a Prepared, inputs: &[TensorRead<'a>], ) -> Result<Vec<Output<'a>>>

Run a prepared program on the engines passed in.

Slot i of prepared runs on engines[i]; every engine must report the target the program was prepared for. On a CPU engine the call returns when the work is done. On a GPU engine it returns once the work is queued and no longer reads caller memory that could change; later work on the same engine runs after it in stream order. An output that is one of the inputs, or a view of one, is returned borrowed; every other output is a new compact tensor.

§Examples
use tenferro_runtime::{ExecutionEngine, Output, Prepared, Runtime};
use tenferro_tensor::TensorRead;

fn run_on<'a>(
    runtime: &Runtime,
    engine: &mut dyn ExecutionEngine,
    prepared: &'a Prepared,
    inputs: &[TensorRead<'a>],
) -> tenferro_runtime::Result<Vec<Output<'a>>> {
    runtime.run(&mut [engine], prepared, inputs)
}
§Errors

Returns crate::Error::RuntimeState when prepared belongs to another runtime or a stale epoch, [crate::Error::InvalidArgument] when the engines do not match the prepared targets, metadata validation errors for mismatched inputs, and the engines’ own execution errors.

Source

pub fn warm_up<'a>( &self, engines: &mut [&mut dyn ExecutionEngine], prepared: &'a Prepared, inputs: &[TensorRead<'a>], ) -> Result<()>

Pay prepared’s first execution now: run it once and wait for it.

A prepared program is specialized to its input signature, so one execution compiles exactly the kernels it will use — fused regions, reduction strategies and any vendor plan — and initializes whatever the engine creates lazily. Self::run is otherwise unchanged: this is one ordinary run whose outputs are dropped, followed by [ExecutionEngine::synchronize], so the cost is not still queued when the caller’s measured region starts.

§Examples
use tenferro_runtime::{ExecutionEngine, Prepared, Runtime};
use tenferro_tensor::TensorRead;

fn warm_up<'a>(
    runtime: &Runtime,
    engine: &mut dyn ExecutionEngine,
    prepared: &'a Prepared,
    inputs: &[TensorRead<'a>],
) -> tenferro_runtime::Result<()> {
    runtime.warm_up(&mut [engine], prepared, inputs)
}
§Errors

crate::Error::RuntimeState when prepared belongs to another runtime or a stale epoch, [crate::Error::InvalidArgument] when the engines do not match the prepared targets or the input metadata does not match the program, and crate::Error::TensorRuntime when an engine fails the run or cannot synchronize.

Source

pub fn reconfigure( &self, edit: impl FnOnce(&mut RuntimeReconfiguration<'_>) -> Result<(), RuntimeConfigError>, ) -> Result<RuntimeEpoch, RuntimeReconfigureError>

Transactionally edit and publish runtime configuration.

No user callback runs while the publication lock is held. If another writer publishes over the same base snapshot, this call returns RuntimeReconfigureError::ConcurrentReconfiguration and publishes nothing.

§Errors

Returns RuntimeReconfigureError when state access, edit validation, identity allocation, epoch advancement, or compare-and-publish fails. Invalid transfer endpoints are reported as the typed RuntimeConfigError::UnknownTransferEndpointEngine or RuntimeConfigError::UnsupportedTransferEndpointStorage source of RuntimeReconfigureError::Edit.

Trait Implementations§

Source§

impl Clone for Runtime

Source§

fn clone(&self) -> Runtime

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Runtime

Source§

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

Formats the value using the given formatter. Read more

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<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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.