Invalidate Caches based on the received input (#1067)

* Invalidate Caches based on the received input

* Add comments

* Fix missing *
This commit is contained in:
Dennis Kobert
2023-03-03 20:35:28 +01:00
committed by Keavon Chambers
parent ccb698ffa8
commit 4ea3802df1
3 changed files with 53 additions and 18 deletions
Generated
+7
View File
@@ -359,6 +359,12 @@ dependencies = [
"dyn-any", "dyn-any",
] ]
[[package]]
name = "boxcar"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38c99613cb3cd7429889a08dfcf651721ca971c86afa30798461f8eee994de47"
[[package]] [[package]]
name = "brotli" name = "brotli"
version = "3.3.4" version = "3.3.4"
@@ -1663,6 +1669,7 @@ dependencies = [
"autoquant", "autoquant",
"bezier-rs", "bezier-rs",
"borrow_stack", "borrow_stack",
"boxcar",
"bytemuck", "bytemuck",
"compilation-client", "compilation-client",
"dyn-any", "dyn-any",
+3 -2
View File
@@ -19,7 +19,7 @@ quantization = ["autoquant"]
[dependencies] [dependencies]
autoquant = { git = "https://github.com/truedoctor/autoquant", optional = true, features = ["fitting"] } autoquant = { git = "https://github.com/truedoctor/autoquant", optional = true, features = ["fitting"] }
graphene-core = {path = "../gcore", features = ["async", "std" ], default-features = false} graphene-core = {path = "../gcore", features = ["async", "std", "serde" ], default-features = false}
borrow_stack = {path = "../borrow_stack"} borrow_stack = {path = "../borrow_stack"}
dyn-any = {path = "../../libraries/dyn-any", features = ["derive"]} dyn-any = {path = "../../libraries/dyn-any", features = ["derive"]}
graph-craft = {path = "../graph-craft"} graph-craft = {path = "../graph-craft"}
@@ -38,12 +38,13 @@ image = "*"
dyn-clone = "1.0" dyn-clone = "1.0"
log = "0.4" log = "0.4"
bezier-rs = { path = "../../libraries/bezier-rs" } bezier-rs = { path = "../../libraries/bezier-rs" , features = ["serde"] }
kurbo = { git = "https://github.com/linebender/kurbo.git", features = [ kurbo = { git = "https://github.com/linebender/kurbo.git", features = [
"serde", "serde",
] } ] }
glam = { version = "0.22", features = ["serde"] } glam = { version = "0.22", features = ["serde"] }
node-macro = { path="../node-macro" } node-macro = { path="../node-macro" }
boxcar = "0.1.0"
[dependencies.serde] [dependencies.serde]
version = "1.0" version = "1.0"
+43 -16
View File
@@ -1,50 +1,77 @@
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData; use std::marker::PhantomData;
use graphene_core::Node; use graphene_core::Node;
use once_cell::sync::OnceCell;
/// Caches the output of a given Node and acts as a proxy /// Caches the output of a given Node and acts as a proxy
#[derive(Default)] #[derive(Default)]
pub struct CacheNode<T> { pub struct CacheNode<T> {
cache: OnceCell<T>, // We have to use an append only data structure to make sure the references
// to the cache entries are always valid
cache: boxcar::Vec<(u64, T)>,
} }
impl<'i, T: 'i> Node<'i, T> for CacheNode<T> { impl<'i, T: 'i + Hash> Node<'i, T> for CacheNode<T> {
type Output = &'i T; type Output = &'i T;
fn eval<'s: 'i>(&'s self, input: T) -> Self::Output { fn eval<'s: 'i>(&'s self, input: T) -> Self::Output {
self.cache.get_or_init(|| { let mut hasher = DefaultHasher::new();
trace!("Creating new cache node"); input.hash(&mut hasher);
input let hash = hasher.finish();
})
if let Some((_, cached_value)) = self.cache.iter().find(|(h, _)| *h == hash) {
return cached_value;
} else {
trace!("Cache miss");
let index = self.cache.push((hash, input));
return &self.cache[index].1;
}
} }
} }
impl<T> CacheNode<T> { impl<T> CacheNode<T> {
pub const fn new() -> CacheNode<T> { pub fn new() -> CacheNode<T> {
CacheNode { cache: OnceCell::new() } CacheNode { cache: boxcar::Vec::new() }
} }
} }
/// Caches the output of a given Node and acts as a proxy /// Caches the output of a given Node and acts as a proxy
/// It provides two modes of operation, it can either be set
/// when calling the node with a `Some<T>` variant or the last
/// value that was added is returned when calling it with `None`
#[derive(Debug, Clone, PartialEq, Eq, Default)] #[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct LetNode<T> { pub struct LetNode<T> {
cache: OnceCell<T>, // We have to use an append only data structure to make sure the references
// to the cache entries are always valid
// TODO: We only ever access the last value so there is not really a reason for us
// to store the previous entries. This should be reworked in the future
cache: boxcar::Vec<(u64, T)>,
} }
impl<'i, T: 'i> Node<'i, Option<T>> for LetNode<T> { impl<'i, T: 'i + Hash> Node<'i, Option<T>> for LetNode<T> {
type Output = &'i T; type Output = &'i T;
fn eval<'s: 'i>(&'s self, input: Option<T>) -> Self::Output { fn eval<'s: 'i>(&'s self, input: Option<T>) -> Self::Output {
match input { match input {
Some(input) => { Some(input) => {
self.cache.set(input).unwrap_or_else(|_| error!("Let node was set twice but is not mutable")); let mut hasher = DefaultHasher::new();
self.cache.get().unwrap() input.hash(&mut hasher);
let hash = hasher.finish();
if let Some((cached_hash, cached_value)) = self.cache.iter().last() {
if hash == *cached_hash {
return cached_value;
}
}
trace!("Cache miss");
let index = self.cache.push((hash, input));
return &self.cache[index].1;
} }
None => self.cache.get().expect("Let node was not initialized"), None => &self.cache.iter().last().expect("Let node was not initialized").1,
} }
} }
} }
impl<T> LetNode<T> { impl<T> LetNode<T> {
pub const fn new() -> LetNode<T> { pub fn new() -> LetNode<T> {
LetNode { cache: OnceCell::new() } LetNode { cache: boxcar::Vec::new() }
} }
} }