use criterion::{black_box, criterion_group, criterion_main, Criterion};
use truecalc_core::eval::{evaluate_expr, Context, EvalCtx, EvalOp, Registry, Span};
use truecalc_core::types::Value;
use truecalc_core::Engine;
const FORMULA: &str = "=((1+2)*(3-4)+(5*6)/(7+8))*((9-1)+(2*3))-(4+5)*(6-7)+(8*9)/(1+1)";
fn node_count(expr: &truecalc_core::Expr, registry: &Registry) -> usize {
let mut n = 0usize;
let mut count = |_op: EvalOp<'_>, _span: Span, _v: &Value| n += 1;
let mut ctx = EvalCtx::new(Context::empty(), registry);
ctx.hook = Some(&mut count);
let _ = evaluate_expr(expr, &mut ctx);
n
}
fn bench_hook(c: &mut Criterion) {
let engine = Engine::sheets();
let expr = engine.parse(FORMULA).expect("valid formula");
let registry = Registry::new();
let nodes = node_count(&expr, ®istry);
println!("eval_hook bench: {nodes} nodes evaluated per iteration");
println!("size_of::<Span>() = {} bytes", std::mem::size_of::<Span>());
let mut group = c.benchmark_group("eval_hook");
group.bench_function("none", |b| {
b.iter(|| {
let mut ctx = EvalCtx::new(Context::empty(), ®istry);
black_box(evaluate_expr(black_box(&expr), &mut ctx))
});
});
group.bench_function("counter", |b| {
b.iter(|| {
let mut n = 0usize;
let mut count = |_op: EvalOp<'_>, _span: Span, _v: &Value| n += 1;
let mut ctx = EvalCtx::new(Context::empty(), ®istry);
ctx.hook = Some(&mut count);
let out = evaluate_expr(black_box(&expr), &mut ctx);
black_box(n);
black_box(out)
});
});
group.bench_function("counter_span", |b| {
b.iter(|| {
let mut n = 0usize;
let mut span_acc = 0usize;
let mut count = |_op: EvalOp<'_>, span: Span, _v: &Value| {
n += 1;
span_acc = span_acc.wrapping_add(span.offset).wrapping_add(span.length);
};
let mut ctx = EvalCtx::new(Context::empty(), ®istry);
ctx.hook = Some(&mut count);
let out = evaluate_expr(black_box(&expr), &mut ctx);
black_box(n);
black_box(span_acc);
black_box(out)
});
});
group.bench_function("collect", |b| {
b.iter(|| {
let mut events: Vec<(u8, Span, Value)> = Vec::new();
let mut collect = |op: EvalOp<'_>, span: Span, v: &Value| {
let tag = match op {
EvalOp::Number => 0,
EvalOp::Text => 1,
EvalOp::Bool => 2,
EvalOp::Variable(_) => 3,
EvalOp::Reference => 4,
EvalOp::UnaryOp(_) => 5,
EvalOp::BinaryOp(_) => 6,
EvalOp::Array => 7,
EvalOp::Apply => 8,
EvalOp::FunctionCall(_) => 9,
EvalOp::Error(_) => 10,
};
events.push((tag, span, v.clone()));
};
let mut ctx = EvalCtx::new(Context::empty(), ®istry);
ctx.hook = Some(&mut collect);
let out = evaluate_expr(black_box(&expr), &mut ctx);
black_box(&events);
black_box(out)
});
});
group.finish();
}
criterion_group!(benches, bench_hook);
criterion_main!(benches);