From b1d11cbae1de664e7bfa83964613099432f2aad6 Mon Sep 17 00:00:00 2001 From: Panagiotis Date: Mon, 2 Dec 2024 12:38:44 +0200 Subject: [PATCH] bench: initial results for jsonlikehelper --- benches/jaq.rs | 155 +++++++++++++++++++++++++++----- tailcall-template/src/jq/jq.rs | 36 ++++++-- tailcall-template/src/jq/mod.rs | 2 +- 3 files changed, 166 insertions(+), 27 deletions(-) diff --git a/benches/jaq.rs b/benches/jaq.rs index ad88ef8..b52071a 100644 --- a/benches/jaq.rs +++ b/benches/jaq.rs @@ -1,9 +1,13 @@ -use jaq_json::Val; -use serde_json::json; use criterion::{criterion_group, criterion_main, Criterion}; -use tailcall_template::mustache::{Mustache, Segment}; use jaq_core::{load, Ctx, Native, RcIter}; +use jaq_json::Val; use load::{Arena, File, Loader}; +use serde_json::json; +use tailcall_template::{ + self, + jq::jq::JsonLikeHelper, + mustache::{Mustache, Segment}, +}; criterion_group!(benches, criterion_benchmark); criterion_main!(benches); @@ -18,10 +22,15 @@ pub fn criterion_benchmark(c: &mut Criterion) { Segment::Literal("Value: ".to_string()), Segment::Expression(vec!["key".to_string()]), ]); - c.bench_function("basic_mustache", |b| b.iter(|| bench_mustache(&data, &mustache, &expected))); + c.bench_function("basic_mustache", |b| { + b.iter(|| bench_mustache(&data, &mustache, &expected)) + }); } { - let program = File { code: "\"Value: \" + .key", path: () }; + let program = File { + code: "\"Value: \" + .key", + path: (), + }; // start out only from core filters, // which do not include filters in the standard library @@ -33,11 +42,39 @@ pub fn criterion_benchmark(c: &mut Criterion) { let modules = loader.load(&arena, program).unwrap(); // compile the filter - let filter: jaq_core::Filter> = jaq_core::Compiler::<_, Native<_>>::default() - .compile(modules) - .unwrap(); + let filter: jaq_core::Filter> = + jaq_core::Compiler::<_, Native<_>>::default() + .compile(modules) + .unwrap(); - c.bench_function("basic_jq", |b| b.iter(|| bench_jq(&data, &filter, &expected))); + c.bench_function("basic_jq", |b| { + b.iter(|| bench_jq(&data, &filter, &expected)) + }); + } + { + let program = File { + code: "\"Value: \" + .key", + path: (), + }; + + // start out only from core filters, + // which do not include filters in the standard library + // such as `map`, `select` etc. + let loader = Loader::new([]); + let arena = Arena::default(); + + // parse the filter + let modules = loader.load(&arena, program).unwrap(); + + // compile the filter + let filter: jaq_core::Filter>> = + jaq_core::Compiler::<_, Native<_>>::default() + .compile(modules) + .unwrap(); + + c.bench_function("basic_jsonlike", |b| { + b.iter(|| bench_jsonlike(&data, &filter, &expected)) + }); } } // COMPLEX SCENARIO @@ -51,19 +88,44 @@ pub fn criterion_benchmark(c: &mut Criterion) { Segment::Literal(", Age: ".to_string()), Segment::Expression(vec!["age".to_string()]), ]); - c.bench_function("complex_mustache", |b| b.iter(|| bench_mustache(&data, &mustache, &expected))); + c.bench_function("complex_mustache", |b| { + b.iter(|| bench_mustache(&data, &mustache, &expected)) + }); } { - let program = File { code: "\"User: \" + .user + \", Age: \" + (.age | tostring)", path: () }; - let loader = Loader::new(jaq_std::defs()); + let program = File { + code: "\"User: \" + .user + \", Age: \" + (.age | tostring)", + path: (), + }; + let defs = jaq_core::load::parse("def tostring: \"\\(.)\";", |p| p.defs()).unwrap(); + let loader = Loader::new(defs); let arena = Arena::default(); let modules = loader.load(&arena, program).unwrap(); let filter = jaq_core::Compiler::<_, Native<_>>::default() - .with_funs(jaq_std::funs()) .compile(modules) .unwrap(); - c.bench_function("complex_jq", |b| b.iter(|| bench_jq(&data, &filter, &expected))); + c.bench_function("complex_jq", |b| { + b.iter(|| bench_jq(&data, &filter, &expected)) + }); + } + { + let program = File { + code: "\"User: \" + .user + \", Age: \" + (.age | tostring)", + path: (), + }; + + let defs = jaq_core::load::parse("def tostring: \"\\(.)\";", |p| p.defs()).unwrap(); + let loader = Loader::new(defs); + let arena = Arena::default(); + let modules = loader.load(&arena, program).unwrap(); + let filter = jaq_core::Compiler::<_, Native<_>>::default() + .compile(modules) + .unwrap(); + + c.bench_function("complex_jsonlike", |b| { + b.iter(|| bench_jsonlike(&data, &filter, &expected)) + }); } } // NESTED SCENARIO @@ -86,25 +148,57 @@ pub fn criterion_benchmark(c: &mut Criterion) { Segment::Literal("User: ".to_string()), Segment::Expression(vec!["user".to_string(), "name".to_string()]), Segment::Literal(", Age: ".to_string()), - Segment::Expression(vec!["user".to_string(), "details".to_string(), "age".to_string()]), + Segment::Expression(vec![ + "user".to_string(), + "details".to_string(), + "age".to_string(), + ]), Segment::Literal(", Location: ".to_string()), - Segment::Expression(vec!["user".to_string(), "details".to_string(), "location".to_string(), "city".to_string()]), + Segment::Expression(vec![ + "user".to_string(), + "details".to_string(), + "location".to_string(), + "city".to_string(), + ]), Segment::Literal(", Country: ".to_string()), - Segment::Expression(vec!["user".to_string(), "details".to_string(), "location".to_string(), "country".to_string()]), + Segment::Expression(vec![ + "user".to_string(), + "details".to_string(), + "location".to_string(), + "country".to_string(), + ]), ]); - c.bench_function("nested_mustache", |b| b.iter(|| bench_mustache(&data, &mustache, &expected))); + c.bench_function("nested_mustache", |b| { + b.iter(|| bench_mustache(&data, &mustache, &expected)) + }); + } + { + let program = File { code: "\"User: \" + .user.name + \", Age: \" + (.user.details.age | tostring) + \", Location: \" + .user.details.location.city + \", Country: \" + .user.details.location.country", path: () }; + let defs = jaq_core::load::parse("def tostring: \"\\(.)\";", |p| p.defs()).unwrap(); + let loader = Loader::new(defs); + let arena = Arena::default(); + let modules = loader.load(&arena, program).unwrap(); + let filter = jaq_core::Compiler::<_, Native<_>>::default() + .compile(modules) + .unwrap(); + + c.bench_function("nested_jq", |b| { + b.iter(|| bench_jq(&data, &filter, &expected)) + }); } { let program = File { code: "\"User: \" + .user.name + \", Age: \" + (.user.details.age | tostring) + \", Location: \" + .user.details.location.city + \", Country: \" + .user.details.location.country", path: () }; - let loader = Loader::new(jaq_std::defs()); + let defs = jaq_core::load::parse("def tostring: \"\\(.)\";", |p| p.defs()).unwrap(); + let loader = Loader::new(defs); let arena = Arena::default(); let modules = loader.load(&arena, program).unwrap(); let filter = jaq_core::Compiler::<_, Native<_>>::default() - .with_funs(jaq_std::funs()) .compile(modules) .unwrap(); - c.bench_function("nested_jq", |b| b.iter(|| bench_jq(&data, &filter, &expected))); + c.bench_function("nested_jsonlike", |b| { + b.iter(|| bench_jsonlike(&data, &filter, &expected)) + }); } } } @@ -123,3 +217,22 @@ fn bench_jq(data: &serde_json::Value, filter: &jaq_core::Filter>, ex assert_eq!(out.next(), Some(Ok(Val::from(expected.to_string())))); assert_eq!(out.next(), None); } + +fn bench_jsonlike( + data: &serde_json::Value, + filter: &jaq_core::Filter>>, + expected: &str, +) { + let inputs = RcIter::new(core::iter::empty()); + + // iterator over the output values + let mut out = filter.run((Ctx::new([], &inputs), JsonLikeHelper(data.clone()))); + + assert_eq!( + out.next(), + Some(Ok(JsonLikeHelper(serde_json::Value::String( + expected.to_string() + )))) + ); + assert_eq!(out.next(), None); +} diff --git a/tailcall-template/src/jq/jq.rs b/tailcall-template/src/jq/jq.rs index 7a6ed0e..7726df2 100644 --- a/tailcall-template/src/jq/jq.rs +++ b/tailcall-template/src/jq/jq.rs @@ -1,10 +1,10 @@ -use std::ops::Deref; +use std::{borrow::Cow, ops::Deref}; use jaq_core::ValR; use crate::jsonlike::{JsonLike, JsonObjectLike}; -#[derive(Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct JsonLikeHelper< A: for<'a> JsonLike<'a> + std::fmt::Display + std::clone::Clone + std::cmp::PartialEq + 'static, >(pub A); @@ -384,7 +384,7 @@ where A: for<'a> JsonLike<'a> + std::fmt::Display + std::clone::Clone + std::cmp::PartialEq + 'static, { fn from(value: String) -> Self { - todo!() + JsonLikeHelper(JsonLike::string(Cow::Owned(value))) } } @@ -402,8 +402,34 @@ where A: for<'a> JsonLike<'a> + std::fmt::Display + std::clone::Clone + std::cmp::PartialEq + 'static, { type Output = ValR; - fn add(self, rhs: Self) -> Self::Output { - todo!() + fn add(mut self, rhs: Self) -> Self::Output { + if self.0.is_null() && rhs.0.is_null() { + return Ok(self); + } + + if let (Some(l), Some(r)) = (self.0.as_f64(), &rhs.0.as_f64()) { + return Ok(JsonLikeHelper(A::number_f64(l + r))); + } + + if let (Some(l), Some(r)) = (self.0.as_str(), &rhs.0.as_str()) { + let mut result = String::from(l); + result.push_str(r); + return Ok(JsonLikeHelper(A::string(result.into()))); + } + + if let (Some(l), Some(r)) = (self.0.as_array_mut(), &rhs.0.as_array()) { + l.extend(r.iter().cloned()); + return Ok(self); + } + + if let (Some(l), Some(r)) = (self.0.as_object_mut(), &rhs.0.as_object()) { + for (k, v) in r.iter() { + l.insert_key(k, v.clone()); + } + return Ok(self); + } + + Err(jaq_core::Error::str("Cannot add values of different types")) } } diff --git a/tailcall-template/src/jq/mod.rs b/tailcall-template/src/jq/mod.rs index 5a64fcf..24dc20b 100644 --- a/tailcall-template/src/jq/mod.rs +++ b/tailcall-template/src/jq/mod.rs @@ -1 +1 @@ -mod jq; +pub mod jq;