Commit bd57bb10 authored by Jeff Smits's avatar Jeff Smits

Added rudimentary caching to BuildTerm for 'large' terms

parent 4845c86c
......@@ -528,12 +528,11 @@ mod build_term {
}
Cached(C(ref cache), ref term) => {
if let Option::Some(ref cached_term) = *cache.borrow() {
Ok(cached_term.clone())
} else {
let result = build_with_annos(term, context, annos)?;
*cache.borrow_mut() = Some(result.clone());
Ok(result)
return Ok(cached_term.clone())
}
let result = build_with_annos(term, context, annos)?;
*cache.borrow_mut() = Some(result.clone());
Ok(result)
}
}
}
......
......@@ -334,34 +334,72 @@ pub enum BuildTerm<'a> {
Cached(C<'a>, Box<BuildTerm<'a>>),
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct C<'a>(pub RefCell<Option<ATermRef<'a>>>);
impl<'a> C<'a> {
fn new() -> Self {
C(RefCell::new(None))
}
}
impl<'a> ::std::hash::Hash for C<'a> {
fn hash<H: ::std::hash::Hasher>(&self, _state: &mut H) {}
}
type SizeEstimate = Option<usize>;
fn add_estimates(first: SizeEstimate, second: SizeEstimate) -> SizeEstimate {
first.and_then(|u1| second.map(|u2| u1 + u2))
}
fn add_estimates_1(first: SizeEstimate, second: SizeEstimate) -> SizeEstimate {
first.and_then(|u1| second.map(|u2| u1 + u2 + 1))
}
// After many thoughtful, reproducible experiments this is the empirically discovered sweet spot for
// caching BuildTerm. Yeah, if only, I just made an "educated guess"
const BUILD_CACHED_THRESHOLD: usize = 7;
impl<'a> TryFrom<ctree::Term<'a>> for BuildTerm<'a> {
type Err = Error;
fn try_from(value: ctree::Term) -> Result<BuildTerm> {
let (bt, est) = value.try_into()?;
if est.is_some() && est.unwrap() > BUILD_CACHED_THRESHOLD {
Ok(BuildTerm::Cached(C::new(), Box::new(bt)))
} else {
Ok(bt)
}
}
}
impl<'a> TryFrom<ctree::Term<'a>> for (BuildTerm<'a>, SizeEstimate) {
type Err = Error;
fn try_from(value: ctree::Term) -> Result<(BuildTerm, SizeEstimate)> {
use ctree::Term as I;
use self::BuildTerm as O;
use ctree::PreTerm as P;
match value {
I::Var(id) => Ok(O::Var(id)),
I::Var(id) => Ok((O::Var(id), None)),
I::Anno(term, annos) => {
if let P::Var(_) = *term {
let pt1 = try_into_box(term)?;
let pt2 = try_into_box(annos)?;
Ok(O::Anno(pt1, pt2))
let (bt1, est1) = (*term).try_into()?;
let (bt2, est2) = (*annos).try_into()?;
let est = add_estimates_1(est1, est2);
Ok((O::Anno(Box::new(bt1), Box::new(bt2)), est))
} else {
match *annos {
P::Op("Nil", ref r) if r.is_empty() => (*term).try_into(),
P::Op("Nil", ref r) if r.is_empty() => {
let (pt, est) = (*term).try_into()?;
Ok((pt, est.map(|u| u + 1)))
},
_ => {
let pt1 = try_into_box(term)?;
let pt2 = try_into_box(annos)?;
Ok(O::Anno(pt1, pt2))
let (bt1, est1) = (*term).try_into()?;
let (bt2, est2) = (*annos).try_into()?;
let est = add_estimates_1(est1, est2);
Ok((O::Anno(Box::new(bt1), Box::new(bt2)), est))
}
}
}
......@@ -375,23 +413,33 @@ impl<'a> TryFrom<ctree::Term<'a>> for BuildTerm<'a> {
impl<'a> TryFrom<ctree::PreTerm<'a>> for BuildTerm<'a> {
type Err = Error;
fn try_from(value: ctree::PreTerm<'a>) -> Result<BuildTerm<'a>> {
fn try_from(value: ctree::PreTerm) -> Result<BuildTerm> {
Ok(TryInto::<(BuildTerm, SizeEstimate)>::try_into(value)?.0)
}
}
impl<'a> TryFrom<ctree::PreTerm<'a>> for (BuildTerm<'a>, SizeEstimate) {
type Err = Error;
fn try_from(value: ctree::PreTerm) -> Result<(BuildTerm, SizeEstimate)> {
use self::BuildTerm as O;
use ctree::PreTerm as I;
match value {
I::Var(id) => Ok(O::Var(id)),
I::Int(i) => Ok(O::Int(i)),
I::Real(f) => Ok(O::Real(f)),
I::Str(s) => Ok(O::Str(s)),
I::Var(id) => Ok((O::Var(id), None)),
I::Int(i) => Ok((O::Int(i), Some(1))),
I::Real(f) => Ok((O::Real(f), Some(1))),
I::Str(s) => Ok((O::Str(s), Some(1))),
I::Op(cons, children) |
I::OpQ(cons, children) => {
let children = try_into_vec(children)?;
Ok(O::Op(cons, children))
let (children, est): (Vec<_>, Vec<SizeEstimate>) = try_into_vec(children)?.into_iter().unzip();
let est = est.into_iter().fold(Some(1), add_estimates);
Ok((O::Op(cons, children), est))
}
I::Explode(cons, children) => {
let cons = cons.try_into()?;
let children = children.try_into()?;
Ok(O::Explode(Box::new(cons), Box::new(children)))
let (cons, est1) = cons.try_into()?;
let (children, est2) = children.try_into()?;
let est = add_estimates_1(est1, est2);
Ok((O::Explode(Box::new(cons), Box::new(children)), est))
}
I::Wld => Err(Error::CTreeParse("Match pattern in build: _")),
I::As(_, _) => Err(Error::CTreeParse("Match pattern in build: @")),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment