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

difftreelog

perf use fxhash

Lach2020-08-23parent: #84fe36c.patch.diff
in: master

8 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -172,6 +172,7 @@
  "jrsonnet-stdlib",
  "md5",
  "pathdiff",
+ "rustc-hash",
  "serde",
  "serde_json",
  "structdump",
@@ -322,6 +323,12 @@
 ]
 
 [[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
 name = "ryu"
 version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
modifiedcrates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -9,7 +9,7 @@
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [features]
-default = ["serialized-stdlib", "faster", "explaining-traces"]
+default = ["serialized-stdlib", "faster", "explaining-traces", "serde-json"]
 # Serializes standard library AST instead of parsing them every run
 serialized-stdlib = ["serde", "bincode", "jrsonnet-parser/deserialize"]
 # Allow to convert Val into serde_json::Value and backwards
@@ -35,6 +35,7 @@
 
 md5 = "0.7.0"
 base64 = "0.12.3"
+rustc-hash = "1.1.0"
 
 # Serialized stdlib
 [dependencies.serde]
modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -158,7 +158,7 @@
 		("std", "native") => parse_args!(context, "std.native", args, 1, [
 			0, x: [Val::Str]!!Val::Str, vec![ValType::Str];
 		], {
-			Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(FuncVal::NativeExt(x.clone(), v))).ok_or_else(
+			Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Rc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or_else(
 				|| UndefinedExternalFunction(x),
 			)?)
 		})?,
@@ -212,7 +212,7 @@
 			if arr.len() <= 1 {
 				return Ok(Val::Arr(arr))
 			}
-			Ok(Val::Arr(sort::sort(context, arr, keyF)?))
+			Ok(Val::Arr(sort::sort(context, arr, &keyF)?))
 		})?,
 		// faster
 		("std", "format") => parse_args!(context, "std.format", args, 2, [
modifiedcrates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/ctx.rs
+++ b/crates/jrsonnet-evaluator/src/ctx.rs
@@ -2,6 +2,8 @@
 	error::Error::*, future_wrapper, map::LayeredHashMap, rc_fn_helper, resolved_lazy_val,
 	LazyBinding, LazyVal, ObjValue, Result, Val,
 };
+use rustc_hash::FxHashMap;
+use std::hash::BuildHasherDefault;
 use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
 
 rc_fn_helper!(
@@ -71,14 +73,15 @@
 	}
 
 	pub fn with_var(self, name: Rc<str>, value: Val) -> Context {
-		let mut new_bindings = HashMap::with_capacity(1);
+		let mut new_bindings =
+			FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());
 		new_bindings.insert(name, resolved_lazy_val!(value));
 		self.extend(new_bindings, None, None, None)
 	}
 
 	pub fn extend(
 		self,
-		new_bindings: HashMap<Rc<str>, LazyVal>,
+		new_bindings: FxHashMap<Rc<str>, LazyVal>,
 		new_dollar: Option<ObjValue>,
 		new_this: Option<ObjValue>,
 		new_super_obj: Option<ObjValue>,
@@ -127,7 +130,8 @@
 	) -> Result<Context> {
 		let this = new_this.or_else(|| self.0.this.clone());
 		let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());
-		let mut new = HashMap::with_capacity(new_bindings.len());
+		let mut new =
+			FxHashMap::with_capacity_and_hasher(new_bindings.len(), BuildHasherDefault::default());
 		for (k, v) in new_bindings.into_iter() {
 			new.insert(k, v.evaluate(this.clone(), super_obj.clone())?);
 		}
modifiedcrates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate.rs
@@ -9,6 +9,7 @@
 	ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,
 	Visibility,
 };
