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>
impl<Key, P: EvictionPolicy, T, B> SkeletonCache<Key, P, T, B>
Sourcepub fn new(cache_size: usize) -> Self
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);Sourcepub fn get_or_insert_with<F>(&self, key: &Key, build: F) -> Arc<Skeleton<T, B>>
pub fn get_or_insert_with<F>(&self, key: &Key, build: F) -> Arc<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]);Sourcepub fn remove<Q>(&self, key: &Q) -> Option<Arc<Skeleton<T, B>>>
pub fn remove<Q>(&self, key: &Q) -> Option<Arc<Skeleton<T, B>>>
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));Sourcepub fn contains_key<Q>(&self, key: &Q) -> bool
pub fn contains_key<Q>(&self, key: &Q) -> bool
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>
impl<P, T, B> SkeletonCache<Box<[Layout]>, P, T, B>
Sourcepub fn run(
&self,
inputs: &[&Tensor<T, B>],
on_miss: &BuildFunction<T, B>,
) -> Result<Tensor<T, B>, OpError>
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]);Sourcepub fn compose<C>(
&self,
inputs: &[&C],
on_miss: &BuildFunction<T, B>,
) -> Result<BakedPromise<T, B>, OpError>where
C: Composable<T, B>,
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]);