From 8cf9c2719652d298006d51bc82a32908ab4e5335 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 12 Sep 2018 21:50:15 +0300 Subject: generic salsa algo --- crates/salsa/src/lib.rs | 238 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 crates/salsa/src/lib.rs (limited to 'crates/salsa/src') diff --git a/crates/salsa/src/lib.rs b/crates/salsa/src/lib.rs new file mode 100644 index 000000000..69c7b35fa --- /dev/null +++ b/crates/salsa/src/lib.rs @@ -0,0 +1,238 @@ +extern crate im; +extern crate parking_lot; + +use std::{ + sync::Arc, + any::Any, + collections::HashMap, + cell::RefCell, +}; +use parking_lot::Mutex; + +type GroundQueryFn = fn(&T, &(Any + Send + Sync + 'static)) -> (Box, OutputFingerprint); +type QueryFn = fn(&QueryCtx, &(Any + Send + Sync + 'static)) -> (Box, OutputFingerprint); + +#[derive(Debug)] +pub struct Db { + db: Arc>, + query_config: Arc>, +} + +pub struct QueryConfig { + ground_fn: HashMap>, + query_fn: HashMap>, +} + +impl ::std::fmt::Debug for QueryConfig { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + ::std::fmt::Display::fmt("QueryConfig { ... }", f) + } +} + +#[derive(Debug)] +struct DbState { + ground_data: T, + gen: Gen, + graph: Mutex)>>, +} + +#[derive(Debug)] +struct QueryRecord { + params: Arc, + output: Arc, + output_fingerprint: OutputFingerprint, + deps: Vec<(QueryId, OutputFingerprint)>, +} + +impl DbState { + fn record( + &self, + query_id: QueryId, + params: Arc, + output: Arc, + output_fingerprint: OutputFingerprint, + deps: Vec<(QueryId, OutputFingerprint)>, + ) { + let gen = self.gen; + let record = QueryRecord { + params, + output, + output_fingerprint, + deps, + }; + self.graph.lock().insert(query_id, (gen, Arc::new(record))); + } +} + +impl QueryConfig { + pub fn new() -> Self { + QueryConfig { + ground_fn: HashMap::new(), + query_fn: HashMap::new(), + } + } + pub fn with_ground_query( + mut self, + query_type: QueryTypeId, + query_fn: GroundQueryFn + ) -> Self { + let prev = self.ground_fn.insert(query_type, query_fn); + assert!(prev.is_none()); + self + } + pub fn with_query( + mut self, + query_type: QueryTypeId, + query_fn: QueryFn, + ) -> Self { + let prev = self.query_fn.insert(query_type, query_fn); + assert!(prev.is_none()); + self + } +} + +pub struct QueryCtx { + db: Arc>, + query_config: Arc>, + stack: RefCell>>, + executed: RefCell>, +} + +impl QueryCtx { + fn new(db: &Db) -> QueryCtx { + QueryCtx { + db: Arc::clone(&db.db), + query_config: Arc::clone(&db.query_config), + stack: RefCell::new(vec![Vec::new()]), + executed: RefCell::new(Vec::new()), + } + } + pub fn get( + &self, + query_id: QueryId, + params: Arc, + ) -> Arc { + let (res, output_fingerprint) = self.get_inner(query_id, params); + self.record_dep(query_id, output_fingerprint); + res + } + + pub fn get_inner( + &self, + query_id: QueryId, + params: Arc, + ) -> (Arc, OutputFingerprint) { + let (gen, record) = { + let guard = self.db.graph.lock(); + match guard.get(&query_id).map(|it| it.clone()){ + None => { + drop(guard); + return self.force(query_id, params); + }, + Some(it) => it, + } + }; + if gen == self.db.gen { + return (record.output.clone(), record.output_fingerprint) + } + if self.query_config.ground_fn.contains_key(&query_id.0) { + return self.force(query_id, params); + } + for (dep_query_id, prev_fingerprint) in record.deps.iter().cloned() { + let dep_params: Arc = { + let guard = self.db.graph.lock(); + guard[&dep_query_id] + .1 + .params + .clone() + }; + if prev_fingerprint != self.get_inner(dep_query_id, dep_params).1 { + return self.force(query_id, params) + } + } + let gen = self.db.gen; + { + let mut guard = self.db.graph.lock(); + guard[&query_id].0 = gen; + } + (record.output.clone(), record.output_fingerprint) + } + fn force( + &self, + query_id: QueryId, + params: Arc, + ) -> (Arc, OutputFingerprint) { + self.executed.borrow_mut().push(query_id.0); + self.stack.borrow_mut().push(Vec::new()); + + let (res, output_fingerprint) = if let Some(f) = self.ground_query_fn_by_type(query_id.0) { + f(&self.db.ground_data, &*params) + } else if let Some(f) = self.query_fn_by_type(query_id.0) { + f(self, &*params) + } else { + panic!("unknown query type: {:?}", query_id.0); + }; + + let res: Arc = res.into(); + + let deps = self.stack.borrow_mut().pop().unwrap(); + self.db.record(query_id, params, res.clone(), output_fingerprint, deps); + (res, output_fingerprint) + } + fn ground_query_fn_by_type(&self, query_type: QueryTypeId) -> Option> { + self.query_config.ground_fn.get(&query_type).map(|&it| it) + } + fn query_fn_by_type(&self, query_type: QueryTypeId) -> Option> { + self.query_config.query_fn.get(&query_type).map(|&it| it) + } + fn record_dep( + &self, + query_id: QueryId, + output_fingerprint: OutputFingerprint, + ) -> () { + let mut stack = self.stack.borrow_mut(); + let deps = stack.last_mut().unwrap(); + deps.push((query_id, output_fingerprint)) + } +} + +impl Db { + pub fn new(query_config: QueryConfig, ground_data: T) -> Db { + Db { + db: Arc::new(DbState { ground_data, gen: Gen(0), graph: Default::default() }), + query_config: Arc::new(query_config), + } + } + + pub fn with_ground_data(&self, ground_data: T) -> Db { + let gen = Gen(self.db.gen.0 + 1); + let graph = self.db.graph.lock().clone(); + let graph = Mutex::new(graph); + Db { + db: Arc::new(DbState { ground_data, gen, graph }), + query_config: Arc::clone(&self.query_config) + } + } + pub fn get( + &self, + query_id: QueryId, + params: Box, + ) -> (Arc, Vec) { + let ctx = QueryCtx::new(self); + let res = ctx.get(query_id, params.into()); + let executed = ::std::mem::replace(&mut *ctx.executed.borrow_mut(), Vec::new()); + (res, executed) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +struct Gen(u64); +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct InputFingerprint(pub u64); +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct OutputFingerprint(pub u64); +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct QueryTypeId(pub u16); +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct QueryId(pub QueryTypeId, pub InputFingerprint); + -- cgit v1.2.3 From cecc7ad5b20e693cb8d962187bd83b9ac234de97 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 12 Sep 2018 22:11:26 +0300 Subject: be generic over data --- crates/salsa/src/lib.rs | 91 ++++++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 43 deletions(-) (limited to 'crates/salsa/src') diff --git a/crates/salsa/src/lib.rs b/crates/salsa/src/lib.rs index 69c7b35fa..a54f2a06f 100644 --- a/crates/salsa/src/lib.rs +++ b/crates/salsa/src/lib.rs @@ -3,53 +3,52 @@ extern crate parking_lot; use std::{ sync::Arc, - any::Any, collections::HashMap, cell::RefCell, }; use parking_lot::Mutex; -type GroundQueryFn = fn(&T, &(Any + Send + Sync + 'static)) -> (Box, OutputFingerprint); -type QueryFn = fn(&QueryCtx, &(Any + Send + Sync + 'static)) -> (Box, OutputFingerprint); +type GroundQueryFn = fn(&T, &D) -> (D, OutputFingerprint); +type QueryFn = fn(&QueryCtx, &D) -> (D, OutputFingerprint); #[derive(Debug)] -pub struct Db { - db: Arc>, - query_config: Arc>, +pub struct Db { + db: Arc>, + query_config: Arc>, } -pub struct QueryConfig { - ground_fn: HashMap>, - query_fn: HashMap>, +pub struct QueryConfig { + ground_fn: HashMap>, + query_fn: HashMap>, } -impl ::std::fmt::Debug for QueryConfig { +impl ::std::fmt::Debug for QueryConfig { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { ::std::fmt::Display::fmt("QueryConfig { ... }", f) } } #[derive(Debug)] -struct DbState { +struct DbState { ground_data: T, gen: Gen, - graph: Mutex)>>, + graph: Mutex>)>>, } #[derive(Debug)] -struct QueryRecord { - params: Arc, - output: Arc, +struct QueryRecord { + params: D, + output: D, output_fingerprint: OutputFingerprint, deps: Vec<(QueryId, OutputFingerprint)>, } -impl DbState { +impl DbState { fn record( &self, query_id: QueryId, - params: Arc, - output: Arc, + params: D, + output: D, output_fingerprint: OutputFingerprint, deps: Vec<(QueryId, OutputFingerprint)>, ) { @@ -64,7 +63,7 @@ impl DbState { } } -impl QueryConfig { +impl QueryConfig { pub fn new() -> Self { QueryConfig { ground_fn: HashMap::new(), @@ -74,7 +73,7 @@ impl QueryConfig { pub fn with_ground_query( mut self, query_type: QueryTypeId, - query_fn: GroundQueryFn + query_fn: GroundQueryFn ) -> Self { let prev = self.ground_fn.insert(query_type, query_fn); assert!(prev.is_none()); @@ -83,7 +82,7 @@ impl QueryConfig { pub fn with_query( mut self, query_type: QueryTypeId, - query_fn: QueryFn, + query_fn: QueryFn, ) -> Self { let prev = self.query_fn.insert(query_type, query_fn); assert!(prev.is_none()); @@ -91,15 +90,18 @@ impl QueryConfig { } } -pub struct QueryCtx { - db: Arc>, - query_config: Arc>, +pub struct QueryCtx { + db: Arc>, + query_config: Arc>, stack: RefCell>>, executed: RefCell>, } -impl QueryCtx { - fn new(db: &Db) -> QueryCtx { +impl QueryCtx +where + D: Clone +{ + fn new(db: &Db) -> QueryCtx { QueryCtx { db: Arc::clone(&db.db), query_config: Arc::clone(&db.query_config), @@ -110,8 +112,8 @@ impl QueryCtx { pub fn get( &self, query_id: QueryId, - params: Arc, - ) -> Arc { + params: D, + ) -> D { let (res, output_fingerprint) = self.get_inner(query_id, params); self.record_dep(query_id, output_fingerprint); res @@ -120,8 +122,8 @@ impl QueryCtx { pub fn get_inner( &self, query_id: QueryId, - params: Arc, - ) -> (Arc, OutputFingerprint) { + params: D, + ) -> (D, OutputFingerprint) { let (gen, record) = { let guard = self.db.graph.lock(); match guard.get(&query_id).map(|it| it.clone()){ @@ -139,7 +141,7 @@ impl QueryCtx { return self.force(query_id, params); } for (dep_query_id, prev_fingerprint) in record.deps.iter().cloned() { - let dep_params: Arc = { + let dep_params: D = { let guard = self.db.graph.lock(); guard[&dep_query_id] .1 @@ -160,29 +162,29 @@ impl QueryCtx { fn force( &self, query_id: QueryId, - params: Arc, - ) -> (Arc, OutputFingerprint) { + params: D, + ) -> (D, OutputFingerprint) { self.executed.borrow_mut().push(query_id.0); self.stack.borrow_mut().push(Vec::new()); let (res, output_fingerprint) = if let Some(f) = self.ground_query_fn_by_type(query_id.0) { - f(&self.db.ground_data, &*params) + f(&self.db.ground_data, ¶ms) } else if let Some(f) = self.query_fn_by_type(query_id.0) { - f(self, &*params) + f(self, ¶ms) } else { panic!("unknown query type: {:?}", query_id.0); }; - let res: Arc = res.into(); + let res: D = res.into(); let deps = self.stack.borrow_mut().pop().unwrap(); self.db.record(query_id, params, res.clone(), output_fingerprint, deps); (res, output_fingerprint) } - fn ground_query_fn_by_type(&self, query_type: QueryTypeId) -> Option> { + fn ground_query_fn_by_type(&self, query_type: QueryTypeId) -> Option> { self.query_config.ground_fn.get(&query_type).map(|&it| it) } - fn query_fn_by_type(&self, query_type: QueryTypeId) -> Option> { + fn query_fn_by_type(&self, query_type: QueryTypeId) -> Option> { self.query_config.query_fn.get(&query_type).map(|&it| it) } fn record_dep( @@ -196,15 +198,18 @@ impl QueryCtx { } } -impl Db { - pub fn new(query_config: QueryConfig, ground_data: T) -> Db { +impl Db +where + D: Clone +{ + pub fn new(query_config: QueryConfig, ground_data: T) -> Db { Db { db: Arc::new(DbState { ground_data, gen: Gen(0), graph: Default::default() }), query_config: Arc::new(query_config), } } - pub fn with_ground_data(&self, ground_data: T) -> Db { + pub fn with_ground_data(&self, ground_data: T) -> Db { let gen = Gen(self.db.gen.0 + 1); let graph = self.db.graph.lock().clone(); let graph = Mutex::new(graph); @@ -216,8 +221,8 @@ impl Db { pub fn get( &self, query_id: QueryId, - params: Box, - ) -> (Arc, Vec) { + params: D, + ) -> (D, Vec) { let ctx = QueryCtx::new(self); let res = ctx.get(query_id, params.into()); let executed = ::std::mem::replace(&mut *ctx.executed.borrow_mut(), Vec::new()); -- cgit v1.2.3 From 60fdfec32759d5e006eae9fe09a87b1a28b19983 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 12 Sep 2018 22:30:48 +0300 Subject: eager invalidation --- crates/salsa/src/lib.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) (limited to 'crates/salsa/src') diff --git a/crates/salsa/src/lib.rs b/crates/salsa/src/lib.rs index a54f2a06f..5de3c7774 100644 --- a/crates/salsa/src/lib.rs +++ b/crates/salsa/src/lib.rs @@ -3,7 +3,7 @@ extern crate parking_lot; use std::{ sync::Arc, - collections::HashMap, + collections::{HashSet, HashMap}, cell::RefCell, }; use parking_lot::Mutex; @@ -138,7 +138,16 @@ where return (record.output.clone(), record.output_fingerprint) } if self.query_config.ground_fn.contains_key(&query_id.0) { - return self.force(query_id, params); + let (invalidated, record) = { + let guard = self.db.graph.lock(); + let (gen, ref record) = guard[&query_id]; + (gen == INVALIDATED, record.clone()) + }; + if invalidated { + return self.force(query_id, params); + } else { + return (record.output.clone(), record.output_fingerprint); + } } for (dep_query_id, prev_fingerprint) in record.deps.iter().cloned() { let dep_params: D = { @@ -198,6 +207,28 @@ where } } +pub struct Invalidations { + types: HashSet, + ids: Vec, +} + +impl Invalidations { + pub fn new() -> Invalidations { + Invalidations { + types: HashSet::new(), + ids: Vec::new(), + } + } + pub fn invalidate( + &mut self, + query_type: QueryTypeId, + params: impl Iterator, + ) { + self.types.insert(query_type); + self.ids.extend(params.map(|it| QueryId(query_type, it))) + } +} + impl Db where D: Clone @@ -209,9 +240,25 @@ where } } - pub fn with_ground_data(&self, ground_data: T) -> Db { + pub fn with_ground_data( + &self, + ground_data: T, + invalidations: Invalidations, + ) -> Db { + for id in self.query_config.ground_fn.keys() { + assert!( + invalidations.types.contains(id), + "all ground queries must be invalidated" + ); + } + let gen = Gen(self.db.gen.0 + 1); - let graph = self.db.graph.lock().clone(); + let mut graph = self.db.graph.lock().clone(); + for id in invalidations.ids { + if let Some((gen, _)) = graph.get_mut(&id) { + *gen = INVALIDATED; + } + } let graph = Mutex::new(graph); Db { db: Arc::new(DbState { ground_data, gen, graph }), @@ -232,6 +279,7 @@ where #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] struct Gen(u64); +const INVALIDATED: Gen = Gen(!0); #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct InputFingerprint(pub u64); #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -- cgit v1.2.3 From 8c737255ff876fc61f8dc8a7d33252476a4b4c8d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 13 Sep 2018 22:58:36 +0300 Subject: use salsa for new module map --- crates/salsa/src/lib.rs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'crates/salsa/src') diff --git a/crates/salsa/src/lib.rs b/crates/salsa/src/lib.rs index 5de3c7774..75815e8bd 100644 --- a/crates/salsa/src/lib.rs +++ b/crates/salsa/src/lib.rs @@ -8,8 +8,8 @@ use std::{ }; use parking_lot::Mutex; -type GroundQueryFn = fn(&T, &D) -> (D, OutputFingerprint); -type QueryFn = fn(&QueryCtx, &D) -> (D, OutputFingerprint); +type GroundQueryFn = Box (D, OutputFingerprint) + Send + Sync + 'static>; +type QueryFn = Box, &D) -> (D, OutputFingerprint) + Send + Sync + 'static>; #[derive(Debug)] pub struct Db { @@ -119,7 +119,7 @@ where res } - pub fn get_inner( + fn get_inner( &self, query_id: QueryId, params: D, @@ -176,9 +176,9 @@ where self.executed.borrow_mut().push(query_id.0); self.stack.borrow_mut().push(Vec::new()); - let (res, output_fingerprint) = if let Some(f) = self.ground_query_fn_by_type(query_id.0) { + let (res, output_fingerprint) = if let Some(f) = self.query_config.ground_fn.get(&query_id.0) { f(&self.db.ground_data, ¶ms) - } else if let Some(f) = self.query_fn_by_type(query_id.0) { + } else if let Some(f) = self.query_config.query_fn.get(&query_id.0) { f(self, ¶ms) } else { panic!("unknown query type: {:?}", query_id.0); @@ -190,12 +190,6 @@ where self.db.record(query_id, params, res.clone(), output_fingerprint, deps); (res, output_fingerprint) } - fn ground_query_fn_by_type(&self, query_type: QueryTypeId) -> Option> { - self.query_config.ground_fn.get(&query_type).map(|&it| it) - } - fn query_fn_by_type(&self, query_type: QueryTypeId) -> Option> { - self.query_config.query_fn.get(&query_type).map(|&it| it) - } fn record_dep( &self, query_id: QueryId, @@ -239,7 +233,9 @@ where query_config: Arc::new(query_config), } } - + pub fn ground_data(&self) -> &T { + &self.db.ground_data + } pub fn with_ground_data( &self, ground_data: T, -- cgit v1.2.3 From d59413c895e7b49ed2ad01be35871e417a57a43c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 15 Sep 2018 17:21:47 +0300 Subject: yet another db api --- crates/salsa/src/lib.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'crates/salsa/src') diff --git a/crates/salsa/src/lib.rs b/crates/salsa/src/lib.rs index 75815e8bd..35deed374 100644 --- a/crates/salsa/src/lib.rs +++ b/crates/salsa/src/lib.rs @@ -8,8 +8,8 @@ use std::{ }; use parking_lot::Mutex; -type GroundQueryFn = Box (D, OutputFingerprint) + Send + Sync + 'static>; -type QueryFn = Box, &D) -> (D, OutputFingerprint) + Send + Sync + 'static>; +pub type GroundQueryFn = Box (D, OutputFingerprint) + Send + Sync + 'static>; +pub type QueryFn = Box, &D) -> (D, OutputFingerprint) + Send + Sync + 'static>; #[derive(Debug)] pub struct Db { @@ -118,6 +118,9 @@ where self.record_dep(query_id, output_fingerprint); res } + pub fn trace(&self) -> Vec { + ::std::mem::replace(&mut *self.executed.borrow_mut(), Vec::new()) + } fn get_inner( &self, @@ -261,12 +264,15 @@ where query_config: Arc::clone(&self.query_config) } } + pub fn query_ctx(&self) -> QueryCtx { + QueryCtx::new(self) + } pub fn get( &self, query_id: QueryId, params: D, ) -> (D, Vec) { - let ctx = QueryCtx::new(self); + let ctx = self.query_ctx(); let res = ctx.get(query_id, params.into()); let executed = ::std::mem::replace(&mut *ctx.executed.borrow_mut(), Vec::new()); (res, executed) -- cgit v1.2.3