+use rustc_hash::FxHashMap;
 use std::{collections::HashMap, rc::Rc};
 
 pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (Rc<str>, LazyBinding) {
@@ -45,7 +46,7 @@
 }
 
 pub fn evaluate_method(ctx: Context, name: Rc<str>, params: ParamsDesc, body: LocExpr) -> Val {
-	Val::Func(FuncVal::Normal(Rc::new(FuncDesc {
+	Val::Func(Rc::new(FuncVal::Normal(FuncDesc {
 		name,
 		ctx,
 		params,
@@ -351,7 +352,7 @@
 					let key = evaluate(ctx.clone(), &obj.key)?;
 					let value = LazyBinding::Bindable(Rc::new(
 						closure!(clone ctx, clone obj.value, |this, _super_obj| {
-							Ok(LazyVal::new_resolved(evaluate(ctx.clone().extend(HashMap::new(), None, this, None), &value)?))
+							Ok(LazyVal::new_resolved(evaluate(ctx.clone().extend(FxHashMap::default(), None, this, None), &value)?))
 						}),
 					));
 
@@ -468,7 +469,7 @@
 							} else if let Some(Val::Str(n)) =
 								v.get("__intristic_namespace__".into())?
 							{
-								Ok(Val::Func(FuncVal::Intristic(n, s)))
+								Ok(Val::Func(Rc::new(FuncVal::Intristic(n, s))))
 							} else {
 								throw!(NoSuchField(s))
 							}
modifiedcrates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/function.rs
1use crate::{error::Error::*, evaluate, lazy_val, resolved_lazy_val, throw, Context, Result, Val};2use closure::closure;3use jrsonnet_parser::{ArgsDesc, ParamsDesc};4use std::{collections::HashMap, rc::Rc};56const NO_DEFAULT_CONTEXT: &str =7	"no default context set for call with defined default parameter value";89/// Creates correct [context](Context) for function body evaluation, returning error on invalid call10///11/// * `ctx` used for passed argument expressions execution, and for body execution (if `body_ctx` is not set)12/// * `body_ctx` used for default parameter values execution, and for body execution (if set)13/// * `params` function parameters definition14/// * `args` passed function arguments15/// * `tailstruct` if true - function arguments is eager executed, otherwise - lazy16pub fn parse_function_call(17	ctx: Context,18	body_ctx: Option<Context>,19	params: &ParamsDesc,20	args: &ArgsDesc,21	tailstrict: bool,22) -> Result<Context> {23	let mut out = HashMap::with_capacity(params.len());24	let mut positioned_args = vec![None; params.0.len()];25	for (id, arg) in args.iter().enumerate() {26		let idx = if let Some(name) = &arg.0 {27			params28				.iter()29				.position(|p| *p.0 == *name)30				.ok_or_else(|| UnknownFunctionParameter(name.clone()))?31		} else {32			id33		};3435		if idx >= params.len() {36			throw!(TooManyArgsFunctionHas(params.len()));37		}38		if positioned_args[idx].is_some() {39			throw!(BindingParameterASecondTime(params[idx].0.clone()));40		}41		positioned_args[idx] = Some(arg.1.clone());42	}43	// Fill defaults44	for (id, p) in params.iter().enumerate() {45		let (ctx, expr) = if let Some(arg) = &positioned_args[id] {46			(ctx.clone(), arg)47		} else if let Some(default) = &p.1 {48			(body_ctx.clone().expect(NO_DEFAULT_CONTEXT), default)49		} else {50			throw!(FunctionParameterNotBoundInCall(p.0.clone()));51		};52		let val = if tailstrict {53			resolved_lazy_val!(evaluate(ctx, expr)?)54		} else {55			lazy_val!(closure!(clone ctx, clone expr, ||evaluate(ctx.clone(), &expr)))56		};57		out.insert(p.0.clone(), val);58	}5960	Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))61}6263pub fn parse_function_call_map(64	ctx: Context,65	body_ctx: Option<Context>,66	params: &ParamsDesc,67	args: &HashMap<Rc<str>, Val>,68	tailstrict: bool,69) -> Result<Context> {70	let mut out = HashMap::with_capacity(params.len());71	let mut positioned_args = vec![None; params.0.len()];72	for (name, val) in args.iter() {73		let idx = params74			.iter()75			.position(|p| *p.0 == **name)76			.ok_or_else(|| UnknownFunctionParameter((&name as &str).to_owned()))?;7778		if idx >= params.len() {79			throw!(TooManyArgsFunctionHas(params.len()));80		}81		if positioned_args[idx].is_some() {82			throw!(BindingParameterASecondTime(params[idx].0.clone()));83		}84		positioned_args[idx] = Some(val.clone());85	}86	// Fill defaults87	for (id, p) in params.iter().enumerate() {88		let val = if let Some(arg) = positioned_args[id].take() {89			resolved_lazy_val!(arg)90		} else if let Some(default) = &p.1 {91			if tailstrict {92				resolved_lazy_val!(evaluate(93					body_ctx.clone().expect(NO_DEFAULT_CONTEXT),94					default95				)?)96			} else {97				let body_ctx = body_ctx.clone();98				let default = default.clone();99				lazy_val!(move || {100					evaluate(body_ctx.clone().expect(NO_DEFAULT_CONTEXT), &default)101				})102			}103		} else {104			throw!(FunctionParameterNotBoundInCall(p.0.clone()));105		};106		out.insert(p.0.clone(), val);107	}108109	Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))110}111112pub(crate) fn place_args(113	ctx: Context,114	body_ctx: Option<Context>,115	params: &ParamsDesc,116	args: &[Val],117) -> Result<Context> {118	let mut out = HashMap::with_capacity(params.len());119	let mut positioned_args = vec![None; params.0.len()];120	for (id, arg) in args.iter().enumerate() {121		if id >= params.len() {122			throw!(TooManyArgsFunctionHas(params.len()));123		}124		positioned_args[id] = Some(arg);125	}126	// Fill defaults127	for (id, p) in params.iter().enumerate() {128		let val = if let Some(arg) = &positioned_args[id] {129			(*arg).clone()130		} else if let Some(default) = &p.1 {131			evaluate(ctx.clone(), default)?132		} else {133			throw!(FunctionParameterNotBoundInCall(p.0.clone()));134		};135		out.insert(p.0.clone(), resolved_lazy_val!(val));136	}137138	Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))139}140141#[macro_export]142macro_rules! parse_args {143	($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [144		$($id: expr, $name: ident $(: [$($p: path)|+] $(!! $a: path)?)?, $nt: expr);+ $(;)?145	], $handler:block) => {{146		use crate::{throw, error::Error::*};147		let args = $args;148		if args.len() > $total_args {149			throw!(TooManyArgsFunctionHas($total_args));150		}151		$(152			if args.len() <= $id {153				throw!(FunctionParameterNotBoundInCall(stringify!($name).into()));154			}155			let $name = &args[$id];156			if $name.0.is_some() {157				if $name.0.as_ref().unwrap() != stringify!($name) {158					throw!(IntristicArgumentReorderingIsNotSupportedYet);159				}160			}161			let $name = evaluate($ctx.clone(), &$name.1)?;162			$(163				match $name {164					$($p(_))|+ => {},165					_ => throw!(TypeMismatch(166						concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"),167						$nt, $name.value_type()?168					)),169				};170				$(171					let $name = match $name {172						$a(v) => v,173						_ =>throw!(TypeMismatch(concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"), $nt, $name.value_type()?)),174					};175				)*176			)*177		)+178		($handler as crate::Result<_>)179	}};180}181182#[test]183fn test() -> Result<()> {184	use crate::val::ValType;185	use jrsonnet_parser::*;186	let state = crate::EvaluationState::default();187	let evaluator = state.with_stdlib();188	let ctx = evaluator.create_default_context()?;189	evaluator.run_in_state(|| {190		parse_args!(ctx, "test", ArgsDesc(vec![191			Arg(None, el!(Expr::Num(2.0))),192			Arg(Some("b".into()), el!(Expr::Num(1.0))),193		]), 2, [194			0, a: [Val::Num]!!Val::Num, vec![ValType::Num];195			1, b: [Val::Num]!!Val::Num, vec![ValType::Num];196		], {197			assert!((a - 2.0).abs() <= f64::EPSILON);198			assert!((b - 1.0).abs() <= f64::EPSILON);199			Ok(())200		})201		.unwrap();202		Ok(())203	})204}
after · crates/jrsonnet-evaluator/src/function.rs
1use crate::{error::Error::*, evaluate, lazy_val, resolved_lazy_val, throw, Context, Result, Val};2use closure::closure;3use jrsonnet_parser::{ArgsDesc, ParamsDesc};4use rustc_hash::FxHashMap;5use std::{collections::HashMap, hash::BuildHasherDefault, rc::Rc};67const NO_DEFAULT_CONTEXT: &str =8	"no default context set for call with defined default parameter value";910/// Creates correct [context](Context) for function body evaluation, returning error on invalid call11///12/// * `ctx` used for passed argument expressions execution, and for body execution (if `body_ctx` is not set)13/// * `body_ctx` used for default parameter values execution, and for body execution (if set)14/// * `params` function parameters definition15/// * `args` passed function arguments16/// * `tailstruct` if true - function arguments is eager executed, otherwise - lazy17pub fn parse_function_call(18	ctx: Context,19	body_ctx: Option<Context>,20	params: &ParamsDesc,21	args: &ArgsDesc,22	tailstrict: bool,23) -> Result<Context> {24	let mut out = HashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());25	let mut positioned_args = vec![None; params.0.len()];26	for (id, arg) in args.iter().enumerate() {27		let idx = if let Some(name) = &arg.0 {28			params29				.iter()30				.position(|p| *p.0 == *name)31				.ok_or_else(|| UnknownFunctionParameter(name.clone()))?32		} else {33			id34		};3536		if idx >= params.len() {37			throw!(TooManyArgsFunctionHas(params.len()));38		}39		if positioned_args[idx].is_some() {40			throw!(BindingParameterASecondTime(params[idx].0.clone()));41		}42		positioned_args[idx] = Some(arg.1.clone());43	}44	// Fill defaults45	for (id, p) in params.iter().enumerate() {46		let (ctx, expr) = if let Some(arg) = &positioned_args[id] {47			(ctx.clone(), arg)48		} else if let Some(default) = &p.1 {49			(body_ctx.clone().expect(NO_DEFAULT_CONTEXT), default)50		} else {51			throw!(FunctionParameterNotBoundInCall(p.0.clone()));52		};53		let val = if tailstrict {54			resolved_lazy_val!(evaluate(ctx, expr)?)55		} else {56			lazy_val!(closure!(clone ctx, clone expr, ||evaluate(ctx.clone(), &expr)))57		};58		out.insert(p.0.clone(), val);59	}6061	Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))62}6364pub fn parse_function_call_map(65	ctx: Context,66	body_ctx: Option<Context>,67	params: &ParamsDesc,68	args: &HashMap<Rc<str>, Val>,69	tailstrict: bool,70) -> Result<Context> {71	let mut out = FxHashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());72	let mut positioned_args = vec![None; params.0.len()];73	for (name, val) in args.iter() {74		let idx = params75			.iter()76			.position(|p| *p.0 == **name)77			.ok_or_else(|| UnknownFunctionParameter((&name as &str).to_owned()))?;7879		if idx >= params.len() {80			throw!(TooManyArgsFunctionHas(params.len()));81		}82		if positioned_args[idx].is_some() {83			throw!(BindingParameterASecondTime(params[idx].0.clone()));84		}85		positioned_args[idx] = Some(val.clone());86	}87	// Fill defaults88	for (id, p) in params.iter().enumerate() {89		let val = if let Some(arg) = positioned_args[id].take() {90			resolved_lazy_val!(arg)91		} else if let Some(default) = &p.1 {92			if tailstrict {93				resolved_lazy_val!(evaluate(94					body_ctx.clone().expect(NO_DEFAULT_CONTEXT),95					default96				)?)97			} else {98				let body_ctx = body_ctx.clone();99				let default = default.clone();100				lazy_val!(move || {101					evaluate(body_ctx.clone().expect(NO_DEFAULT_CONTEXT), &default)102				})103			}104		} else {105			throw!(FunctionParameterNotBoundInCall(p.0.clone()));106		};107		out.insert(p.0.clone(), val);108	}109110	Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))111}112113pub(crate) fn place_args(114	ctx: Context,115	body_ctx: Option<Context>,116	params: &ParamsDesc,117	args: &[Val],118) -> Result<Context> {119	let mut out = FxHashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());120	let mut positioned_args = vec![None; params.0.len()];121	for (id, arg) in args.iter().enumerate() {122		if id >= params.len() {123			throw!(TooManyArgsFunctionHas(params.len()));124		}125		positioned_args[id] = Some(arg);126	}127	// Fill defaults128	for (id, p) in params.iter().enumerate() {129		let val = if let Some(arg) = &positioned_args[id] {130			(*arg).clone()131		} else if let Some(default) = &p.1 {132			evaluate(ctx.clone(), default)?133		} else {134			throw!(FunctionParameterNotBoundInCall(p.0.clone()));135		};136		out.insert(p.0.clone(), resolved_lazy_val!(val));137	}138139	Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))140}141142#[macro_export]143macro_rules! parse_args {144	($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [145		$($id: expr, $name: ident $(: [$($p: path)|+] $(!! $a: path)?)?, $nt: expr);+ $(;)?146	], $handler:block) => {{147		use crate::{throw, error::Error::*};148		let args = $args;149		if args.len() > $total_args {150			throw!(TooManyArgsFunctionHas($total_args));151		}152		$(153			if args.len() <= $id {154				throw!(FunctionParameterNotBoundInCall(stringify!($name).into()));155			}156			let $name = &args[$id];157			if $name.0.is_some() {158				if $name.0.as_ref().unwrap() != stringify!($name) {159					throw!(IntristicArgumentReorderingIsNotSupportedYet);160				}161			}162			let $name = evaluate($ctx.clone(), &$name.1)?;163			$(164				match $name {165					$($p(_))|+ => {},166					_ => throw!(TypeMismatch(167						concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"),168						$nt, $name.value_type()?169					)),170				};171				$(172					let $name = match $name {173						$a(v) => v,174						_ =>throw!(TypeMismatch(concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"), $nt, $name.value_type()?)),175					};176				)*177			)*178		)+179		($handler as crate::Result<_>)180	}};181}182183#[test]184fn test() -> Result<()> {185	use crate::val::ValType;186	use jrsonnet_parser::*;187	let state = crate::EvaluationState::default();188	let evaluator = state.with_stdlib();189	let ctx = evaluator.create_default_context()?;190	evaluator.run_in_state(|| {191		parse_args!(ctx, "test", ArgsDesc(vec![192			Arg(None, el!(Expr::Num(2.0))),193			Arg(Some("b".into()), el!(Expr::Num(1.0))),194		]), 2, [195			0, a: [Val::Num]!!Val::Num, vec![ValType::Num];196			1, b: [Val::Num]!!Val::Num, vec![ValType::Num];197		], {198			assert!((a - 2.0).abs() <= f64::EPSILON);199			assert!((b - 1.0).abs() <= f64::EPSILON);200			Ok(())201		})202		.unwrap();203		Ok(())204	})205}
modifiedcrates/jrsonnet-evaluator/src/map.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/map.rs
+++ b/crates/jrsonnet-evaluator/src/map.rs
@@ -1,16 +1,17 @@
-use std::{borrow::Borrow, collections::HashMap, hash::Hash, rc::Rc};
+use rustc_hash::FxHashMap;
+use std::{borrow::Borrow, hash::Hash, rc::Rc};
 
 #[derive(Default, Debug)]
 struct LayeredHashMapInternals<K: Hash, V> {
 	parent: Option<LayeredHashMap<K, V>>,
-	current: HashMap<K, V>,
+	current: FxHashMap<K, V>,
 }
 
 #[derive(Debug)]
 pub struct LayeredHashMap<K: Hash, V>(Rc<LayeredHashMapInternals<K, V>>);
 
 impl<K: Hash + Eq, V> LayeredHashMap<K, V> {
-	pub fn extend(self, new_layer: HashMap<K, V>) -> Self {
+	pub fn extend(self, new_layer: FxHashMap<K, V>) -> Self {
 		match Rc::try_unwrap(self.0) {
 			Ok(mut map) => {
 				map.current.extend(new_layer);
@@ -45,7 +46,7 @@
 	fn default() -> Self {
 		LayeredHashMap(Rc::new(LayeredHashMapInternals {
 			parent: None,
-			current: HashMap::new(),
+			current: FxHashMap::default(),
 		}))
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -71,15 +71,16 @@
 	pub body: LocExpr,
 }
 
-#[derive(Debug, Clone)]
+#[derive(Debug)]
 pub enum FuncVal {
 	/// Plain function implemented in jsonnet
-	Normal(Rc<FuncDesc>),
+	Normal(FuncDesc),
 	/// Standard library function
 	Intristic(Rc<str>, Rc<str>),
 	/// Library functions implemented in native
 	NativeExt(Rc<str>, Rc<NativeCallback>),
 }
+
 impl PartialEq for FuncVal {
 	fn eq(&self, other: &Self) -> bool {
 		match (self, other) {
@@ -212,8 +213,9 @@
 	Lazy(LazyVal),
 	Arr(Rc<Vec<Val>>),
 	Obj(ObjValue),
-	Func(FuncVal),
+	Func(Rc<FuncVal>),
 }
+
 macro_rules! matches_unwrap {
 	($e: expr, $p: pat, $r: expr) => {
 		match $e {