use std::sync::Arc;
use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput};
use lex_ast::canonicalize_program;
use lex_bytecode::vm::Vm;
use lex_bytecode::{compile_program, Program, Value};
use lex_syntax::parse_source;
const RESPONSE_BUILD_SRC: &str = r#"
type Response = { status :: Int, total :: Int }
fn handle(user_id :: Int, item_id :: Int, qty :: Int) -> Response {
let v1 := { a: user_id, b: item_id, c: qty }
let v2 := { d: v1.a, e: v1.b, f: v1.c, g: v1.a * 2 }
let v3 := { h: v2.d, i: v2.e, j: v2.f, k: v2.g }
let v4 := { l: v3.h * 3, m: v3.i * 5, n: v3.j * 7, o: v3.k }
let v5 := { p: v4.l + v4.m, q: v4.n + v4.o, r: v4.l - v4.m }
let v6 := { s: v5.p + v5.q, t: v5.q + v5.r, u: v5.p - v5.r }
match v6.s > 0 {
true => { status: 200, total: v6.s + v6.t + v6.u },
false => { status: 400, total: 0 },
}
}
fn drive(n :: Int) -> Int {
match n {
0 => 0,
_ => {
let r := handle(n, 7, 3)
r.total + drive(n - 1)
},
}
}
"#;
fn compile_with_env(src: &str, no_stack_records: bool) -> Arc<Program> {
if no_stack_records {
unsafe { std::env::set_var("LEX_NO_STACK_RECORDS", "1"); }
let prog = parse_source(src).expect("parse");
let stages = canonicalize_program(&prog);
lex_types::check_program(&stages).expect("typecheck");
let p = Arc::new(compile_program(&stages));
unsafe { std::env::remove_var("LEX_NO_STACK_RECORDS"); }
p
} else {
let prog = parse_source(src).expect("parse");
let stages = canonicalize_program(&prog);
lex_types::check_program(&stages).expect("typecheck");
Arc::new(compile_program(&stages))
}
}
fn assert_lowering_state(p: &Program, expect_lowered: bool) {
use lex_bytecode::Op;
let mut total_record_sites = 0usize;
let mut lowered_sites = 0usize;
for f in &p.functions {
for op in &f.code {
match op {
Op::MakeRecord { .. } => total_record_sites += 1,
Op::AllocStackRecord { .. } => {
total_record_sites += 1;
lowered_sites += 1;
}
_ => {}
}
}
}
assert!(total_record_sites > 0, "workload must have record sites");
if expect_lowered {
assert!(lowered_sites > 0,
"expected lowering on enabled arm (sites: {total_record_sites})");
} else {
assert_eq!(lowered_sites, 0,
"expected no lowering on disabled arm (sites: {total_record_sites})");
}
}
fn bench_response_build(c: &mut Criterion) {
let enabled = compile_with_env(RESPONSE_BUILD_SRC, false);
let disabled = compile_with_env(RESPONSE_BUILD_SRC, true);
assert_lowering_state(&enabled, true);
assert_lowering_state(&disabled, false);
let mut group = c.benchmark_group("response_build");
for n in [100i64, 1_000] {
let nu = n as u64;
group.throughput(Throughput::Elements(7 * nu));
let enabled_arm = Arc::clone(&enabled);
group.bench_function(format!("enabled/n={n}"), move |b| {
b.iter(|| {
let mut vm = Vm::new(&enabled_arm);
vm.set_step_limit(u64::MAX);
black_box(vm.call("drive", vec![Value::Int(n)]).unwrap());
})
});
let disabled_arm = Arc::clone(&disabled);
group.bench_function(format!("disabled/n={n}"), move |b| {
b.iter(|| {
let mut vm = Vm::new(&disabled_arm);
vm.set_step_limit(u64::MAX);
black_box(vm.call("drive", vec![Value::Int(n)]).unwrap());
})
});
}
group.finish();
}
criterion_group!(benches, bench_response_build);
criterion_main!(benches);