Phase 10.7 - JIT統計とイベント機能の完成
主要な実装: - PHI(b1)統計追跡: phi_total_slots/phi_b1_slotsをJSON出力 - 関数単位統計API: JitStatsBox.perFunction()で詳細統計取得 - JITイベントシステム: compile/execute/fallback/trapをJSONL形式で記録 - Store/Load命令対応: ローカル変数を含む関数のJIT実行が可能に 新しいBox: - JitStatsBox: JIT統計の取得 - JitConfigBox: JIT設定の管理(将来用) - JitEventsBox: イベントのJSONL出力(将来用) - JitPolicyBox: 実行ポリシー管理(将来用) CLI拡張: - --jit-exec, --jit-stats, --jit-dump等のフラグ追加 - --jit-directモードでの独立JIT実行 - NYASH_JIT_*環境変数によるきめ細かい制御 ドキュメント: - Phase 10.7実装計画の詳細化 - Phase 10.9 (ビルトインBox JIT) の計画追加 - JIT統計JSONスキーマ v1の仕様化 ChatGPT5との共同開発により、JIT基盤が大幅に強化されました。 次はPhase 10.9でビルトインBoxのJIT対応を進め、 Python統合(Phase 10.1)への道を開きます。 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -5,8 +5,9 @@ impl NyashRunner {
|
||||
/// Execute benchmark mode (split)
|
||||
pub(crate) fn execute_benchmark_mode(&self) {
|
||||
println!("🏁 Running benchmark mode with {} iterations", self.config.iterations);
|
||||
// Two tests: simple add, arithmetic loop
|
||||
let tests: Vec<(&str, &str)> = vec![
|
||||
// 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![
|
||||
(
|
||||
"simple_add",
|
||||
r#"
|
||||
@ -16,6 +17,7 @@ impl NyashRunner {
|
||||
y = x + 58
|
||||
return y
|
||||
"#,
|
||||
false,
|
||||
),
|
||||
(
|
||||
"arith_loop_100k",
|
||||
@ -29,6 +31,7 @@ impl NyashRunner {
|
||||
}
|
||||
return sum
|
||||
"#,
|
||||
false,
|
||||
),
|
||||
(
|
||||
"branch_return",
|
||||
@ -42,33 +45,63 @@ impl NyashRunner {
|
||||
return 2
|
||||
}
|
||||
"#,
|
||||
false,
|
||||
),
|
||||
(
|
||||
"f64_add_jit",
|
||||
r#"
|
||||
local x, y
|
||||
x = 1.5
|
||||
y = 2.25
|
||||
return x + y
|
||||
"#,
|
||||
true,
|
||||
),
|
||||
];
|
||||
|
||||
for (name, code) in tests {
|
||||
for (name, code, jit_f64_only) in tests {
|
||||
println!("\n====================================");
|
||||
println!("🧪 Test: {}", name);
|
||||
// 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);
|
||||
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);
|
||||
|
||||
// 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);
|
||||
// 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);
|
||||
|
||||
// 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" });
|
||||
// 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" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn bench_interpreter(&self, code: &str, iters: u32) -> std::time::Duration {
|
||||
// Enable native f64 when available to exercise widened ABI
|
||||
std::env::set_var("NYASH_JIT_NATIVE_F64", "1");
|
||||
let start = std::time::Instant::now();
|
||||
for _ in 0..iters {
|
||||
if let Ok(ast) = NyashParser::parse_from_string(code) {
|
||||
@ -101,6 +134,8 @@ impl NyashRunner {
|
||||
// Force JIT mode for this run
|
||||
std::env::set_var("NYASH_JIT_EXEC", "1");
|
||||
std::env::set_var("NYASH_JIT_THRESHOLD", "1");
|
||||
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"); }
|
||||
let start = std::time::Instant::now();
|
||||
for _ in 0..iters {
|
||||
if let Ok(ast) = NyashParser::parse_from_string(code) {
|
||||
@ -115,4 +150,32 @@ impl NyashRunner {
|
||||
println!(" 🔥 JIT: {} iters in {:?} ({:.2} ops/sec)", iters, elapsed, iters as f64 / elapsed.as_secs_f64());
|
||||
elapsed
|
||||
}
|
||||
|
||||
/// 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(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user