Phase 4 — Platform shells: - calcpad-macos/: SwiftUI two-column editor with Rust FFI bridge (16 files) - calcpad-windows/: iced GUI with Windows 11 Fluent theme (7 files, 13 tests) - calcpad-web/: React 18 + CodeMirror 6 + WASM Worker + PWA (20 files) - calcpad-cli/: clap-based CLI with expression eval, pipe/stdin, JSON/CSV output, and interactive REPL with rustyline history Phase 5 — Engine modules: - formatting/: answer formatting (decimal/scientific/SI notation, thousands separators, currency), line type classification, clipboard values (93 tests) - plugins/: CalcPadPlugin trait, PluginRegistry, Rhai scripting stub (43 tests) - benches/: criterion benchmarks (single-line, 100/500-line sheets, DAG, incremental) - tests/sheet_scenarios.rs: 20 real-world integration tests - tests/proptest_fuzz.rs: 12 property-based fuzz tests 771 tests passing across workspace, 0 failures.
156 lines
4.5 KiB
Rust
156 lines
4.5 KiB
Rust
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
|
|
use calcpad_engine::context::EvalContext;
|
|
use calcpad_engine::pipeline::{eval_line, eval_sheet};
|
|
use calcpad_engine::SheetContext;
|
|
|
|
// --- Single-line benchmarks ---
|
|
|
|
fn bench_single_line_arithmetic(c: &mut Criterion) {
|
|
c.bench_function("single_line_arithmetic", |b| {
|
|
b.iter(|| {
|
|
let mut ctx = EvalContext::new();
|
|
eval_line(black_box("(3 + 4) * 2 ^ 3 - 1"), &mut ctx)
|
|
})
|
|
});
|
|
}
|
|
|
|
fn bench_single_line_unit_conversion(c: &mut Criterion) {
|
|
c.bench_function("single_line_unit_conversion", |b| {
|
|
b.iter(|| {
|
|
let mut ctx = EvalContext::new();
|
|
eval_line(black_box("5kg in lb"), &mut ctx)
|
|
})
|
|
});
|
|
}
|
|
|
|
// --- Sheet benchmarks (pipeline API) ---
|
|
|
|
fn bench_100_line_sheet(c: &mut Criterion) {
|
|
let lines: Vec<String> = (0..100)
|
|
.map(|i| {
|
|
if i == 0 {
|
|
"x = 1".to_string()
|
|
} else {
|
|
format!("x = x + {}", i)
|
|
}
|
|
})
|
|
.collect();
|
|
let line_refs: Vec<&str> = lines.iter().map(|s| s.as_str()).collect();
|
|
|
|
c.bench_function("100_line_sheet_pipeline", |b| {
|
|
b.iter(|| {
|
|
let mut ctx = EvalContext::new();
|
|
eval_sheet(black_box(&line_refs), &mut ctx)
|
|
})
|
|
});
|
|
}
|
|
|
|
fn bench_variable_heavy_sheet(c: &mut Criterion) {
|
|
let mut lines: Vec<String> = Vec::with_capacity(50);
|
|
for i in 0..10 {
|
|
lines.push(format!("v{} = {}", i, i * 10 + 1));
|
|
}
|
|
for i in 10..50 {
|
|
let a = i % 10;
|
|
let b = (i + 3) % 10;
|
|
lines.push(format!("r{} = v{} + v{}", i, a, b));
|
|
}
|
|
let line_refs: Vec<&str> = lines.iter().map(|s| s.as_str()).collect();
|
|
|
|
c.bench_function("variable_heavy_sheet_dag", |b| {
|
|
b.iter(|| {
|
|
let mut ctx = EvalContext::new();
|
|
eval_sheet(black_box(&line_refs), &mut ctx)
|
|
})
|
|
});
|
|
}
|
|
|
|
// --- SheetContext benchmarks (with dependency tracking) ---
|
|
|
|
fn bench_sheet_context_500_lines(c: &mut Criterion) {
|
|
let lines: Vec<String> = (0..500)
|
|
.map(|i| match i % 5 {
|
|
0 => format!("x{} = {}", i, i),
|
|
1 => format!("{} + {} * {}", i, i + 1, i + 2),
|
|
2 => format!("{}kg in g", (i as f64) * 0.1),
|
|
3 => format!("// Comment line {}", i),
|
|
4 => format!("sqrt({})", (i * i) as f64),
|
|
_ => unreachable!(),
|
|
})
|
|
.collect();
|
|
|
|
c.bench_function("sheet_context_500_lines_full_eval", |b| {
|
|
b.iter(|| {
|
|
let mut sheet = SheetContext::new();
|
|
for (i, line) in lines.iter().enumerate() {
|
|
sheet.set_line(i, line);
|
|
}
|
|
sheet.eval()
|
|
})
|
|
});
|
|
}
|
|
|
|
fn bench_sheet_context_incremental_edit(c: &mut Criterion) {
|
|
let lines: Vec<String> = (0..500)
|
|
.map(|i| match i % 3 {
|
|
0 => format!("x{} = {}", i, i),
|
|
1 => format!("{} + {}", i, i + 1),
|
|
2 => format!("sqrt({})", (i * i) as f64),
|
|
_ => unreachable!(),
|
|
})
|
|
.collect();
|
|
|
|
let mut sheet = SheetContext::new();
|
|
for (i, line) in lines.iter().enumerate() {
|
|
sheet.set_line(i, line);
|
|
}
|
|
sheet.eval();
|
|
|
|
c.bench_function("sheet_context_incremental_single_edit", |b| {
|
|
let mut s = sheet.clone();
|
|
let mut counter = 0;
|
|
b.iter(|| {
|
|
counter += 1;
|
|
// Edit a line that doesn't affect others (no variable)
|
|
s.set_line(250, &format!("{} + {}", counter, counter + 1));
|
|
s.eval()
|
|
})
|
|
});
|
|
}
|
|
|
|
fn bench_sheet_context_incremental_variable_change(c: &mut Criterion) {
|
|
let mut sheet = SheetContext::new();
|
|
sheet.set_line(0, "base = 100");
|
|
for i in 1..500 {
|
|
if i % 10 == 0 {
|
|
sheet.set_line(i, "base + 1");
|
|
} else {
|
|
sheet.set_line(i, &format!("{} + {}", i, i + 1));
|
|
}
|
|
}
|
|
sheet.eval();
|
|
|
|
c.bench_function("sheet_context_incremental_variable_change", |b| {
|
|
let mut s = sheet.clone();
|
|
let mut counter = 100;
|
|
b.iter(|| {
|
|
counter += 1;
|
|
s.set_line(0, &format!("base = {}", counter));
|
|
s.eval()
|
|
})
|
|
});
|
|
}
|
|
|
|
criterion_group!(
|
|
benches,
|
|
bench_single_line_arithmetic,
|
|
bench_single_line_unit_conversion,
|
|
bench_100_line_sheet,
|
|
bench_variable_heavy_sheet,
|
|
bench_sheet_context_500_lines,
|
|
bench_sheet_context_incremental_edit,
|
|
bench_sheet_context_incremental_variable_change,
|
|
);
|
|
criterion_main!(benches);
|