Skip to content

Commit 7531589

Browse files
hyperpolymathclaude
andcommitted
bench: add criterion benchmarks for iseriser core functions
Benchmarks cover manifest parsing (minimal, rich, gleam fixtures), manifest validation, LanguageModel ABI conversion, and repo scanning. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0cbf7c5 commit 7531589

2 files changed

Lines changed: 240 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,8 @@ serde_json = "1"
2222

2323
[dev-dependencies]
2424
tempfile = "3"
25+
criterion = { version = "0.5", features = ["html_reports"] }
26+
27+
[[bench]]
28+
name = "iseriser_bench"
29+
harness = false

benches/iseriser_bench.rs

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
//
4+
// benches/iseriser_bench.rs — Criterion benchmarks for the iseriser crate.
5+
//
6+
// Benchmarks cover the two hottest paths in normal usage:
7+
//
8+
// 1. **Manifest parsing** (`parse_manifest`) — called once per `iseriser`
9+
// invocation but must be fast enough to support tooling that calls it in
10+
// a loop across hundreds of repos.
11+
//
12+
// 2. **Manifest validation** (`validate`) — validates the parsed manifest
13+
// against ABI constraints.
14+
//
15+
// 3. **Repo scanning** (`scan_repo`) — walks a directory tree and emits
16+
// recommendations; the most I/O-bound hot path.
17+
//
18+
// 4. **Manifest → LanguageModel conversion** (`to_language_model`) — the
19+
// ABI boundary conversion exercised on every code-generation run.
20+
//
21+
// Run with:
22+
// cargo bench --bench iseriser_bench
23+
24+
use criterion::{black_box, criterion_group, criterion_main, Criterion};
25+
use iseriser::manifest::{parse_manifest, validate};
26+
27+
// ---------------------------------------------------------------------------
28+
// Manifest TOML fixtures
29+
// ---------------------------------------------------------------------------
30+
31+
/// Minimal well-formed manifest — the common case for quick tooling calls.
32+
const MINIMAL_MANIFEST: &str = r#"
33+
[project]
34+
name = "chapeliser"
35+
version = "0.1.0"
36+
37+
[language]
38+
name = "Chapel"
39+
paradigm = "imperative"
40+
type-system = "simple"
41+
compilation-target = "native"
42+
key-primitives = ["task", "locale", "domain", "forall", "sync"]
43+
44+
[output]
45+
repo-name = "chapeliser"
46+
github-org = "hyperpolymath"
47+
description = "Chapel interop -iser for distributed HPC"
48+
"#;
49+
50+
/// A richer manifest with many key-primitives — stress-tests the Vec allocation
51+
/// in `LanguageConfig::key_primitives`.
52+
const RICH_MANIFEST: &str = r#"
53+
[project]
54+
name = "juliaiser"
55+
version = "0.2.0"
56+
57+
[language]
58+
name = "Julia"
59+
paradigm = "functional"
60+
type-system = "gradual"
61+
compilation-target = "jvm"
62+
key-primitives = [
63+
"macro", "ccall", "llvmcall", "generated", "invokelatest",
64+
"world-age", "multiple-dispatch", "abstract-type", "parametric",
65+
"union-type", "where-clause", "tuple-type", "named-tuple",
66+
"vararg", "keyword-arg", "optional-arg", "type-alias",
67+
"inner-constructor", "outer-constructor", "conversion",
68+
]
69+
70+
[output]
71+
repo-name = "juliaiser"
72+
github-org = "hyperpolymath"
73+
description = "Julia FFI/ABI interop meta-iser"
74+
"#;
75+
76+
/// A functional-paradigm Gleam manifest — exercises enum variant parsing.
77+
const GLEAM_MANIFEST: &str = r#"
78+
[project]
79+
name = "gleamiser"
80+
version = "0.1.0"
81+
82+
[language]
83+
name = "Gleam"
84+
paradigm = "functional"
85+
type-system = "dependent"
86+
compilation-target = "beam"
87+
key-primitives = ["process", "message", "otp", "actor", "channel", "gleam-stdlib"]
88+
89+
[output]
90+
repo-name = "gleamiser"
91+
github-org = "hyperpolymath"
92+
description = "Gleam interop -iser targeting the BEAM runtime"
93+
"#;
94+
95+
// ---------------------------------------------------------------------------
96+
// Manifest parsing benchmarks
97+
// ---------------------------------------------------------------------------
98+
99+
/// Benchmark parsing a minimal iseriser.toml.
100+
fn bench_parse_minimal(c: &mut Criterion) {
101+
c.bench_function("parse_manifest/minimal", |b| {
102+
b.iter(|| {
103+
let m = parse_manifest(black_box(MINIMAL_MANIFEST))
104+
.expect("minimal manifest must parse");
105+
black_box(m);
106+
});
107+
});
108+
}
109+
110+
/// Benchmark parsing a manifest with many key-primitives.
111+
fn bench_parse_rich(c: &mut Criterion) {
112+
c.bench_function("parse_manifest/rich_20_primitives", |b| {
113+
b.iter(|| {
114+
let m = parse_manifest(black_box(RICH_MANIFEST))
115+
.expect("rich manifest must parse");
116+
black_box(m);
117+
});
118+
});
119+
}
120+
121+
/// Benchmark parsing a functional-paradigm manifest.
122+
fn bench_parse_gleam(c: &mut Criterion) {
123+
c.bench_function("parse_manifest/gleam_beam_target", |b| {
124+
b.iter(|| {
125+
let m = parse_manifest(black_box(GLEAM_MANIFEST))
126+
.expect("gleam manifest must parse");
127+
black_box(m);
128+
});
129+
});
130+
}
131+
132+
// ---------------------------------------------------------------------------
133+
// Manifest validation benchmarks
134+
// ---------------------------------------------------------------------------
135+
136+
/// Benchmark validation of a well-formed manifest.
137+
fn bench_validate_valid(c: &mut Criterion) {
138+
let manifest = parse_manifest(MINIMAL_MANIFEST).expect("parse");
139+
c.bench_function("validate/valid_manifest", |b| {
140+
b.iter(|| {
141+
let result = validate(black_box(&manifest));
142+
black_box(result.expect("valid manifest must pass validation"));
143+
});
144+
});
145+
}
146+
147+
/// Benchmark validation of a rich manifest (more fields to check).
148+
fn bench_validate_rich(c: &mut Criterion) {
149+
let manifest = parse_manifest(RICH_MANIFEST).expect("parse rich");
150+
c.bench_function("validate/rich_20_primitives", |b| {
151+
b.iter(|| {
152+
let result = validate(black_box(&manifest));
153+
black_box(result.expect("rich manifest must pass validation"));
154+
});
155+
});
156+
}
157+
158+
// ---------------------------------------------------------------------------
159+
// LanguageModel conversion benchmarks
160+
// ---------------------------------------------------------------------------
161+
162+
/// Benchmark the manifest → LanguageModel ABI conversion.
163+
///
164+
/// Called on every code-generation run at the ABI boundary.
165+
fn bench_to_language_model(c: &mut Criterion) {
166+
let manifest = parse_manifest(MINIMAL_MANIFEST).expect("parse");
167+
c.bench_function("to_language_model/chapel", |b| {
168+
b.iter(|| {
169+
let model = black_box(&manifest).to_language_model();
170+
black_box(model);
171+
});
172+
});
173+
}
174+
175+
/// Benchmark conversion of a manifest with many primitives.
176+
fn bench_to_language_model_rich(c: &mut Criterion) {
177+
let manifest = parse_manifest(RICH_MANIFEST).expect("parse rich");
178+
c.bench_function("to_language_model/julia_20_primitives", |b| {
179+
b.iter(|| {
180+
let model = black_box(&manifest).to_language_model();
181+
black_box(model);
182+
});
183+
});
184+
}
185+
186+
// ---------------------------------------------------------------------------
187+
// Repo scanning benchmark
188+
// ---------------------------------------------------------------------------
189+
190+
/// Benchmark scanning the iseriser repo itself.
191+
///
192+
/// Uses the repo root as the scan target — this is a real directory walk
193+
/// with file presence checks, exercising the most I/O-bound path in the crate.
194+
fn bench_scan_repo(c: &mut Criterion) {
195+
// Use the repo root (two levels up from benches/).
196+
let repo_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
197+
.to_str()
198+
.expect("valid UTF-8 path")
199+
.to_owned();
200+
201+
c.bench_function("scan_repo/iseriser_root", |b| {
202+
b.iter(|| {
203+
let recs = iseriser::scan::scan_repo(black_box(&repo_root))
204+
.expect("scan must succeed");
205+
black_box(recs);
206+
});
207+
});
208+
}
209+
210+
// ---------------------------------------------------------------------------
211+
// Criterion registration
212+
// ---------------------------------------------------------------------------
213+
214+
criterion_group!(
215+
parse_benches,
216+
bench_parse_minimal,
217+
bench_parse_rich,
218+
bench_parse_gleam,
219+
);
220+
221+
criterion_group!(
222+
validate_benches,
223+
bench_validate_valid,
224+
bench_validate_rich,
225+
);
226+
227+
criterion_group!(
228+
abi_benches,
229+
bench_to_language_model,
230+
bench_to_language_model_rich,
231+
);
232+
233+
criterion_group!(scan_benches, bench_scan_repo);
234+
235+
criterion_main!(parse_benches, validate_benches, abi_benches, scan_benches);

0 commit comments

Comments
 (0)