git.delta.rocks / jrsonnet / refs/commits / b25a25b23bf9

difftreelog

perf(evaluator) cache LazyVal/obj fields

Лач2020-06-01parent: #11818aa.patch.diff
in: master

2 files changed

modifiedcrates/jsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/obj.rs
+++ b/crates/jsonnet-evaluator/src/obj.rs
@@ -1,7 +1,8 @@
 use crate::{evaluate_binary_op, Binding, Val};
 use jsonnet_parser::{BinaryOpType, Visibility};
 use std::{
-	collections::{BTreeMap, BTreeSet},
+	cell::RefCell,
+	collections::{BTreeMap, BTreeSet, HashMap},
 	fmt::Debug,
 	rc::Rc,
 };
@@ -17,6 +18,7 @@
 pub struct ObjValueInternals {
 	super_obj: Option<ObjValue>,
 	this_entries: Rc<BTreeMap<String, ObjMember>>,
+	value_cache: RefCell<HashMap<String, Val>>,
 }
 pub struct ObjValue(pub(crate) Rc<ObjValueInternals>);
 impl Debug for ObjValue {
@@ -48,6 +50,7 @@
 		ObjValue(Rc::new(ObjValueInternals {
 			super_obj,
 			this_entries,
+			value_cache: RefCell::new(HashMap::new()),
 		}))
 	}
 	pub fn with_super(&self, super_obj: ObjValue) -> ObjValue {
@@ -69,8 +72,14 @@
 		fields
 	}
 	pub fn get(&self, key: &str) -> Option<Val> {
-		// TODO: Cache get_raw result
-		self.get_raw(key, self)
+		if let Some(v) = self.0.value_cache.borrow().get(key) {
+			return Some(v.clone());
+		}
+		let v = self.get_raw(key, self).map(|v| v.unwrap_if_lazy());
+		if let Some(v) = v.clone() {
+			self.0.value_cache.borrow_mut().insert(key.to_owned(), v);
+		}
+		v
 	}
 	fn get_raw(&self, key: &str, real_this: &ObjValue) -> Option<Val> {
 		match (self.0.this_entries.get(key), &self.0.super_obj) {
modifiedcrates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth
before · crates/jsonnet-evaluator/src/val.rs
1use crate::{2	lazy_binding, rc_fn_helper, Context, FunctionDefault, FunctionRhs, LazyBinding, ObjValue,3};4use closure::closure;5use jsonnet_parser::{LiteralType, ParamsDesc};6use std::{7	collections::HashMap,8	fmt::{Debug, Display},9};1011rc_fn_helper!(LazyVal, lazy_val, dyn Fn() -> Val);1213#[derive(Debug, PartialEq, Clone)]14pub struct FuncDesc {15	pub ctx: Context,16	pub params: ParamsDesc,17	pub eval_rhs: FunctionRhs,18	pub eval_default: FunctionDefault,19}20impl FuncDesc {21	// TODO: Check for unset variables22	pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Val {23		let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();24		let future_ctx = Context::new_future();2526		// self.params27		// 	.with_defaults()28		// 	.into_iter()29		// 	.for_each(|Param(name, default)| {30		// 		let default = Rc::new(*default.unwrap());31		// 		new_bindings.insert(32		// 			name,33		// 			binding!(move |_, _| Val::Lazy(lazy_val!(|| self34		// 				.eval_default35		// 				.036		// 				.default(future_ctx.unwrap(), *default.clone())))),37		// 		);38		// 	});39		for (name, val) in args.clone().into_iter().filter(|e| e.0.is_some()) {40			new_bindings.insert(41				name.as_ref().unwrap().clone(),42				lazy_binding!(43					closure!(clone val, |_, _| lazy_val!(closure!(clone val, || val.clone())))44				),45			);46		}47		for (i, param) in self.params.0.iter().enumerate() {48			if let Some((None, val)) = args.get(i) {49				new_bindings.insert(50					param.0.clone(),51					lazy_binding!(52						closure!(clone val, |_, _| lazy_val!(closure!(clone val, || val.clone())))53					),54				);55			}56		}57		let ctx = self58			.ctx59			.extend(new_bindings, None, None, None)60			.into_future(future_ctx);61		self.eval_rhs.0(ctx)62	}63}6465#[derive(Debug, PartialEq, Clone)]66pub enum Val {67	Literal(LiteralType),68	Str(String),69	Num(f64),70	Lazy(LazyVal),71	Arr(Vec<Val>),72	Obj(ObjValue),73	Func(FuncDesc),7475	// Library functions implemented in native76	Intristic(String, String),77}78impl Val {79	pub fn unwrap_if_lazy(self) -> Self {80		if let Val::Lazy(v) = self {81			v.0().unwrap_if_lazy()82		} else {83			self84		}85	}86	pub fn type_of(&self) -> &'static str {87		match self {88			Val::Str(..) => "string",89			Val::Num(..) => "number",90			Val::Arr(..) => "array",91			Val::Obj(..) => "object",92			Val::Func(..) => "function",93			_ => panic!("no native type found"),94		}95	}96}97impl Display for Val {98	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {99		match self {100			Val::Literal(v) => write!(f, "{}", v)?,101			Val::Str(str) => write!(f, "\"{}\"", str)?,102			Val::Num(n) => write!(f, "{}", n)?,103			Val::Arr(values) => {104				write!(f, "[")?;105				let mut first = true;106				for value in values {107					if first {108						first = false;109					} else {110						write!(f, ",")?;111					}112					write!(f, "{}", value)?;113				}114				write!(f, "]")?;115			}116			Val::Obj(value) => {117				write!(f, "{{")?;118				let mut first = true;119				for field in value.fields() {120					if first {121						first = false;122					} else {123						write!(f, ",")?;124					}125					write!(f, "\"{}\":", field)?;126					write!(f, "{}", value.get(&field).unwrap())?;127				}128				write!(f, "}}")?;129			}130			Val::Lazy(lazy) => {131				write!(f, "{}", lazy.0())?;132			}133			Val::Func(_) => {134				write!(f, "<<FUNC>>")?;135			}136			v => panic!("no json equivalent for {:?}", v),137		};138		Ok(())139	}140}141142pub fn bool_val(v: bool) -> Val {143	if v {144		Val::Literal(LiteralType::True)145	} else {146		Val::Literal(LiteralType::False)147	}148}
after · crates/jsonnet-evaluator/src/val.rs
1use crate::{lazy_binding, Context, FunctionDefault, FunctionRhs, LazyBinding, ObjValue};2use closure::closure;3use jsonnet_parser::ParamsDesc;4use std::{5	cell::RefCell,6	collections::HashMap,7	fmt::{Debug, Display},8	rc::Rc,9};1011struct LazyValInternals {12	pub f: Box<dyn Fn() -> Val>,13	pub cached: RefCell<Option<Val>>,14}15#[derive(Clone)]16pub struct LazyVal(Rc<LazyValInternals>);17impl LazyVal {18	pub fn new(f: Box<dyn Fn() -> Val>) -> Self {19		LazyVal(Rc::new(LazyValInternals {20			f,21			cached: RefCell::new(None),22		}))23	}24	pub fn evaluate(&self) -> Val {25		{26			let cached = self.0.cached.borrow();27			if cached.is_some() {28				return cached.clone().unwrap();29			}30		}31		let result = (self.0.f)();32		self.0.cached.borrow_mut().replace(result.clone());33		result34	}35}36#[macro_export]37macro_rules! lazy_val {38	($f: expr) => {39		$crate::LazyVal::new(Box::new($f))40	};41}42impl Debug for LazyVal {43	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {44		write!(f, "Lazy")45	}46}47impl PartialEq for LazyVal {48	fn eq(&self, other: &Self) -> bool {49		Rc::ptr_eq(&self.0, &other.0)50	}51}5253#[derive(Debug, PartialEq, Clone)]54pub struct FuncDesc {55	pub ctx: Context,56	pub params: ParamsDesc,57	pub eval_rhs: FunctionRhs,58	pub eval_default: FunctionDefault,59}60impl FuncDesc {61	// TODO: Check for unset variables62	pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Val {63		let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();64		let future_ctx = Context::new_future();6566		// self.params67		// 	.with_defaults()68		// 	.into_iter()69		// 	.for_each(|Param(name, default)| {70		// 		let default = Rc::new(*default.unwrap());71		// 		new_bindings.insert(72		// 			name,73		// 			binding!(move |_, _| Val::Lazy(lazy_val!(|| self74		// 				.eval_default75		// 				.076		// 				.default(future_ctx.unwrap(), *default.clone())))),77		// 		);78		// 	});79		for (name, val) in args.clone().into_iter().filter(|e| e.0.is_some()) {80			new_bindings.insert(81				name.as_ref().unwrap().clone(),82				lazy_binding!(83					closure!(clone val, |_, _| lazy_val!(closure!(clone val, || val.clone())))84				),85			);86		}87		for (i, param) in self.params.0.iter().enumerate() {88			if let Some((None, val)) = args.get(i) {89				new_bindings.insert(90					param.0.clone(),91					lazy_binding!(92						closure!(clone val, |_, _| lazy_val!(closure!(clone val, || val.clone())))93					),94				);95			}96		}97		let ctx = self98			.ctx99			.extend(new_bindings, None, None, None)100			.into_future(future_ctx);101		self.eval_rhs.0(ctx)102	}103}104105#[derive(Debug, PartialEq, Clone)]106pub enum Val {107	Bool(bool),108	Null,109	Str(String),110	Num(f64),111	Lazy(LazyVal),112	Arr(Vec<Val>),113	Obj(ObjValue),114	Func(FuncDesc),115116	// Library functions implemented in native117	Intristic(String, String),118}119impl Val {120	pub fn unwrap_if_lazy(self) -> Self {121		if let Val::Lazy(v) = self {122			v.evaluate().unwrap_if_lazy()123		} else {124			self125		}126	}127	pub fn type_of(&self) -> &'static str {128		match self {129			Val::Str(..) => "string",130			Val::Num(..) => "number",131			Val::Arr(..) => "array",132			Val::Obj(..) => "object",133			Val::Func(..) => "function",134			_ => panic!("no native type found"),135		}136	}137}138impl Display for Val {139	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {140		match self {141			Val::Str(str) => write!(f, "\"{}\"", str)?,142			Val::Num(n) => write!(f, "{}", n)?,143			Val::Arr(values) => {144				write!(f, "[")?;145				let mut first = true;146				for value in values {147					if first {148						first = false;149					} else {150						write!(f, ",")?;151					}152					write!(f, "{}", value)?;153				}154				write!(f, "]")?;155			}156			Val::Obj(value) => {157				write!(f, "{{")?;158				let mut first = true;159				for field in value.fields() {160					if first {161						first = false;162					} else {163						write!(f, ",")?;164					}165					write!(f, "\"{}\":", field)?;166					write!(f, "{}", value.get(&field).unwrap())?;167				}168				write!(f, "}}")?;169			}170			Val::Lazy(lazy) => {171				write!(f, "{}", lazy.evaluate())?;172			}173			Val::Func(_) => {174				write!(f, "<<FUNC>>")?;175			}176			v => panic!("no json equivalent for {:?}", v),177		};178		Ok(())179	}180}181182pub fn bool_val(v: bool) -> Val {183	Val::Bool(v)184}