Skip to main content

SkeletonCache

Struct SkeletonCache 

Source
pub struct SkeletonCache<Key: Clone + Hash + Eq, P: EvictionPolicy, T, B: Backend = DefaultBackend>(/* private fields */);
Expand description

A concurrent store of skeletons keyed by Key

Holds several skeletons at once and picks one by key. Eviction is delegated to the chosen EvictionPolicy. This is the primitive DynamicSkeleton is built on; reach for that first unless you need a custom key.

§Examples

use candela::skeleton::{BuildFunction, LRUPolicy, SkeletonCache, SkeletonSlot};
use candela::{Layout, Tensor};

// Keyed by input layouts, evicting under an LRU policy.
let cache: SkeletonCache<Box<[Layout]>, LRUPolicy, f32> = SkeletonCache::new(4);
let build: BuildFunction<f32> = Box::new(|inputs: &[Layout]| {
    let a = SkeletonSlot::new(inputs[0].clone());
    (&a * 2.0).into_skeleton(&[a]).unwrap()
});

let out = cache.run(&[&Tensor::from_scalar(3.0, &[4])], &build)?;
assert_eq!(out.data(), &[6.0; 4]);

Implementations§

Source§

impl<Key, P: EvictionPolicy, T, B> SkeletonCache<Key, P, T, B>
where Key: Clone + Hash + Eq, T: Clone + PartialEq + ComputeFor<B>, B: Backend,

Source

pub fn new(cache_size: usize) -> Self

Creates a new cache

Reserves room for at least cache_size entries. The policy decides whether the cache stays at that size or grows past it.

§Examples
use candela::skeleton::{LRUPolicy, SkeletonCache};
use candela::Layout;

let cache: SkeletonCache<Box<[Layout]>, LRUPolicy, f32> = SkeletonCache::new(4);
Source

pub fn get_or_insert_with<F>(&self, key: &Key, build: F) -> Arc<Skeleton<T, B>>
where F: FnOnce() -> Skeleton<T, B>,

Looks up key, building and inserting on a miss

Returns the cached skeleton if key is present. Otherwise build is called, the result is stored under key, and a handle to it is returned. build runs at most once, and only on a miss.

§Examples
use candela::skeleton::{LRUPolicy, SkeletonCache, SkeletonSlot};
use candela::{Layout, Tensor};

let cache: SkeletonCache<Box<[Layout]>, LRUPolicy, f32> = SkeletonCache::new(4);
let key: Box<[Layout]> = Box::new([Layout::new(&[4])]);

// Built on the first call; a second call with the same key reuses it.
let sk = cache.get_or_insert_with(&key, || {
    let a = SkeletonSlot::from_shape(&[4]);
    (&a * 2.0).into_skeleton(&[a]).unwrap()
});
assert_eq!(sk.run(&[&Tensor::from_scalar(3.0, &[4])])?.data(), &[6.0; 4]);
Source

pub fn remove<Q>(&self, key: &Q) -> Option<Arc<Skeleton<T, B>>>
where Key: Borrow<Q>, Q: Hash + Eq + ?Sized,

Removes the entry for key

Returns the skeleton that was stored, or None if key was not present. The freed slot is returned to the cache for reuse.

§Examples
use candela::skeleton::{LRUPolicy, SkeletonCache, SkeletonSlot};
use candela::Layout;

let cache: SkeletonCache<Box<[Layout]>, LRUPolicy, f32> = SkeletonCache::new(4);
let key: Box<[Layout]> = Box::new([Layout::new(&[4])]);
cache.get_or_insert_with(&key, || {
    let a = SkeletonSlot::from_shape(&[4]);
    (&a * 2.0).into_skeleton(&[a]).unwrap()
});

assert!(cache.remove(&key).is_some());
assert!(!cache.contains_key(&key));
Source

pub fn contains_key<Q>(&self, key: &Q) -> bool
where Key: Borrow<Q>, Q: Hash + Eq + ?Sized,

Returns whether key currently has an entry in the cache

