mod common;
use serde::{Deserialize, Serialize};
use std::fmt::Write as _;
use std::path::Path;
use std::time::{Duration, Instant};
use tatara_lisp::DeriveTataraDomain;
#[derive(DeriveTataraDomain, Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[tatara(keyword = "defperfexp")]
pub struct PerfExperimentSpec {
pub hypothesis: String,
pub variants: Vec<PerfVariant>,
#[serde(default)]
pub iterations: Option<u32>,
#[serde(default)]
pub tags: Vec<String>,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PerfVariant {
pub name: String,
pub source: String,
}
fn load_experiments() -> Vec<tatara_lisp::NamedDefinition<PerfExperimentSpec>> {
let dir = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("perf_corpus");
if !dir.is_dir() {
return Vec::new();
}
let mut paths: Vec<std::path::PathBuf> = std::fs::read_dir(&dir)
.unwrap_or_else(|e| panic!("perf_corpus: {e}"))
.filter_map(|e| e.ok().map(|d| d.path()))
.filter(|p| p.extension().and_then(|s| s.to_str()) == Some("lisp"))
.collect();
paths.sort();
let mut out = Vec::new();
for path in paths {
let src = std::fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("read {}: {e}", path.display()));
let mut defs =
tatara_lisp::compile_named::<PerfExperimentSpec>(&src).unwrap_or_else(|e| {
panic!("compile {}: {e}", path.display())
});
for def in &mut defs {
def.name = format!(
"{}::{}",
path.file_stem().and_then(|s| s.to_str()).unwrap_or("?"),
def.name
);
}
out.extend(defs);
}
out
}
fn time_variant(source: &str, iterations: u32) -> (Duration, sui_eval::perf::PerfSnapshot) {
let mut samples: Vec<Duration> = Vec::with_capacity(iterations as usize);
let (_, snap) = sui_eval::perf::with_scope(|| {
for _ in 0..iterations {
let start = Instant::now();
let _ = sui_eval::eval(source);
samples.push(start.elapsed());
}
});
samples.sort();
let median = samples[samples.len() / 2];
(median, snap)
}
fn render_experiment(
def: &tatara_lisp::NamedDefinition<PerfExperimentSpec>,
results: &[(String, Duration, sui_eval::perf::PerfSnapshot)],
) -> String {
let mut out = String::new();
writeln!(out, "### `{}`", def.name).unwrap();
writeln!(out).unwrap();
writeln!(out, "> **Hypothesis.** {}", def.spec.hypothesis).unwrap();
writeln!(out).unwrap();
if !def.spec.tags.is_empty() {
writeln!(out, "Tags: {}", def.spec.tags.join(", ")).unwrap();
writeln!(out).unwrap();
}
let it = def.spec.iterations.unwrap_or(100);
writeln!(out, "Iterations per variant: {it}").unwrap();
writeln!(out).unwrap();
writeln!(
out,
"| variant | median µs | eval_expr | force_value | thunks (cr/fo) | dominant |"
)
.unwrap();
writeln!(
out,
"|---------|----------:|----------:|------------:|---------------:|----------|"
)
.unwrap();
let mut sorted: Vec<&(String, Duration, sui_eval::perf::PerfSnapshot)> = results.iter().collect();
sorted.sort_by(|a, b| a.1.cmp(&b.1));
for (name, dur, snap) in sorted {
let dominant = snap
.dominant_expr_kind()
.map(|(c, n)| format!("{}({})", sui_eval::perf::counter_name(c), n))
.unwrap_or_else(|| "-".to_string());
writeln!(
out,
"| `{}` | {} | {} | {} | {}/{} | {} |",
name,
dur.as_micros(),
snap.get(sui_eval::perf::Counter::EvalExpr),
snap.get(sui_eval::perf::Counter::ForceValue),
snap.thunks_created,
snap.thunks_forced,
dominant,
)
.unwrap();
}
writeln!(out).unwrap();
out
}
fn write_report(body: &str) -> std::path::PathBuf {
let target = Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("workspace root")
.join("target");
std::fs::create_dir_all(&target).ok();
let path = target.join("perf-experiments.md");
std::fs::write(&path, body).expect("write perf-experiments.md");
path
}
#[test]
fn run_all_perf_experiments() {
let experiments = load_experiments();
if experiments.is_empty() {
eprintln!(
"no experiments under tests/perf_corpus/ — add a `(defperfexp …)` form to run."
);
return;
}
let mut out = String::new();
writeln!(out, "# Perf experiments").unwrap();
writeln!(out).unwrap();
writeln!(
out,
"> Regenerated by `cargo test -p sui-eval --test perf_experiments --release`."
)
.unwrap();
writeln!(out).unwrap();
writeln!(
out,
"Each experiment is one `(defperfexp …)` Lisp form under \
`tests/perf_corpus/`. Per-variant numbers are medians over \
the declared iteration count, measured inside a single \
`sui_eval::perf::with_scope` per variant — counter totals \
are the SUM across all iterations, so divide by iteration \
count for per-call numbers."
)
.unwrap();
writeln!(out).unwrap();
writeln!(out, "## Experiments").unwrap();
writeln!(out).unwrap();
for def in &experiments {
let iterations = def.spec.iterations.unwrap_or(100);
let mut results = Vec::with_capacity(def.spec.variants.len());
for v in &def.spec.variants {
let (median, snap) = time_variant(&v.source, iterations);
results.push((v.name.clone(), median, snap));
}
out.push_str(&render_experiment(def, &results));
}
let path = write_report(&out);
eprintln!("\nwrote perf experiments report to {}", path.display());
}
#[test]
fn experiment_spec_parses_end_to_end() {
let src = r#"
(defperfexp smoke
:hypothesis "the infrastructure itself is functional"
:variants (
(:name "one" :source "1")
(:name "two" :source "1 + 1"))
:iterations 10)
"#;
let defs = tatara_lisp::compile_named::<PerfExperimentSpec>(src)
.expect("parse smoke experiment");
assert_eq!(defs.len(), 1);
assert_eq!(defs[0].name, "smoke");
assert_eq!(defs[0].spec.variants.len(), 2);
assert_eq!(defs[0].spec.variants[0].name, "one");
assert_eq!(defs[0].spec.iterations, Some(10));
}