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
impl Runtime
Sourcepub fn builder() -> RuntimeConfigBuilder
pub fn builder() -> RuntimeConfigBuilder
Return a consuming runtime configuration builder.
Sourcepub fn snapshot(&self) -> Result<Arc<RuntimeConfigSnapshot>, RuntimeStateError>
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.
Sourcepub fn epoch(&self) -> Result<RuntimeEpoch, RuntimeStateError>
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.
Sourcepub fn prepared_cache_limits(
&self,
) -> Result<PreparedPlanCacheLimits, RuntimeStateError>
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.
Sourcepub fn set_prepared_cache_limits(
&self,
limits: PreparedPlanCacheLimits,
) -> Result<(), RuntimeStateError>
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.
Sourcepub fn clear_prepared_cache(&self) -> Result<(), RuntimeStateError>
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.
Sourcepub fn cache_stats(&self) -> Result<RuntimeCacheStats, RuntimeCacheError>
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.
Sourcepub fn clear_caches(&self) -> Result<(), RuntimeCacheError>
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.
Sourcepub fn prepare_extension_immediate(
&self,
engine_id: &EngineId,
op: &dyn ExtensionOp,
signature: &InputSignature,
) -> Result<PrepareCapability>
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.
Sourcepub fn prepare(
&self,
program: &CompiledGraph,
inputs: &[TensorRead<'_>],
targets: &[EngineTarget],
) -> Result<Prepared>
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.
Sourcepub fn run<'a>(
&self,
engines: &mut [&mut dyn ExecutionEngine],
prepared: &'a Prepared,
inputs: &[TensorRead<'a>],
) -> Result<Vec<Output<'a>>>
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.
Sourcepub fn warm_up<'a>(
&self,
engines: &mut [&mut dyn ExecutionEngine],
prepared: &'a Prepared,
inputs: &[TensorRead<'a>],
) -> Result<()>
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.
Sourcepub fn reconfigure(
&self,
edit: impl FnOnce(&mut RuntimeReconfiguration<'_>) -> Result<(), RuntimeConfigError>,
) -> Result<RuntimeEpoch, RuntimeReconfigureError>
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.