use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
#[path = "../src/conversion.rs"]
mod conversion;
use conversion::json_to_cel_value;
fn bench_compilation(c: &mut Criterion) {
let mut group = c.benchmark_group("compilation");
let expressions = [
("trivial", "true"),
("simple_comparison", "ctx.x > 10"),
("simple_string", "ctx.name == \"test\""),
("medium_logic", "ctx.a > 0 && ctx.b < 100"),
(
"medium_string_ops",
"ctx.email.contains(\"@\") && ctx.email.size() > 5",
),
(
"complex_nested",
"ctx.user.profile.settings.enabled && ctx.score > 50",
),
(
"complex_multi_cond",
"ctx.a > 0 && ctx.b < 100 && ctx.c == \"active\" && ctx.d != null",
),
(
"complex_list",
"\"admin\" in ctx.roles || \"superuser\" in ctx.roles",
),
(
"very_complex",
"(ctx.price * ctx.quantity) * (1.0 - ctx.discount / 100.0) > ctx.minimum",
),
];
for (name, expr) in expressions {
group.bench_with_input(BenchmarkId::new("compile", name), &expr, |b, expr| {
b.iter(|| cel_interpreter::Program::compile(black_box(*expr)).unwrap())
});
}
group.finish();
}
fn bench_evaluation(c: &mut Criterion) {
let mut group = c.benchmark_group("evaluation");
let simple_program = cel_interpreter::Program::compile("ctx.x > 50").unwrap();
let medium_program =
cel_interpreter::Program::compile("ctx.a > 0 && ctx.b < 100 && ctx.enabled").unwrap();
let complex_program = cel_interpreter::Program::compile(
"ctx.user.profile.settings.enabled && ctx.score > 50 && \"admin\" in ctx.roles",
)
.unwrap();
let math_program = cel_interpreter::Program::compile(
"(ctx.price * ctx.quantity) * (1.0 - ctx.discount / 100.0) > ctx.minimum",
)
.unwrap();
let small_context = serde_json::json!({
"x": 75
});
let medium_context = serde_json::json!({
"a": 50,
"b": 25,
"enabled": true,
"email": "test@example.com"
});
let large_context = serde_json::json!({
"user": {
"id": 1,
"name": "Test User",
"profile": {
"settings": { "enabled": true },
"verified": true
}
},
"roles": ["user", "admin"],
"status": "active",
"score": 85,
"price": 100.0,
"quantity": 5,
"discount": 10.0,
"minimum": 400.0
});
group.bench_function("eval_simple", |b| {
let ctx_val = json_to_cel_value(&small_context);
b.iter(|| {
let mut context = cel_interpreter::Context::default();
let _ = context.add_variable("ctx", ctx_val.clone());
simple_program.execute(black_box(&context)).unwrap()
})
});
group.bench_function("eval_medium", |b| {
let ctx_val = json_to_cel_value(&medium_context);
b.iter(|| {
let mut context = cel_interpreter::Context::default();
let _ = context.add_variable("ctx", ctx_val.clone());
medium_program.execute(black_box(&context)).unwrap()
})
});
group.bench_function("eval_complex", |b| {
let ctx_val = json_to_cel_value(&large_context);
b.iter(|| {
let mut context = cel_interpreter::Context::default();
let _ = context.add_variable("ctx", ctx_val.clone());
complex_program.execute(black_box(&context)).unwrap()
})
});
group.bench_function("eval_math", |b| {
let ctx_val = json_to_cel_value(&large_context);
b.iter(|| {
let mut context = cel_interpreter::Context::default();
let _ = context.add_variable("ctx", ctx_val.clone());
math_program.execute(black_box(&context)).unwrap()
})
});
group.finish();
}
fn bench_json_conversion(c: &mut Criterion) {
let mut group = c.benchmark_group("json_conversion");
let small_json = serde_json::json!({
"x": 42,
"name": "test"
});
let medium_json = serde_json::json!({
"a": 50,
"b": 25,
"enabled": true,
"email": "test@example.com",
"tags": ["foo", "bar", "baz"]
});
let large_json = serde_json::json!({
"user": {
"id": 1,
"name": "Test User",
"profile": {
"settings": { "enabled": true, "theme": "dark", "notifications": true },
"verified": true,
"metadata": { "created": "2024-01-01", "updated": "2024-06-15" }
}
},
"roles": ["user", "admin", "moderator"],
"permissions": ["read", "write", "delete", "admin"],
"data": {
"items": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
"nested": { "deep": { "value": 42 } }
}
});
group.bench_function("convert_small", |b| {
b.iter(|| json_to_cel_value(black_box(&small_json)))
});
group.bench_function("convert_medium", |b| {
b.iter(|| json_to_cel_value(black_box(&medium_json)))
});
group.bench_function("convert_large", |b| {
b.iter(|| json_to_cel_value(black_box(&large_json)))
});
group.finish();
}
fn bench_end_to_end(c: &mut Criterion) {
let mut group = c.benchmark_group("end_to_end");
let expressions = [
("simple", "ctx.x > 50"),
("medium", "ctx.a > 0 && ctx.b < 100 && ctx.enabled"),
(
"complex",
"ctx.user.profile.settings.enabled && ctx.score > 50",
),
];
let context_json = serde_json::json!({
"x": 75,
"a": 50,
"b": 25,
"enabled": true,
"user": {
"profile": {
"settings": { "enabled": true }
}
},
"score": 85
});
for (name, expr) in expressions {
group.bench_with_input(
BenchmarkId::new("full_pipeline", name),
&(expr, &context_json),
|b, (expr, json)| {
b.iter(|| {
let program = cel_interpreter::Program::compile(black_box(*expr)).unwrap();
let ctx_val = json_to_cel_value(black_box(*json));
let mut context = cel_interpreter::Context::default();
let _ = context.add_variable("ctx", ctx_val);
program.execute(&context).unwrap()
})
},
);
}
group.finish();
}
fn bench_throughput(c: &mut Criterion) {
let mut group = c.benchmark_group("throughput");
let program = cel_interpreter::Program::compile("ctx.value > 50 && ctx.active").unwrap();
let contexts: Vec<serde_json::Value> = (0..1000)
.map(|i| {
serde_json::json!({
"value": i % 100,
"active": i % 3 != 0
})
})
.collect();
let cel_contexts: Vec<cel_interpreter::Value> =
contexts.iter().map(json_to_cel_value).collect();
group.throughput(Throughput::Elements(1000));
group.bench_function("eval_1000_contexts", |b| {
b.iter(|| {
let mut matches = 0;
for ctx_val in &cel_contexts {
let mut context = cel_interpreter::Context::default();
let _ = context.add_variable("ctx", ctx_val.clone());
if let cel_interpreter::Value::Bool(true) =
program.execute(black_box(&context)).unwrap()
{
matches += 1;
}
}
matches
})
});
group.finish();
}
fn bench_complexity_scaling(c: &mut Criterion) {
let mut group = c.benchmark_group("complexity_scaling");
let expressions = [
("1_condition", "ctx.a > 0"),
("2_conditions", "ctx.a > 0 && ctx.b > 0"),
("3_conditions", "ctx.a > 0 && ctx.b > 0 && ctx.c > 0"),
(
"4_conditions",
"ctx.a > 0 && ctx.b > 0 && ctx.c > 0 && ctx.d > 0",
),
(
"5_conditions",
"ctx.a > 0 && ctx.b > 0 && ctx.c > 0 && ctx.d > 0 && ctx.e > 0",
),
];
let context = serde_json::json!({
"a": 10, "b": 20, "c": 30, "d": 40, "e": 50
});
let ctx_val = json_to_cel_value(&context);
for (name, expr) in expressions {
let program = cel_interpreter::Program::compile(expr).unwrap();
group.bench_with_input(BenchmarkId::new("eval", name), &program, |b, program| {
b.iter(|| {
let mut context = cel_interpreter::Context::default();
let _ = context.add_variable("ctx", ctx_val.clone());
program.execute(black_box(&context)).unwrap()
})
});
}
group.finish();
}
criterion_group!(
benches,
bench_compilation,
bench_evaluation,
bench_json_conversion,
bench_end_to_end,
bench_throughput,
bench_complexity_scaling,
);
criterion_main!(benches);