Update all dependencies to latest versions
wasmtime 31→43, wasm-encoder/wasmparser 0.228→0.246, rustyline 15→18. API migrations: F64Const now takes Ieee64 wrapper, wasmtime has own Error type (wasmtime::bail! in host closures), cache_config_load_default removed. Add performance regression limits to benchmark tests.
This commit is contained in:
Generated
+502
-468
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -41,9 +41,9 @@ needless_collect = "warn"
|
|||||||
or_fun_call = "warn"
|
or_fun_call = "warn"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
wasm-encoder = "0.228"
|
wasm-encoder = "0.246"
|
||||||
wasmparser = "0.228"
|
wasmparser = "0.246"
|
||||||
wasmtime = "31"
|
wasmtime = "43"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
proptest = "1"
|
proptest = "1"
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ wafer-core = { path = "../core", version = "0.1.0" }
|
|||||||
wasmtime = { workspace = true }
|
wasmtime = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
rustyline = "15"
|
rustyline = "18"
|
||||||
|
|||||||
@@ -349,7 +349,7 @@ fn emit_op(f: &mut Function, op: &IrOp, ctx: &mut EmitCtx) {
|
|||||||
IrOp::PushF64(val) => {
|
IrOp::PushF64(val) => {
|
||||||
fsp_dec(f);
|
fsp_dec(f);
|
||||||
f.instruction(&Instruction::GlobalGet(FSP))
|
f.instruction(&Instruction::GlobalGet(FSP))
|
||||||
.instruction(&Instruction::F64Const(*val))
|
.instruction(&Instruction::F64Const((*val).into()))
|
||||||
.instruction(&Instruction::F64Store(MEM8));
|
.instruction(&Instruction::F64Store(MEM8));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -855,14 +855,14 @@ fn emit_op(f: &mut Function, op: &IrOp, ctx: &mut EmitCtx) {
|
|||||||
// -- Float comparisons (cross-stack) --------------------------------
|
// -- Float comparisons (cross-stack) --------------------------------
|
||||||
IrOp::FZeroEq => {
|
IrOp::FZeroEq => {
|
||||||
fpop(f);
|
fpop(f);
|
||||||
f.instruction(&Instruction::F64Const(0.0))
|
f.instruction(&Instruction::F64Const(0.0.into()))
|
||||||
.instruction(&Instruction::F64Eq);
|
.instruction(&Instruction::F64Eq);
|
||||||
bool_to_forth_flag(f, SCRATCH_BASE);
|
bool_to_forth_flag(f, SCRATCH_BASE);
|
||||||
push_via_local(f, SCRATCH_BASE + 1);
|
push_via_local(f, SCRATCH_BASE + 1);
|
||||||
}
|
}
|
||||||
IrOp::FZeroLt => {
|
IrOp::FZeroLt => {
|
||||||
fpop(f);
|
fpop(f);
|
||||||
f.instruction(&Instruction::F64Const(0.0))
|
f.instruction(&Instruction::F64Const(0.0.into()))
|
||||||
.instruction(&Instruction::F64Lt);
|
.instruction(&Instruction::F64Lt);
|
||||||
bool_to_forth_flag(f, SCRATCH_BASE);
|
bool_to_forth_flag(f, SCRATCH_BASE);
|
||||||
push_via_local(f, SCRATCH_BASE + 1);
|
push_via_local(f, SCRATCH_BASE + 1);
|
||||||
|
|||||||
@@ -282,8 +282,6 @@ impl ForthVM {
|
|||||||
pub fn new_with_config(wafer_config: WaferConfig) -> anyhow::Result<Self> {
|
pub fn new_with_config(wafer_config: WaferConfig) -> anyhow::Result<Self> {
|
||||||
let mut config = wasmtime::Config::new();
|
let mut config = wasmtime::Config::new();
|
||||||
config.cranelift_nan_canonicalization(false);
|
config.cranelift_nan_canonicalization(false);
|
||||||
// Best-effort module caching
|
|
||||||
let _ = config.cache_config_load_default();
|
|
||||||
let engine = Engine::new(&config)?;
|
let engine = Engine::new(&config)?;
|
||||||
let output = Arc::new(Mutex::new(String::new()));
|
let output = Arc::new(Mutex::new(String::new()));
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ struct RunnerHost {}
|
|||||||
fn make_engine() -> anyhow::Result<Engine> {
|
fn make_engine() -> anyhow::Result<Engine> {
|
||||||
let mut config = wasmtime::Config::new();
|
let mut config = wasmtime::Config::new();
|
||||||
config.cranelift_nan_canonicalization(false);
|
config.cranelift_nan_canonicalization(false);
|
||||||
Engine::new(&config)
|
Ok(Engine::new(&config)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a pre-compiled `.wasm` module from a file path.
|
/// Execute a pre-compiled `.wasm` module from a file path.
|
||||||
@@ -336,7 +336,7 @@ fn create_host_func(
|
|||||||
((hi << 32) | lo, divisor)
|
((hi << 32) | lo, divisor)
|
||||||
};
|
};
|
||||||
if divisor == 0 {
|
if divisor == 0 {
|
||||||
anyhow::bail!("division by zero");
|
wasmtime::bail!("division by zero");
|
||||||
}
|
}
|
||||||
let quot = (dividend / divisor) as u32;
|
let quot = (dividend / divisor) as u32;
|
||||||
let rem = (dividend % divisor) as u32;
|
let rem = (dividend % divisor) as u32;
|
||||||
@@ -368,7 +368,7 @@ fn create_host_func(
|
|||||||
// Unimplemented host function: trap with a clear message.
|
// Unimplemented host function: trap with a clear message.
|
||||||
let name_owned = name.to_string();
|
let name_owned = name.to_string();
|
||||||
Func::new(store, void_type, move |_caller, _params, _results| {
|
Func::new(store, void_type, move |_caller, _params, _results| {
|
||||||
anyhow::bail!("host function '{name_owned}' is not available in standalone mode")
|
wasmtime::bail!("host function '{name_owned}' is not available in standalone mode")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -592,6 +592,9 @@ struct PerfBenchmark {
|
|||||||
verify: &'static str,
|
verify: &'static str,
|
||||||
expected: i32,
|
expected: i32,
|
||||||
samples: u32, // Number of runs for WAFER median
|
samples: u32, // Number of runs for WAFER median
|
||||||
|
/// Maximum acceptable WAFER/gforth ratio (< 1.0 = WAFER faster).
|
||||||
|
/// Test fails if ratio exceeds this. Set ~40-50% above measured baseline.
|
||||||
|
max_ratio: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn perf_benchmarks() -> Vec<PerfBenchmark> {
|
fn perf_benchmarks() -> Vec<PerfBenchmark> {
|
||||||
@@ -603,6 +606,7 @@ fn perf_benchmarks() -> Vec<PerfBenchmark> {
|
|||||||
verify: "25 FIB",
|
verify: "25 FIB",
|
||||||
expected: 75025,
|
expected: 75025,
|
||||||
samples: 5,
|
samples: 5,
|
||||||
|
max_ratio: 0.65,
|
||||||
},
|
},
|
||||||
PerfBenchmark {
|
PerfBenchmark {
|
||||||
name: "Factorial(12)x10K",
|
name: "Factorial(12)x10K",
|
||||||
@@ -612,6 +616,7 @@ fn perf_benchmarks() -> Vec<PerfBenchmark> {
|
|||||||
verify: "12 FACT",
|
verify: "12 FACT",
|
||||||
expected: 479001600,
|
expected: 479001600,
|
||||||
samples: 5,
|
samples: 5,
|
||||||
|
max_ratio: 0.75,
|
||||||
},
|
},
|
||||||
PerfBenchmark {
|
PerfBenchmark {
|
||||||
name: "GCD-bench(500)",
|
name: "GCD-bench(500)",
|
||||||
@@ -621,6 +626,7 @@ fn perf_benchmarks() -> Vec<PerfBenchmark> {
|
|||||||
verify: "48 36 GCD",
|
verify: "48 36 GCD",
|
||||||
expected: 12,
|
expected: 12,
|
||||||
samples: 5,
|
samples: 5,
|
||||||
|
max_ratio: 0.70,
|
||||||
},
|
},
|
||||||
PerfBenchmark {
|
PerfBenchmark {
|
||||||
name: "NestedLoops(50)",
|
name: "NestedLoops(50)",
|
||||||
@@ -630,6 +636,7 @@ fn perf_benchmarks() -> Vec<PerfBenchmark> {
|
|||||||
verify: "5 NESTED",
|
verify: "5 NESTED",
|
||||||
expected: 0,
|
expected: 0,
|
||||||
samples: 3,
|
samples: 3,
|
||||||
|
max_ratio: 0.20,
|
||||||
},
|
},
|
||||||
PerfBenchmark {
|
PerfBenchmark {
|
||||||
name: "Collatz(2K)",
|
name: "Collatz(2K)",
|
||||||
@@ -641,6 +648,7 @@ fn perf_benchmarks() -> Vec<PerfBenchmark> {
|
|||||||
verify: "27 COLLATZ",
|
verify: "27 COLLATZ",
|
||||||
expected: 111,
|
expected: 111,
|
||||||
samples: 3,
|
samples: 3,
|
||||||
|
max_ratio: 0.45,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -827,15 +835,17 @@ fn performance_report() {
|
|||||||
println!(" WAFER vs Gforth Performance Comparison (release mode)");
|
println!(" WAFER vs Gforth Performance Comparison (release mode)");
|
||||||
println!("{sep}\n");
|
println!("{sep}\n");
|
||||||
println!(
|
println!(
|
||||||
"{:<22} {:>10} {:>10} {:>10} {:>10} {:>10}",
|
"{:<22} {:>10} {:>10} {:>10} {:>10} {:>10} {:>10}",
|
||||||
"Benchmark", "WAFER", "CONSOL", "gforth", "gf-fast", "WAFER/gf"
|
"Benchmark", "WAFER", "CONSOL", "gforth", "gf-fast", "WAFER/gf", "limit"
|
||||||
);
|
);
|
||||||
println!(
|
println!(
|
||||||
"{:<22} {:>10} {:>10} {:>10} {:>10} {:>10}",
|
"{:<22} {:>10} {:>10} {:>10} {:>10} {:>10} {:>10}",
|
||||||
"", "(us)", "(us)", "(us)", "(us)", ""
|
"", "(us)", "(us)", "(us)", "(us)", "", ""
|
||||||
);
|
);
|
||||||
println!("{thin}");
|
println!("{thin}");
|
||||||
|
|
||||||
|
let mut regressions: Vec<String> = Vec::new();
|
||||||
|
|
||||||
for bench in &benchmarks {
|
for bench in &benchmarks {
|
||||||
let wafer = wafer_release
|
let wafer = wafer_release
|
||||||
.and_then(|w| measure_wafer_release(w, bench))
|
.and_then(|w| measure_wafer_release(w, bench))
|
||||||
@@ -853,25 +863,45 @@ fn performance_report() {
|
|||||||
} else {
|
} else {
|
||||||
wafer
|
wafer
|
||||||
};
|
};
|
||||||
let ratio = gf.map_or_else(
|
let ratio_val = gf.and_then(|g| {
|
||||||
|| "-".to_string(),
|
|
||||||
|g| {
|
|
||||||
if g > 0 {
|
if g > 0 {
|
||||||
format!("{:.2}x", best_wafer as f64 / g as f64)
|
Some(best_wafer as f64 / g as f64)
|
||||||
} else {
|
} else {
|
||||||
"-".to_string()
|
None
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
);
|
let ratio = ratio_val.map_or_else(|| "-".to_string(), |r| format!("{r:.2}x"));
|
||||||
|
let limit_str = format!("{:.2}x", bench.max_ratio);
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"{:<22} {:>10} {:>10} {:>10} {:>10} {:>10}",
|
"{:<22} {:>10} {:>10} {:>10} {:>10} {:>10} {:>10}",
|
||||||
bench.name, wafer, consol, gf_str, gf_fast_str, ratio
|
bench.name, wafer, consol, gf_str, gf_fast_str, ratio, limit_str
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Check regression limits
|
||||||
|
if let Some(r) = ratio_val {
|
||||||
|
if r > bench.max_ratio {
|
||||||
|
regressions.push(format!(
|
||||||
|
" {} ratio {:.2}x exceeds limit {:.2}x (REGRESSION)",
|
||||||
|
bench.name, r, bench.max_ratio
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if r < 0.02 {
|
||||||
|
regressions.push(format!(
|
||||||
|
" {} ratio {:.4}x suspiciously low (measurement error?)",
|
||||||
|
bench.name, r
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{thin}");
|
println!("{thin}");
|
||||||
println!(" WAFER = all optimizations, CONSOL = after CONSOLIDATE");
|
println!(" WAFER = all optimizations, CONSOL = after CONSOLIDATE");
|
||||||
println!(" WAFER/gf = best(WAFER,CONSOL) vs gforth, < 1.0 means WAFER faster");
|
println!(" WAFER/gf = best(WAFER,CONSOL) vs gforth, < 1.0 means WAFER faster");
|
||||||
println!("{sep}\n");
|
println!("{sep}\n");
|
||||||
|
|
||||||
|
if !regressions.is_empty() {
|
||||||
|
let msg = regressions.join("\n");
|
||||||
|
panic!("Performance regression detected:\n{msg}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user