Phase 2 abgeschlossen: Bytecode, TBVM, Runtime-Scheibe, tbc run
- Sema zum Lowering-Pass umgebaut: typisiertes HIR (Slots, explizite Konvertierungsknoten) als Codegen-Eingabe; BYREF verlangt exakten Typ - Bytecode-Feindesign umgesetzt: monomorpher Opcode-Satz, .tbc-Container (Formatversion 1) mit eigenem Writer/Reader - Codegenerator HIR -> Bytecode (Fixup-Listen, keine globalen Passes) - TBVM-Interpreter: Kontrollfluss, GOSUB-Stack je Frame, BYREF/BYVAL, STATIC, DEF FN, DATA/READ/RESTORE, ON [LOCAL] ERROR/RESUME/ERR/ERL, Breakpoints/Einzelschritt/Inspektion, STOP fortsetzbar - Runtime-Scheibe: Host-Trait (Konsole/Capture), Builtin-Tabelle, Konvertierungsmatrix, PRINT-Formatierung/Druckzonen, Stringfunktionen - tbc run/build/check mit Exit-Codes nach Entscheidung D6 - Korpus-Harness (byte-genauer Vergleich) + 3 neue Korpusdateien (konvertierung, fehlerbehandlung, byref); 137 Tests gruen - Benchmarks: Einzelmodul 1,2 ms / Projekt 49.760 Zeilen 124 ms (Budgets eingehalten), VM ~5 Mio Schleifeniterationen/s - Doku fortgeschrieben (tbvm-design, sprachreferenz, PLAN); verlagerte Punkte als explizite Aufgaben in Phase 3 - OpenSpec-Change phase-2-bytecode-vm (27/27 Tasks) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
88
crates/tb-vm/benches/compile.rs
Normal file
88
crates/tb-vm/benches/compile.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
//! Compile-Budget-Benchmark (Instant-Compile-Anforderung, PLAN.md):
|
||||
//! Projekt mit ~50.000 Zeilen < 1 s, einzelnes Modul (~500 Zeilen) < 50 ms
|
||||
//! (Release-Build). Eigener Harness (kein Criterion — keine
|
||||
//! Fremdabhängigkeit nötig); Lauf mit `cargo bench -p tb-vm`.
|
||||
|
||||
use std::fmt::Write as _;
|
||||
use std::time::Instant;
|
||||
|
||||
/// Realistisch gemischtes Modul mit `n_blocks` Codeblöcken
|
||||
/// (~10 Zeilen je Block) plus Prozeduren.
|
||||
fn generate_module(n_blocks: usize, seed: usize) -> String {
|
||||
let mut src = String::new();
|
||||
let _ = writeln!(src, "' Generiertes Benchmark-Modul {seed}");
|
||||
let _ = writeln!(src, "DIM feld{seed}%(100)");
|
||||
for i in 0..n_blocks {
|
||||
let v = format!("v{seed}x{i}");
|
||||
let _ = writeln!(src, "{v}% = {i} MOD 100");
|
||||
let _ = writeln!(src, "{v}tot# = {v}% * 1.5 + SQR({v}% + 1)");
|
||||
let _ = writeln!(src, "IF {v}% > 50 THEN");
|
||||
let _ = writeln!(src, " {v}s$ = \"gross\" + STR$({v}%)");
|
||||
let _ = writeln!(src, "ELSE");
|
||||
let _ = writeln!(src, " {v}s$ = LEFT$(\"klein\", 3)");
|
||||
let _ = writeln!(src, "END IF");
|
||||
let _ = writeln!(src, "FOR {v}i% = 1 TO 10");
|
||||
let _ = writeln!(src, " feld{seed}%({v}i% MOD 100) = {v}i%");
|
||||
let _ = writeln!(src, "NEXT");
|
||||
}
|
||||
// Prozeduren am Modulende
|
||||
for p in 0..(n_blocks / 20).max(1) {
|
||||
let _ = writeln!(src, "SUB Tu{seed}p{p} (a%, b#)");
|
||||
let _ = writeln!(src, " b# = a% * 2 + b#");
|
||||
let _ = writeln!(src, "END SUB");
|
||||
}
|
||||
src
|
||||
}
|
||||
|
||||
fn compile_all(sources: &[(String, String)]) -> usize {
|
||||
let mut total = 0;
|
||||
for (name, src) in sources {
|
||||
let m = tb_vm::compile_source(name, src).expect("Benchmark-Quelle muss kompilieren");
|
||||
total += m.procs.iter().map(|p| p.code.len()).sum::<usize>();
|
||||
}
|
||||
total
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Einzelnes Modul: ~500 Zeilen (Budget < 50 ms).
|
||||
let single = generate_module(50, 0);
|
||||
let single_lines = single.lines().count();
|
||||
|
||||
// Projekt: ~50.000 Zeilen über 20 Module (Budget < 1 s).
|
||||
let modules: Vec<(String, String)> = (0..20)
|
||||
.map(|i| (format!("MOD{i}"), generate_module(245, i)))
|
||||
.collect();
|
||||
let project_lines: usize = modules.iter().map(|(_, s)| s.lines().count()).sum();
|
||||
|
||||
// Aufwärmen
|
||||
let _ = compile_all(&[("WARM".into(), single.clone())]);
|
||||
|
||||
let t = Instant::now();
|
||||
let mut best_single = f64::MAX;
|
||||
for _ in 0..10 {
|
||||
let t1 = Instant::now();
|
||||
let _ = compile_all(&[("EINZEL".into(), single.clone())]);
|
||||
best_single = best_single.min(t1.elapsed().as_secs_f64());
|
||||
}
|
||||
let _ = t;
|
||||
|
||||
let t2 = Instant::now();
|
||||
let instrs = compile_all(&modules);
|
||||
let project_secs = t2.elapsed().as_secs_f64();
|
||||
|
||||
println!("Compile-Budget-Benchmark (Release):");
|
||||
println!(
|
||||
" Einzelmodul: {single_lines} Zeilen in {:.2} ms (Budget 50 ms) {}",
|
||||
best_single * 1000.0,
|
||||
if best_single < 0.050 { "OK" } else { "VERFEHLT" }
|
||||
);
|
||||
println!(
|
||||
" Projekt: {project_lines} Zeilen in {:.0} ms (Budget 1000 ms) {} — {instrs} Instruktionen",
|
||||
project_secs * 1000.0,
|
||||
if project_secs < 1.0 { "OK" } else { "VERFEHLT" }
|
||||
);
|
||||
let lps = project_lines as f64 / project_secs;
|
||||
println!(" Durchsatz: {:.0} Zeilen/s", lps);
|
||||
assert!(best_single < 0.050, "Einzelmodul-Budget verfehlt");
|
||||
assert!(project_secs < 1.0, "Projekt-Budget verfehlt");
|
||||
}
|
||||
Reference in New Issue
Block a user