Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
fa7dadbdeb
|
@@ -43,10 +43,6 @@ bench:
|
|||||||
bench-opts:
|
bench-opts:
|
||||||
cargo test -p wafer-core --test benchmark_report -- --nocapture --ignored
|
cargo test -p wafer-core --test benchmark_report -- --nocapture --ignored
|
||||||
|
|
||||||
# Cross-engine performance report: WAFER vs gforth vs SwiftForth (sf64)
|
|
||||||
bench-compare:
|
|
||||||
CARGO_PROFILE_RELEASE_STRIP=none cargo test -p wafer-core --release --test comparison -- --nocapture --ignored performance_report
|
|
||||||
|
|
||||||
# Check dependency licenses and advisories
|
# Check dependency licenses and advisories
|
||||||
deny:
|
deny:
|
||||||
cargo deny check
|
cargo deny check
|
||||||
|
|||||||
@@ -72,19 +72,6 @@
|
|||||||
1-
|
1-
|
||||||
REPEAT ;
|
REPEAT ;
|
||||||
|
|
||||||
\ ---------------------------------------------------------------
|
|
||||||
\ Common extensions (not in Forth 2012, gforth-compatible)
|
|
||||||
\ ---------------------------------------------------------------
|
|
||||||
|
|
||||||
\ -ROT ( x1 x2 x3 -- x3 x1 x2 ) rotate top item to third place
|
|
||||||
: -ROT ROT ROT ;
|
|
||||||
|
|
||||||
\ <= ( n1 n2 -- flag ) true if n1 <= n2 (signed)
|
|
||||||
: <= > 0= ;
|
|
||||||
|
|
||||||
\ >= ( n1 n2 -- flag ) true if n1 >= n2 (signed)
|
|
||||||
: >= < 0= ;
|
|
||||||
|
|
||||||
\ ---------------------------------------------------------------
|
\ ---------------------------------------------------------------
|
||||||
\ Phase 2: Double-cell arithmetic
|
\ Phase 2: Double-cell arithmetic
|
||||||
\ ---------------------------------------------------------------
|
\ ---------------------------------------------------------------
|
||||||
|
|||||||
@@ -1088,10 +1088,12 @@ fn emit_do_loop(f: &mut Function, body: &[IrOp], is_plus_loop: bool, ctx: &mut E
|
|||||||
.instruction(&Instruction::End);
|
.instruction(&Instruction::End);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if index >= limit, exit
|
// Forth 2012: LOOP exits when the index crosses the boundary between
|
||||||
|
// limit-1 and limit. With step +1 that is exactly new_index == limit
|
||||||
|
// in wraparound arithmetic — start >= limit must wrap, not exit early.
|
||||||
f.instruction(&Instruction::LocalGet(index_local))
|
f.instruction(&Instruction::LocalGet(index_local))
|
||||||
.instruction(&Instruction::LocalGet(limit_local))
|
.instruction(&Instruction::LocalGet(limit_local))
|
||||||
.instruction(&Instruction::I32GeS)
|
.instruction(&Instruction::I32Eq)
|
||||||
.instruction(&Instruction::BrIf(1)) // break to $exit
|
.instruction(&Instruction::BrIf(1)) // break to $exit
|
||||||
.instruction(&Instruction::Br(0)) // continue loop
|
.instruction(&Instruction::Br(0)) // continue loop
|
||||||
.instruction(&Instruction::End) // end loop
|
.instruction(&Instruction::End) // end loop
|
||||||
@@ -1897,7 +1899,8 @@ fn emit_promoted_op(f: &mut Function, op: &IrOp, sim: &mut StackSim) {
|
|||||||
// Fix up stack for next iteration (LOOP body is stack-neutral)
|
// Fix up stack for next iteration (LOOP body is stack-neutral)
|
||||||
emit_promoted_loop_fixup(f, sim, &loop_top_stack);
|
emit_promoted_loop_fixup(f, sim, &loop_top_stack);
|
||||||
|
|
||||||
// LOOP: increment by 1, check >= limit
|
// LOOP: increment by 1, exit when new_index == limit
|
||||||
|
// (Forth 2012 boundary crossing; start >= limit wraps around)
|
||||||
f.instruction(&Instruction::LocalGet(index_local));
|
f.instruction(&Instruction::LocalGet(index_local));
|
||||||
f.instruction(&Instruction::I32Const(1));
|
f.instruction(&Instruction::I32Const(1));
|
||||||
f.instruction(&Instruction::I32Add);
|
f.instruction(&Instruction::I32Add);
|
||||||
@@ -1905,7 +1908,7 @@ fn emit_promoted_op(f: &mut Function, op: &IrOp, sim: &mut StackSim) {
|
|||||||
|
|
||||||
f.instruction(&Instruction::LocalGet(index_local));
|
f.instruction(&Instruction::LocalGet(index_local));
|
||||||
f.instruction(&Instruction::LocalGet(limit_local));
|
f.instruction(&Instruction::LocalGet(limit_local));
|
||||||
f.instruction(&Instruction::I32GeS);
|
f.instruction(&Instruction::I32Eq);
|
||||||
f.instruction(&Instruction::BrIf(1)); // break to $exit
|
f.instruction(&Instruction::BrIf(1)); // break to $exit
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2829,9 +2832,11 @@ fn emit_consolidated_do_loop(
|
|||||||
.instruction(&Instruction::End);
|
.instruction(&Instruction::End);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Forth 2012 boundary crossing: exit when new_index == limit
|
||||||
|
// (start >= limit wraps around instead of exiting early).
|
||||||
f.instruction(&Instruction::LocalGet(index_local))
|
f.instruction(&Instruction::LocalGet(index_local))
|
||||||
.instruction(&Instruction::LocalGet(limit_local))
|
.instruction(&Instruction::LocalGet(limit_local))
|
||||||
.instruction(&Instruction::I32GeS)
|
.instruction(&Instruction::I32Eq)
|
||||||
.instruction(&Instruction::BrIf(1))
|
.instruction(&Instruction::BrIf(1))
|
||||||
.instruction(&Instruction::Br(0))
|
.instruction(&Instruction::Br(0))
|
||||||
.instruction(&Instruction::End)
|
.instruction(&Instruction::End)
|
||||||
|
|||||||
+14
-51
@@ -393,11 +393,6 @@ impl<R: Runtime> ForthVM<R> {
|
|||||||
substitutions: Arc::new(Mutex::new(HashMap::new())),
|
substitutions: Arc::new(Mutex::new(HashMap::new())),
|
||||||
search_order: Arc::new(Mutex::new(vec![1])),
|
search_order: Arc::new(Mutex::new(vec![1])),
|
||||||
next_wid: Arc::new(Mutex::new(2)),
|
next_wid: Arc::new(Mutex::new(2)),
|
||||||
// SystemTime::now() PANICS on wasm32-unknown-unknown (no time
|
|
||||||
// source), which turned VM construction into an `unreachable`
|
|
||||||
// trap in the browser. Seed from the wall clock only where one
|
|
||||||
// exists; wasm hosts start deterministic and reseed via RND-SEED.
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
rng_state: {
|
rng_state: {
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
let seed = SystemTime::now()
|
let seed = SystemTime::now()
|
||||||
@@ -409,8 +404,6 @@ impl<R: Runtime> ForthVM<R> {
|
|||||||
seed
|
seed
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
rng_state: Arc::new(Mutex::new(0xDEAD_BEEF_CAFE_BABE)),
|
|
||||||
compile_frames: Vec::new(),
|
compile_frames: Vec::new(),
|
||||||
compiling_word_addr: 0,
|
compiling_word_addr: 0,
|
||||||
};
|
};
|
||||||
@@ -5390,30 +5383,20 @@ impl<R: Runtime> ForthVM<R> {
|
|||||||
/// UTIME ( -- ud ) push microseconds since epoch as a double-cell value.
|
/// UTIME ( -- ud ) push microseconds since epoch as a double-cell value.
|
||||||
fn register_utime(&mut self) -> anyhow::Result<()> {
|
fn register_utime(&mut self) -> anyhow::Result<()> {
|
||||||
let func: HostFn = Box::new(move |ctx: &mut dyn HostAccess| {
|
let func: HostFn = Box::new(move |ctx: &mut dyn HostAccess| {
|
||||||
// SystemTime::now() panics on wasm32-unknown-unknown; fail as a
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
// catchable Forth error instead of poisoning the VM with a trap.
|
let us = SystemTime::now()
|
||||||
#[cfg(target_arch = "wasm32")]
|
.duration_since(UNIX_EPOCH)
|
||||||
{
|
.unwrap_or_default()
|
||||||
let _ = ctx;
|
.as_micros() as u64;
|
||||||
Err(anyhow::anyhow!("UTIME: no time source on this platform"))
|
let lo = us as i32;
|
||||||
}
|
let hi = (us >> 32) as i32;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
// Push double: lo first (deeper), then hi on top
|
||||||
{
|
let sp = ctx.get_dsp();
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
let new_sp = sp - 2 * CELL_SIZE;
|
||||||
let us = SystemTime::now()
|
ctx.mem_write_i32(new_sp as u32, hi as i32);
|
||||||
.duration_since(UNIX_EPOCH)
|
ctx.mem_write_slice(new_sp as u32 + 4, &lo.to_le_bytes());
|
||||||
.unwrap_or_default()
|
ctx.set_dsp((new_sp as i32) as u32);
|
||||||
.as_micros() as u64;
|
Ok(())
|
||||||
let lo = us as i32;
|
|
||||||
let hi = (us >> 32) as i32;
|
|
||||||
// Push double: lo first (deeper), then hi on top
|
|
||||||
let sp = ctx.get_dsp();
|
|
||||||
let new_sp = sp - 2 * CELL_SIZE;
|
|
||||||
ctx.mem_write_i32(new_sp as u32, hi as i32);
|
|
||||||
ctx.mem_write_slice(new_sp as u32 + 4, &lo.to_le_bytes());
|
|
||||||
ctx.set_dsp((new_sp as i32) as u32);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
self.register_host_primitive("UTIME", false, func)?;
|
self.register_host_primitive("UTIME", false, func)?;
|
||||||
@@ -7479,12 +7462,6 @@ mod tests {
|
|||||||
assert_eq!(eval_stack("1 2 3 ROT"), vec![1, 3, 2]);
|
assert_eq!(eval_stack("1 2 3 ROT"), vec![1, 3, 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_minus_rot() {
|
|
||||||
// ( 1 2 3 -- 3 1 2 ) top-first: [2, 1, 3]
|
|
||||||
assert_eq!(eval_stack("1 2 3 -ROT"), vec![2, 1, 3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- Comparison --
|
// -- Comparison --
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -7505,20 +7482,6 @@ mod tests {
|
|||||||
assert_eq!(eval_stack("3 5 >"), vec![0]);
|
assert_eq!(eval_stack("3 5 >"), vec![0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_less_or_equal() {
|
|
||||||
assert_eq!(eval_stack("3 5 <="), vec![-1]);
|
|
||||||
assert_eq!(eval_stack("5 5 <="), vec![-1]);
|
|
||||||
assert_eq!(eval_stack("5 3 <="), vec![0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_greater_or_equal() {
|
|
||||||
assert_eq!(eval_stack("5 3 >="), vec![-1]);
|
|
||||||
assert_eq!(eval_stack("5 5 >="), vec![-1]);
|
|
||||||
assert_eq!(eval_stack("3 5 >="), vec![0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- Logic --
|
// -- Logic --
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+55
-105
@@ -1,10 +1,8 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
//! Cross-engine comparison tests: WAFER vs gforth (and `SwiftForth` for perf).
|
//! Cross-engine comparison tests: WAFER vs gforth.
|
||||||
//!
|
//!
|
||||||
//! Validates that WAFER produces identical output to gforth for standard
|
//! Validates that WAFER produces identical output to gforth for standard
|
||||||
//! Forth programs, and benchmarks performance of the engines. `SwiftForth`
|
//! Forth programs, and benchmarks performance of both engines.
|
||||||
//! (`sf64`, native-code commercial compiler) joins the performance report
|
|
||||||
//! as an upper-bound reference when installed.
|
|
||||||
//!
|
//!
|
||||||
//! WAFER-only correctness: `cargo test -p wafer-core --test comparison`
|
//! WAFER-only correctness: `cargo test -p wafer-core --test comparison`
|
||||||
//! Full comparison + perf: `cargo test -p wafer-core --test comparison -- --nocapture --ignored`
|
//! Full comparison + perf: `cargo test -p wafer-core --test comparison -- --nocapture --ignored`
|
||||||
@@ -65,46 +63,6 @@ fn find_gforth_fast() -> Option<&'static str> {
|
|||||||
.as_deref()
|
.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// SwiftForth (sf64) discovery (cached)
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
|
|
||||||
static SF64_PATH: OnceLock<Option<String>> = OnceLock::new();
|
|
||||||
|
|
||||||
/// Probe sf64 by piping `bye` via stdin — sf64 has no `-e` flag; it takes
|
|
||||||
/// Forth source from stdin or as bare command-line arguments.
|
|
||||||
fn probe_sf64(candidate: &str) -> bool {
|
|
||||||
run_via_stdin(candidate, "bye\n").is_some_and(|o| o.status.success())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_sf64() -> Option<&'static str> {
|
|
||||||
SF64_PATH
|
|
||||||
.get_or_init(|| {
|
|
||||||
for candidate in &["/Applications/ForthInc/SwiftForth/bin/macos/sf64", "sf64"] {
|
|
||||||
if probe_sf64(candidate) {
|
|
||||||
return Some(candidate.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.as_deref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Spawn `binary`, write `input` to its stdin, and collect the output.
|
|
||||||
fn run_via_stdin(binary: &str, input: &str) -> Option<std::process::Output> {
|
|
||||||
Command::new(binary)
|
|
||||||
.stdin(std::process::Stdio::piped())
|
|
||||||
.stdout(std::process::Stdio::piped())
|
|
||||||
.stderr(std::process::Stdio::piped())
|
|
||||||
.spawn()
|
|
||||||
.and_then(|mut child| {
|
|
||||||
use std::io::Write;
|
|
||||||
child.stdin.take().unwrap().write_all(input.as_bytes())?;
|
|
||||||
child.wait_with_output()
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Engine runners
|
// Engine runners
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
@@ -740,11 +698,31 @@ fn measure_wafer_release(wafer: &str, bench: &PerfBenchmark) -> Option<u64> {
|
|||||||
define = bench.define,
|
define = bench.define,
|
||||||
run = bench.run_code,
|
run = bench.run_code,
|
||||||
);
|
);
|
||||||
let output = run_via_stdin(wafer, &code)?;
|
let output = Command::new(wafer)
|
||||||
|
.stdin(std::process::Stdio::piped())
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.and_then(|mut child| {
|
||||||
|
use std::io::Write;
|
||||||
|
child.stdin.take().unwrap().write_all(code.as_bytes())?;
|
||||||
|
child.wait_with_output()
|
||||||
|
})
|
||||||
|
.ok()?;
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
median_printed_time(&output.stdout)
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let mut times: Vec<u64> = stdout
|
||||||
|
.trim()
|
||||||
|
.lines()
|
||||||
|
.filter_map(|l| l.trim().parse::<u64>().ok())
|
||||||
|
.collect();
|
||||||
|
times.sort();
|
||||||
|
if times.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(times[times.len() / 2])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Measure WAFER execution time after CONSOLIDATE (direct calls between all words).
|
/// Measure WAFER execution time after CONSOLIDATE (direct calls between all words).
|
||||||
@@ -756,17 +734,21 @@ fn measure_wafer_consolidated(wafer: &str, bench: &PerfBenchmark) -> Option<u64>
|
|||||||
define = bench.define,
|
define = bench.define,
|
||||||
run = bench.run_code,
|
run = bench.run_code,
|
||||||
);
|
);
|
||||||
let output = run_via_stdin(wafer, &code)?;
|
let output = Command::new(wafer)
|
||||||
|
.stdin(std::process::Stdio::piped())
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.and_then(|mut child| {
|
||||||
|
use std::io::Write;
|
||||||
|
child.stdin.take().unwrap().write_all(code.as_bytes())?;
|
||||||
|
child.wait_with_output()
|
||||||
|
})
|
||||||
|
.ok()?;
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
median_printed_time(&output.stdout)
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse the microsecond values printed by TIMED-BENCH (one per line) and
|
|
||||||
/// return the median.
|
|
||||||
fn median_printed_time(stdout: &[u8]) -> Option<u64> {
|
|
||||||
let stdout = String::from_utf8_lossy(stdout);
|
|
||||||
let mut times: Vec<u64> = stdout
|
let mut times: Vec<u64> = stdout
|
||||||
.trim()
|
.trim()
|
||||||
.lines()
|
.lines()
|
||||||
@@ -796,28 +778,18 @@ fn measure_gforth(gforth: &str, bench: &PerfBenchmark) -> Option<u64> {
|
|||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
median_printed_time(&output.stdout)
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
}
|
// Parse the 3 timing values and take the median
|
||||||
|
let mut times: Vec<u64> = stdout
|
||||||
/// Measure `SwiftForth` (`sf64`) execution time using Forth-level `ucounter`
|
.trim()
|
||||||
/// (double-cell microsecond counter; `2swap d- drop` yields elapsed us —
|
.lines()
|
||||||
/// the same wrapper shape as gforth's `utime`). Timing excludes startup.
|
.filter_map(|l| l.trim().parse::<u64>().ok())
|
||||||
/// sf64 has no `-e` flag, so the program is piped via stdin — one statement
|
.collect();
|
||||||
/// per line, because sf64 truncates input lines at ~256 chars.
|
times.sort();
|
||||||
/// Returns microseconds, or None if sf64 is unavailable or fails.
|
if times.is_empty() {
|
||||||
fn measure_sf64(sf64: &str, bench: &PerfBenchmark) -> Option<u64> {
|
|
||||||
let code = format!(
|
|
||||||
"{define}\n{run}\n\
|
|
||||||
: TIMED-BENCH ucounter {run} ucounter 2swap d- drop . cr ;\n\
|
|
||||||
TIMED-BENCH\nTIMED-BENCH\nTIMED-BENCH\nbye\n",
|
|
||||||
define = bench.define,
|
|
||||||
run = bench.run_code,
|
|
||||||
);
|
|
||||||
let output = run_via_stdin(sf64, &code)?;
|
|
||||||
if !output.status.success() {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
median_printed_time(&output.stdout)
|
Some(times[times.len() / 2])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -858,31 +830,18 @@ fn performance_report() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sf64 = find_sf64();
|
let sep = "=".repeat(80);
|
||||||
if sf64.is_none() {
|
let thin = "-".repeat(80);
|
||||||
eprintln!("NOTE: sf64 (SwiftForth) not found — column skipped");
|
|
||||||
}
|
|
||||||
|
|
||||||
let sep = "=".repeat(100);
|
|
||||||
let thin = "-".repeat(100);
|
|
||||||
println!("\n{sep}");
|
println!("\n{sep}");
|
||||||
println!(" WAFER vs Gforth vs SwiftForth Performance Comparison (release mode)");
|
println!(" WAFER vs Gforth Performance Comparison (release mode)");
|
||||||
println!("{sep}\n");
|
println!("{sep}\n");
|
||||||
println!(
|
println!(
|
||||||
"{:<22} {:>9} {:>9} {:>9} {:>9} {:>9} {:>9} {:>9} {:>9}",
|
"{:<22} {:>10} {:>10} {:>10} {:>10} {:>10} {:>10}",
|
||||||
"Benchmark",
|
"Benchmark", "WAFER", "CONSOL", "gforth", "gf-fast", "WAFER/gf", "limit"
|
||||||
"WAFER",
|
|
||||||
"CONSOL",
|
|
||||||
"gforth",
|
|
||||||
"gf-fast",
|
|
||||||
"sf64",
|
|
||||||
"WAFER/gf",
|
|
||||||
"WAFER/sf",
|
|
||||||
"limit"
|
|
||||||
);
|
);
|
||||||
println!(
|
println!(
|
||||||
"{:<22} {:>9} {:>9} {:>9} {:>9} {:>9} {:>9} {:>9} {:>9}",
|
"{:<22} {:>10} {:>10} {:>10} {:>10} {:>10} {:>10}",
|
||||||
"", "(us)", "(us)", "(us)", "(us)", "(us)", "", "", ""
|
"", "(us)", "(us)", "(us)", "(us)", "", ""
|
||||||
);
|
);
|
||||||
println!("{thin}");
|
println!("{thin}");
|
||||||
|
|
||||||
@@ -897,11 +856,9 @@ fn performance_report() {
|
|||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let gf = gforth.and_then(|g| measure_gforth(g, bench));
|
let gf = gforth.and_then(|g| measure_gforth(g, bench));
|
||||||
let gf_fast = gforth_fast.and_then(|g| measure_gforth(g, bench));
|
let gf_fast = gforth_fast.and_then(|g| measure_gforth(g, bench));
|
||||||
let sf = sf64.and_then(|s| measure_sf64(s, bench));
|
|
||||||
|
|
||||||
let gf_str = gf.map_or_else(|| "-".to_string(), |v| format!("{v}"));
|
let gf_str = gf.map_or_else(|| "-".to_string(), |v| format!("{v}"));
|
||||||
let gf_fast_str = gf_fast.map_or_else(|| "-".to_string(), |v| format!("{v}"));
|
let gf_fast_str = gf_fast.map_or_else(|| "-".to_string(), |v| format!("{v}"));
|
||||||
let sf_str = sf.map_or_else(|| "-".to_string(), |v| format!("{v}"));
|
|
||||||
let best_wafer = if consol > 0 && consol < wafer {
|
let best_wafer = if consol > 0 && consol < wafer {
|
||||||
consol
|
consol
|
||||||
} else {
|
} else {
|
||||||
@@ -915,15 +872,11 @@ fn performance_report() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
let ratio = ratio_val.map_or_else(|| "-".to_string(), |r| format!("{r:.2}x"));
|
let ratio = ratio_val.map_or_else(|| "-".to_string(), |r| format!("{r:.2}x"));
|
||||||
let sf_ratio = sf.filter(|&s| s > 0).map_or_else(
|
|
||||||
|| "-".to_string(),
|
|
||||||
|s| format!("{:.2}x", best_wafer as f64 / s as f64),
|
|
||||||
);
|
|
||||||
let limit_str = format!("{:.2}x", bench.max_ratio);
|
let limit_str = format!("{:.2}x", bench.max_ratio);
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"{:<22} {:>9} {:>9} {:>9} {:>9} {:>9} {:>9} {:>9} {:>9}",
|
"{:<22} {:>10} {:>10} {:>10} {:>10} {:>10} {:>10}",
|
||||||
bench.name, wafer, consol, gf_str, gf_fast_str, sf_str, ratio, sf_ratio, limit_str
|
bench.name, wafer, consol, gf_str, gf_fast_str, ratio, limit_str
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check regression limits
|
// Check regression limits
|
||||||
@@ -946,9 +899,6 @@ fn performance_report() {
|
|||||||
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!(
|
|
||||||
" WAFER/sf = best(WAFER,CONSOL) vs SwiftForth sf64 (native code; informational, no limit)"
|
|
||||||
);
|
|
||||||
println!("{sep}\n");
|
println!("{sep}\n");
|
||||||
|
|
||||||
if !regressions.is_empty() {
|
if !regressions.is_empty() {
|
||||||
|
|||||||
Reference in New Issue
Block a user