From 77c2d7bce9147944f18a49640e7e84af2eb07bc9 Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Fri, 3 Jul 2026 23:10:07 +0200 Subject: [PATCH] [iterate-4B] spike: multi-core feasibility measurement (concurrency probe) Throwaway diagnostic for the multi-core-vs-JIT decision. Two real numbers: 1. Existing --parallel is ~25x SLOWER, not faster: 100.9s wall vs 4.0s lockstep at -n 200M --gpu-inline (237s user across 6 threads burned on coarse-mutex + phaser contention). Naive coarse-locking inverts the win. 2. XENIA_CONCURRENCY_PROBE (env-gated, zero cost off): per-round histogram of runnable HW-slot width. Sylpheed at -n 300M: AVG WIDTH = 3.83 (width>=4 in 77% of rounds) => a *perfect* host-thread-per-guest-thread design has a ~3.8x Amdahl ceiling. The parallelism is real; the existing vehicle just can't capture it. Golden n200m byte-identical (probe inert unless env set). Co-Authored-By: Claude Opus 4.8 --- crates/xenia-app/src/main.rs | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/crates/xenia-app/src/main.rs b/crates/xenia-app/src/main.rs index c6344e9..b663d18 100644 --- a/crates/xenia-app/src/main.rs +++ b/crates/xenia-app/src/main.rs @@ -3296,6 +3296,17 @@ fn run_execution( // loop doesn't heap-allocate a `Vec` every iteration. let mut order_buf = [0u8; xenia_cpu::scheduler::HW_THREAD_COUNT]; + // SPIKE (multi-core feasibility, 2026-07-03): env-gated histogram of the + // per-round "runnable width" — how many HW slots hold a Ready thread at + // once. The average width bounds the best-case multi-core speedup (Amdahl): + // lockstep runs the round's slots serially; a perfect host-thread-per-guest + // design runs them concurrently, so wall shrinks by ~avg-width. Zero cost + // unless `XENIA_CONCURRENCY_PROBE` is set. Throwaway diagnostic. + let concurrency_probe = std::env::var("XENIA_CONCURRENCY_PROBE").is_ok(); + let mut width_hist = [0u64; xenia_cpu::scheduler::HW_THREAD_COUNT + 1]; + let mut rounds_with_work = 0u64; + let mut slot_visits = 0u64; + 'outer: loop { // Per-round prologue: budget / shutdown / heartbeat / vsync / // timers / audio-interrupt injection. Carved into @@ -3357,6 +3368,14 @@ fn run_execution( let order_n = kernel.scheduler.round_schedule_into(&mut order_buf); let order = &order_buf[..order_n]; + if concurrency_probe { + width_hist[order_n] += 1; + if order_n > 0 { + rounds_with_work += 1; + slot_visits += order_n as u64; + } + } + if order.is_empty() { // No Ready threads — advance time to the earliest pending // deadline, fire timers, handle deadline wakes, and on hard @@ -3432,6 +3451,31 @@ fn run_execution( RoundCtl::Continue => {} } } + if concurrency_probe { + let avg_width = if rounds_with_work > 0 { + slot_visits as f64 / rounds_with_work as f64 + } else { + 0.0 + }; + eprintln!("=== XENIA_CONCURRENCY_PROBE (multi-core Amdahl ceiling) ==="); + for (w, &c) in width_hist.iter().enumerate() { + let pct = if rounds_with_work + width_hist[0] > 0 { + 100.0 * c as f64 / (rounds_with_work + width_hist[0]) as f64 + } else { + 0.0 + }; + eprintln!(" runnable-width {w}: {c:>12} rounds ({pct:>5.1}%)"); + } + eprintln!( + " rounds_with_work={rounds_with_work} slot_visits={slot_visits} \ + idle_rounds={}", + width_hist[0] + ); + eprintln!( + " AVG RUNNABLE WIDTH = {avg_width:.3} => best-case multi-core \ + speedup ceiling ~= {avg_width:.2}x (concurrent slots / round)" + ); + } stats }