§Examples
use candela::skeleton::{LRUPolicy, SkeletonCache, SkeletonSlot};
use candela::Layout;

let cache: SkeletonCache<Box<[Layout]>, LRUPolicy, f32> = SkeletonCache::new(4);
let key: Box<[Layout]> = Box::new([Layout::new(&[4])]);

assert!(!cache.contains_key(&key));
cache.get_or_insert_with(&key, || {
    let a = SkeletonSlot::from_shape(&[4]);
    (&a * 2.0).into_skeleton(&[a]).unwrap()
});
assert!(cache.contains_key(&key));
Source§

impl<P, T, B> SkeletonCache<Box<[Layout]>, P, T, B>

Source

pub fn run( &self, inputs: &[&Tensor<T, B>], on_miss: &BuildFunction<T, B>, ) -> Result<Tensor<T, B>, OpError>

Runs the cached skeleton for the inputs’ shapes, building one on a miss.

Keys the cache by the inputs’ layouts; on a miss on_miss builds the Skeleton, which is then cached and run. See Skeleton::run.

§Examples
use candela::skeleton::{BuildFunction, LRUPolicy, SkeletonCache, SkeletonSlot};
use candela::{Layout, Tensor};

let cache: SkeletonCache<Box<[Layout]>, LRUPolicy, f32> = SkeletonCache::new(4);
let build: BuildFunction<f32> = Box::new(|inputs: &[Layout]| {
    let a = SkeletonSlot::new(inputs[0].clone());
    (&a + 1.0).into_skeleton(&[a]).unwrap()
});

let out = cache.run(&[&Tensor::from_scalar(3.0, &[4])], &build)?;
assert_eq!(out.data(), &[4.0; 4]);
Source

pub fn compose<C>( &self, inputs: &[&C], on_miss: &BuildFunction<T, B>, ) -> Result<BakedPromise<T, B>, OpError>
where C: Composable<T, B>,

Composes the cached skeleton for the inputs’ shapes, building one on a miss.

Like run, but embeds the skeleton’s plan into a BakedPromise instead of executing it. See Skeleton::compose.

§Examples
use candela::skeleton::{BuildFunction, LRUPolicy, SkeletonCache, SkeletonSlot};
use candela::{Layout, Tensor};

let cache: SkeletonCache<Box<[Layout]>, LRUPolicy, f32> = SkeletonCache::new(4);
let build: BuildFunction<f32> = Box::new(|inputs: &[Layout]| {
    let a = SkeletonSlot::new(inputs[0].clone());
    (&a * 2.0).into_skeleton(&[a]).unwrap()
});

// Compose over a lazy promise and fold the result into a larger graph.
let a = Tensor::from_scalar(1.0, &[4]) + 2.0;
let baked = cache.compose(&[&a], &build)?;
assert_eq!(baked.to_promise().materialize().data(), &[6.0; 4]);

Trait Implementations§

Source§

impl<Key: Clone + Hash + Eq, P: EvictionPolicy, T, B: Backend> Debug for SkeletonCache<Key, P, T, B>

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<Key, P, T, B = CpuPure> !Freeze for SkeletonCache<Key, P, T, B>

§

impl<Key, P, T, B> RefUnwindSafe for SkeletonCache<Key, P, T, B>

§

impl<Key, P, T, B> Send for SkeletonCache<Key, P, T, B>
where P: Send, Key: Send, B: Sync + Send, T: Sync + Send,

§

impl<Key, P, T, B> Sync for SkeletonCache<Key, P, T, B>
where P: Send, Key: Send, B: Sync + Send, T: Sync + Send,

§

impl<Key, P, T, B> Unpin for SkeletonCache<Key, P, T, B>
where P: Unpin, Key: Unpin,

§

impl<Key, P, T, B> UnsafeUnpin for SkeletonCache<Key, P, T, B>
where P: UnsafeUnpin,

§

impl<Key, P, T, B> UnwindSafe for SkeletonCache<Key, P, T, 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.