[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 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-03 23:10:07 +02:00
parent 9851873e42
commit 77c2d7bce9

View File

@@ -3296,6 +3296,17 @@ fn run_execution(
// loop doesn't heap-allocate a `Vec<u8>` 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
}