difftreelog
perf use fxhash
in: master
8 files changed
Cargo.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"
crates/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]
crates/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, [
crates/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())?);
}
crates/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))
}
crates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth1use 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}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}crates/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(),
}))
}
}
crates/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 {