use std::hint::black_box;
use std::path::PathBuf;
use std::sync::Arc;
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use eure::document::{EureDocument, cst_to_document};
use eure::query::{TextFile, TextFileContent, build_runtime};
use eure_json::{Config, EureToJson, document_to_value};
use eure_parol::parse_tolerant;
use eure_tree::prelude::Cst;
use query_flow::DurabilityLevel;
fn small_input() -> &'static str {
r#"name = "Alice"
age = 30
active = true
"#
}
fn medium_input() -> &'static str {
r#"$schema = "user.schema.eure"
name = "Alice"
email = "alice@example.com"
age = 30
active = true
profile {
bio = "Software engineer"
location = "Tokyo"
website = "https://alice.dev"
}
tags[] = "rust"
tags[] = "typescript"
tags[] = "python"
contacts[] {
type = "email"
value = "alice@work.com"
primary = true
}
contacts[] {
type = "phone"
value = "+1-555-1234"
primary = false
}
metadata {
created_at = "2024-01-15"
updated_at = "2024-06-20"
version = 3
}
"#
}
fn large_input() -> String {
let mut s = String::new();
for i in 0..100 {
s.push_str(&format!(
r#"items[] {{
id = {i}
name = "Item {i}"
price = {price}
in_stock = {in_stock}
tags = ["tag-a", "tag-b", "tag-c"]
}}
"#,
i = i,
price = (i as f64) * 9.99,
in_stock = i % 2 == 0
));
}
s
}
fn manual_pipeline(text: &str) -> serde_json::Value {
let cst = parse_tolerant(text).cst();
let doc = cst_to_document(text, &cst).expect("document construction should succeed");
document_to_value(&doc, &Config::default()).expect("json conversion should succeed")
}
fn manual_parse_only(text: &str) -> Cst {
parse_tolerant(text).cst()
}
fn manual_document_only(text: &str, cst: &Cst) -> EureDocument {
cst_to_document(text, cst).expect("document construction should succeed")
}
fn manual_json_only(doc: &EureDocument) -> serde_json::Value {
document_to_value(doc, &Config::default()).expect("json conversion should succeed")
}
fn query_pipeline(text: &str) -> Arc<serde_json::Value> {
let runtime = build_runtime();
let file = TextFile::from_path(PathBuf::from("bench.eure"));
runtime.resolve_asset(
file.clone(),
TextFileContent(text.to_string()),
DurabilityLevel::Static,
);
runtime
.query(EureToJson::new(file, Config::default()))
.expect("query should succeed")
}
fn query_pipeline_with_runtime(
runtime: &query_flow::QueryRuntime,
file: &TextFile,
) -> Arc<serde_json::Value> {
runtime
.query(EureToJson::new(file.clone(), Config::default()))
.expect("query should succeed")
}
fn bench_full_pipeline(c: &mut Criterion) {
let mut group = c.benchmark_group("full_pipeline");
let small = small_input();
group.bench_with_input(BenchmarkId::new("manual", "small"), &small, |b, input| {
b.iter(|| manual_pipeline(black_box(input)))
});
group.bench_with_input(BenchmarkId::new("query", "small"), &small, |b, input| {
b.iter(|| query_pipeline(black_box(input)))
});
let medium = medium_input();
group.bench_with_input(BenchmarkId::new("manual", "medium"), &medium, |b, input| {
b.iter(|| manual_pipeline(black_box(input)))
});
group.bench_with_input(BenchmarkId::new("query", "medium"), &medium, |b, input| {
b.iter(|| query_pipeline(black_box(input)))
});
let large = large_input();
group.bench_with_input(
BenchmarkId::new("manual", "large"),
large.as_str(),
|b, input| b.iter(|| manual_pipeline(black_box(input))),
);
group.bench_with_input(
BenchmarkId::new("query", "large"),
large.as_str(),
|b, input| b.iter(|| query_pipeline(black_box(input))),
);
group.finish();
}
fn bench_query_reuse(c: &mut Criterion) {
let mut group = c.benchmark_group("query_reuse");
let small = small_input();
let runtime = build_runtime();
let file = TextFile::from_path(PathBuf::from("bench.eure"));
runtime.resolve_asset(
file.clone(),
TextFileContent(small.to_string()),
DurabilityLevel::Static,
);
group.bench_function("query_with_prebuilt_runtime/small", |b| {
b.iter(|| query_pipeline_with_runtime(black_box(&runtime), black_box(&file)))
});
group.bench_function("manual/small", |b| {
b.iter(|| manual_pipeline(black_box(small)))
});
group.finish();
}
fn bench_parse_phase(c: &mut Criterion) {
let mut group = c.benchmark_group("parse_phase");
let small = small_input();
let medium = medium_input();
let large = large_input();
group.bench_with_input(BenchmarkId::new("manual", "small"), &small, |b, input| {
b.iter(|| manual_parse_only(black_box(input)))
});
group.bench_with_input(BenchmarkId::new("manual", "medium"), &medium, |b, input| {
b.iter(|| manual_parse_only(black_box(input)))
});
group.bench_with_input(
BenchmarkId::new("manual", "large"),
large.as_str(),
|b, input| b.iter(|| manual_parse_only(black_box(input))),
);
group.finish();
}
fn bench_document_phase(c: &mut Criterion) {
let mut group = c.benchmark_group("document_phase");
let small = small_input();
let small_cst = manual_parse_only(small);
let medium = medium_input();
let medium_cst = manual_parse_only(medium);
let large = large_input();
let large_cst = manual_parse_only(&large);
group.bench_function("manual/small", |b| {
b.iter(|| manual_document_only(black_box(small), black_box(&small_cst)))
});
group.bench_function("manual/medium", |b| {
b.iter(|| manual_document_only(black_box(medium), black_box(&medium_cst)))
});
group.bench_function("manual/large", |b| {
b.iter(|| manual_document_only(black_box(&large), black_box(&large_cst)))
});
group.finish();
}
fn bench_json_phase(c: &mut Criterion) {
let mut group = c.benchmark_group("json_phase");
let small = small_input();
let small_cst = manual_parse_only(small);
let small_doc = manual_document_only(small, &small_cst);
let medium = medium_input();
let medium_cst = manual_parse_only(medium);
let medium_doc = manual_document_only(medium, &medium_cst);
let large = large_input();
let large_cst = manual_parse_only(&large);
let large_doc = manual_document_only(&large, &large_cst);
group.bench_function("manual/small", |b| {
b.iter(|| manual_json_only(black_box(&small_doc)))
});
group.bench_function("manual/medium", |b| {
b.iter(|| manual_json_only(black_box(&medium_doc)))
});
group.bench_function("manual/large", |b| {
b.iter(|| manual_json_only(black_box(&large_doc)))
});
group.finish();
}
criterion_group!(
benches,
bench_full_pipeline,
bench_query_reuse,
bench_parse_phase,
bench_document_phase,
bench_json_phase,
);
criterion_main!(benches);