use std::time::Duration;
use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
use ccalc_engine::env::{Env, Value};
use ccalc_engine::eval::{Base, FormatMode};
use ccalc_engine::exec::{self, exec_stmts};
use ccalc_engine::io::IoContext;
use ccalc_engine::parser::parse_stmts;
fn new_env() -> Env {
let mut env = Env::new();
env.insert("ans".to_string(), Value::Scalar(0.0));
env.insert("i".to_string(), Value::Complex(0.0, 1.0));
env.insert("j".to_string(), Value::Complex(0.0, 1.0));
env
}
fn run(code: &str, env: &mut Env) {
let stmts = parse_stmts(code).unwrap_or_else(|e| panic!("parse failed: {e}\ncode: {code}"));
let mut io = IoContext::new();
exec_stmts(&stmts, env, &mut io, &FormatMode::Short, Base::Dec, true)
.unwrap_or_else(|e| panic!("exec failed: {e}\ncode: {code}"));
}
fn exec_checked(stmts: &[(ccalc_engine::parser::Stmt, bool)], env: &mut Env, io: &mut IoContext) {
exec_stmts(stmts, env, io, &FormatMode::Short, Base::Dec, true)
.unwrap_or_else(|e| panic!("bench exec failed: {e}"));
}
fn bench_scalar_ops(c: &mut Criterion) {
let stmts = parse_stmts("sum(1:1000000)").expect("parse");
let mut env = new_env();
let mut io = IoContext::new();
c.bench_function("scalar_ops_sum_1M", |b| {
b.iter(|| exec_checked(black_box(&stmts), &mut env, &mut io))
});
}
fn bench_fib(c: &mut Criterion) {
exec::init();
let fib_def = "\
function result = fib(n)
if n <= 1
result = n;
else
result = fib(n-1) + fib(n-2);
end
end";
let mut env = new_env();
run(fib_def, &mut env);
let stmts = parse_stmts("fib(30)").expect("parse");
let mut io = IoContext::new();
let mut group = c.benchmark_group("fib");
group.sample_size(10);
group.measurement_time(Duration::from_secs(90));
group.bench_function("fib_30", |b| {
b.iter(|| exec_checked(black_box(&stmts), &mut env, &mut io))
});
group.finish();
}
fn bench_loop_throughput(c: &mut Criterion) {
let stmts = parse_stmts("s = 0\nfor k = 1:10000\n s += k\nend").expect("parse");
let mut env = new_env();
let mut io = IoContext::new();
c.bench_function("loop_10k", |b| {
b.iter(|| exec_checked(black_box(&stmts), &mut env, &mut io))
});
}
fn bench_matmul(c: &mut Criterion) {
let mut group = c.benchmark_group("matmul");
group.measurement_time(Duration::from_secs(15));
group.sample_size(20);
for &n in &[100usize, 500, 1000] {
let mut env = new_env();
let mut io = IoContext::new();
run(&format!("A = ones({n}, {n});"), &mut env);
let stmts = parse_stmts("A * A").expect("parse");
group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, _| {
b.iter(|| exec_checked(black_box(&stmts), &mut env, &mut io))
});
}
group.finish();
}
fn bench_inv(c: &mut Criterion) {
let mut group = c.benchmark_group("inv");
group.measurement_time(Duration::from_secs(15));
group.sample_size(20);
for &n in &[100usize, 500] {
let mut env = new_env();
let mut io = IoContext::new();
run(&format!("A = ones({n},{n}) + eye({n});"), &mut env);
let stmts = parse_stmts("inv(A)").expect("parse");
group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, _| {
b.iter(|| exec_checked(black_box(&stmts), &mut env, &mut io))
});
}
group.finish();
}
fn bench_fn_calls(c: &mut Criterion) {
exec::init();
let fn_def = "\
function y = inc(x)
y = x + 1;
end";
let mut env = new_env();
run(fn_def, &mut env);
let stmts = parse_stmts("s = 0\nfor k = 1:1000\n s = inc(k)\nend").expect("parse");
let mut io = IoContext::new();
c.bench_function("fn_calls_1000", |b| {
b.iter(|| exec_checked(black_box(&stmts), &mut env, &mut io))
});
}
criterion_group!(
benches,
bench_scalar_ops,
bench_fib,
bench_loop_throughput,
bench_matmul,
bench_inv,
bench_fn_calls,
);
criterion_main!(benches);