2025-08-26 04:34:14 +09:00
use super ::super ::NyashRunner ;
use nyash_rust ::{ parser ::NyashParser , interpreter ::NyashInterpreter , box_factory ::builtin ::BuiltinGroups , mir ::MirCompiler , backend ::VM } ;
impl NyashRunner {
/// Execute benchmark mode (split)
pub ( crate ) fn execute_benchmark_mode ( & self ) {
println! ( " 🏁 Running benchmark mode with {} iterations " , self . config . iterations ) ;
2025-08-28 09:26:58 +09:00
// Tests: some run on all backends, some are JIT+f64 only
// Third element indicates JIT+f64 only (skip VM/Interpreter)
let tests : Vec < ( & str , & str , bool ) > = vec! [
2025-08-27 17:06:46 +09:00
(
" simple_add " ,
r #"
local x
x = 42
local y
y = x + 58
return y
" #,
2025-08-28 09:26:58 +09:00
false ,
2025-08-27 17:06:46 +09:00
) ,
(
" arith_loop_100k " ,
r #"
local i , sum
i = 0
sum = 0
loop ( i < 100000 ) {
sum = sum + i
i = i + 1
}
return sum
" #,
2025-08-28 09:26:58 +09:00
false ,
2025-08-27 17:06:46 +09:00
) ,
2025-08-27 17:39:12 +09:00
(
" branch_return " ,
r #"
local a , b
a = 3
b = 5
if ( a < b ) {
return 1
} else {
return 2
}
" #,
2025-08-28 09:26:58 +09:00
false ,
) ,
(
" f64_add_jit " ,
r #"
local x , y
x = 1.5
y = 2.25
return x + y
" #,
true ,
2025-08-27 17:39:12 +09:00
) ,
2025-08-27 17:06:46 +09:00
] ;
2025-08-28 09:26:58 +09:00
for ( name , code , jit_f64_only ) in tests {
2025-08-27 17:06:46 +09:00
println! ( " \n ==================================== " ) ;
println! ( " 🧪 Test: {} " , name ) ;
2025-08-28 09:26:58 +09:00
if jit_f64_only {
println! ( " (JIT+f64 only) Skipping VM/Interpreter; requires --features cranelift-jit " ) ;
// Warmup JIT
let warmup = ( self . config . iterations / 10 ) . max ( 1 ) ;
self . bench_jit ( code , warmup ) ;
// Measured
let jit_time = self . bench_jit ( code , self . config . iterations ) ;
println! ( " \n 📊 Performance Summary [ {} ]: " , name ) ;
println! ( " JIT f64 ops: {} iters in {:?} ( {:.2} ops/sec) " , self . config . iterations , jit_time , self . config . iterations as f64 / jit_time . as_secs_f64 ( ) ) ;
} else {
// Quick correctness check across modes (golden): Interpreter vs VM vs VM+JIT
if let Err ( e ) = self . verify_outputs_match ( code ) {
println! ( " ❌ Output mismatch: {} " , e ) ;
} else {
println! ( " ✅ Outputs match across Interpreter/VM/JIT " ) ;
}
// Warmup (not measured)
let warmup = ( self . config . iterations / 10 ) . max ( 1 ) ;
self . bench_interpreter ( code , warmup ) ;
self . bench_vm ( code , warmup ) ;
self . bench_jit ( code , warmup ) ;
2025-08-26 04:34:14 +09:00
2025-08-28 09:26:58 +09:00
// Measured runs
let interpreter_time = self . bench_interpreter ( code , self . config . iterations ) ;
let vm_time = self . bench_vm ( code , self . config . iterations ) ;
let jit_time = self . bench_jit ( code , self . config . iterations ) ;
2025-08-27 17:06:46 +09:00
2025-08-28 09:26:58 +09:00
// Summary
let vm_vs_interp = interpreter_time . as_secs_f64 ( ) / vm_time . as_secs_f64 ( ) ;
let jit_vs_vm = vm_time . as_secs_f64 ( ) / jit_time . as_secs_f64 ( ) ;
println! ( " \n 📊 Performance Summary [ {} ]: " , name ) ;
println! ( " VM is {:.2} x {} than Interpreter " , if vm_vs_interp > 1.0 { vm_vs_interp } else { 1.0 / vm_vs_interp } , if vm_vs_interp > 1.0 { " faster " } else { " slower " } ) ;
println! ( " JIT is {:.2} x {} than VM (note: compile cost included) " , if jit_vs_vm > 1.0 { jit_vs_vm } else { 1.0 / jit_vs_vm } , if jit_vs_vm > 1.0 { " faster " } else { " slower " } ) ;
}
2025-08-27 17:06:46 +09:00
}
}
2025-08-26 04:34:14 +09:00
2025-08-27 17:06:46 +09:00
fn bench_interpreter ( & self , code : & str , iters : u32 ) -> std ::time ::Duration {
2025-08-28 09:26:58 +09:00
// Enable native f64 when available to exercise widened ABI
std ::env ::set_var ( " NYASH_JIT_NATIVE_F64 " , " 1 " ) ;
2025-08-26 04:34:14 +09:00
let start = std ::time ::Instant ::now ( ) ;
2025-08-27 17:06:46 +09:00
for _ in 0 .. iters {
if let Ok ( ast ) = NyashParser ::parse_from_string ( code ) {
2025-08-26 04:34:14 +09:00
let mut interp = NyashInterpreter ::new_with_groups ( BuiltinGroups ::native_full ( ) ) ;
let _ = interp . execute ( ast ) ;
}
}
2025-08-27 17:06:46 +09:00
let elapsed = start . elapsed ( ) ;
println! ( " ⚡ Interpreter: {} iters in {:?} ( {:.2} ops/sec) " , iters , elapsed , iters as f64 / elapsed . as_secs_f64 ( ) ) ;
elapsed
}
2025-08-26 04:34:14 +09:00
2025-08-27 17:06:46 +09:00
fn bench_vm ( & self , code : & str , iters : u32 ) -> std ::time ::Duration {
2025-08-26 04:34:14 +09:00
let start = std ::time ::Instant ::now ( ) ;
2025-08-27 17:06:46 +09:00
for _ in 0 .. iters {
if let Ok ( ast ) = NyashParser ::parse_from_string ( code ) {
2025-08-26 04:34:14 +09:00
let mut mc = MirCompiler ::new ( ) ;
if let Ok ( cr ) = mc . compile ( ast ) {
let mut vm = VM ::new ( ) ;
let _ = vm . execute_module ( & cr . module ) ;
}
}
}
2025-08-27 17:06:46 +09:00
let elapsed = start . elapsed ( ) ;
println! ( " 🚀 VM: {} iters in {:?} ( {:.2} ops/sec) " , iters , elapsed , iters as f64 / elapsed . as_secs_f64 ( ) ) ;
elapsed
}
2025-08-26 04:34:14 +09:00
2025-08-27 17:06:46 +09:00
fn bench_jit ( & self , code : & str , iters : u32 ) -> std ::time ::Duration {
// Force JIT mode for this run
std ::env ::set_var ( " NYASH_JIT_EXEC " , " 1 " ) ;
std ::env ::set_var ( " NYASH_JIT_THRESHOLD " , " 1 " ) ;
2025-08-28 09:26:58 +09:00
if self . config . jit_stats { std ::env ::set_var ( " NYASH_JIT_STATS " , " 1 " ) ; }
if self . config . jit_stats_json { std ::env ::set_var ( " NYASH_JIT_STATS_JSON " , " 1 " ) ; }
2025-08-27 17:06:46 +09:00
let start = std ::time ::Instant ::now ( ) ;
for _ in 0 .. iters {
if let Ok ( ast ) = NyashParser ::parse_from_string ( code ) {
let mut mc = MirCompiler ::new ( ) ;
if let Ok ( cr ) = mc . compile ( ast ) {
let mut vm = VM ::new ( ) ;
let _ = vm . execute_module ( & cr . module ) ;
}
}
}
let elapsed = start . elapsed ( ) ;
println! ( " 🔥 JIT: {} iters in {:?} ( {:.2} ops/sec) " , iters , elapsed , iters as f64 / elapsed . as_secs_f64 ( ) ) ;
elapsed
2025-08-26 04:34:14 +09:00
}
2025-08-28 09:26:58 +09:00
/// Verify that outputs match across VM and JIT-enabled VM (golden)
fn verify_outputs_match ( & self , code : & str ) -> Result < ( ) , String > {
// VM
let vm_out = {
let ast = NyashParser ::parse_from_string ( code ) . map_err ( | e | format! ( " vm parse: {} " , e ) ) ? ;
let mut mc = MirCompiler ::new ( ) ;
let cr = mc . compile ( ast ) . map_err ( | e | format! ( " vm compile: {} " , e ) ) ? ;
let mut vm = VM ::new ( ) ;
let out = vm . execute_module ( & cr . module ) . map_err ( | e | format! ( " vm exec: {} " , e ) ) ? ;
out . to_string_box ( ) . value
} ;
// VM+JIT
let jit_out = {
std ::env ::set_var ( " NYASH_JIT_EXEC " , " 1 " ) ;
std ::env ::set_var ( " NYASH_JIT_THRESHOLD " , " 1 " ) ;
let ast = NyashParser ::parse_from_string ( code ) . map_err ( | e | format! ( " jit parse: {} " , e ) ) ? ;
let mut mc = MirCompiler ::new ( ) ;
let cr = mc . compile ( ast ) . map_err ( | e | format! ( " jit compile: {} " , e ) ) ? ;
let mut vm = VM ::new ( ) ;
let out = vm . execute_module ( & cr . module ) . map_err ( | e | format! ( " jit exec: {} " , e ) ) ? ;
out . to_string_box ( ) . value
} ;
if vm_out ! = jit_out {
return Err ( format! ( " vm=' {} ' jit=' {} ' " , vm_out , jit_out ) ) ;
}
Ok ( ( ) )
}
2025-08-26 04:34:14 +09:00
}