[iterate-4B] perf: quick hot-path wins (bulk quantum-decrement + ctx coalesce)

Two byte-identical interpreter-dispatch optimizations (headless golden
n200m unchanged). Together ~13% faster on the -n 200M --gpu-inline
benchmark (4.33s -> ~3.8s, ~46 -> ~53 MIPS).

- scheduler.rs: `decrement_quantum_by(n)` — the superblock epilogue looped
  `for _ in 0..executed { decrement_quantum() }` (~one bounds-checked call
  per retired guest instruction, the largest fixed per-superblock cost).
  QUANTUM_DEFAULT (50k) >> a superblock's instr count, so the quantum
  boundary is crossed at most once/call: common path is one subtraction,
  the rare boundary step defers to decrement_quantum for exact rotation
  (reload + same-priority peer hand-off) semantics. Byte-identical.
- main.rs run_superblock: resolve the running thread's PpcContext ONCE per
  block (was three `ctx_mut_ref` double-indexed slot lookups: cycle-before,
  the step, cycle-after).

Profiled remaining breakdown (-n 300M --gpu-inline): step_block body 50%
(67 MIPS, the JIT target), block decode/cache 9% (page-version-bound),
kernel HLE 8%, scheduler/lock remainder ~31%. Sub-10% cheap wins remain
(block-linking is page-version-gated so only partial); the 5-10x lift is
the JIT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-03 23:03:46 +02:00
parent 913b566a26
commit 9851873e42
2 changed files with 57 additions and 11 deletions

View File

@@ -2946,9 +2946,9 @@ fn worker_epilogue(
stats.instruction_count = stats.instruction_count.wrapping_add(executed);
for _ in 0..executed {
kernel.scheduler.decrement_quantum();
}
// PERF: byte-identical bulk decrement (was `for _ in 0..executed`), the
// largest fixed per-superblock cost — see `Scheduler::decrement_quantum_by`.
kernel.scheduler.decrement_quantum_by(executed);
match result {
StepResult::Continue => {}
@@ -3142,19 +3142,19 @@ fn run_superblock(
let mut total_executed: u64 = 0;
let (result, last_block_ptr, last_pc_before) = loop {
let cycle_before = kernel.scheduler.ctx_mut_ref(thread_ref).cycle_count;
let mmio_before = mem.mmio_access_count();
let block = unsafe { &*block_ptr };
let _prof_t0 = xenia_gpu::prof::is_on().then(std::time::Instant::now);
let result = {
// PERF: resolve the running thread's context ONCE per block (was three
// `ctx_mut_ref` slot lookups — for cycle-before, the step, and
// cycle-after — each a double bounds-checked index). Byte-identical.
let (result, executed) = {
let ctx = kernel.scheduler.ctx_mut_ref(thread_ref);
step_block(ctx, mem, block)
let cycle_before = ctx.cycle_count;
let result = step_block(ctx, mem, block);
let executed = ctx.cycle_count.saturating_sub(cycle_before);
(result, executed)
};
let executed = kernel
.scheduler
.ctx_mut_ref(thread_ref)
.cycle_count
.saturating_sub(cycle_before);
if let Some(t0) = _prof_t0 {
use xenia_gpu::prof;
prof::add(&prof::STEP_NS, t0.elapsed().as_nanos() as u64);

View File

@@ -1016,6 +1016,52 @@ impl Scheduler {
false
}
/// Bulk equivalent of calling [`Self::decrement_quantum`] exactly `n`
/// times, producing a **byte-identical** final scheduler state. PERF: the
/// superblock epilogue used to loop `for _ in 0..executed { decrement_quantum() }`
/// — up to ~128 bounds-checked calls per superblock, ~one per retired guest
/// instruction across the whole run (the single largest fixed per-superblock
/// cost). Since `QUANTUM_DEFAULT` (50_000) ≫ a superblock's instruction
/// count, the quantum boundary is crossed at most once per call, so the
/// common path is a single subtraction (O(1)); only the rare
/// boundary-crossing step defers to `decrement_quantum` to reproduce the
/// exact rotation semantics (reload + same-priority peer hand-off).
pub fn decrement_quantum_by(&mut self, mut n: u64) {
while n > 0 {
let Some(r) = self.current else {
return;
};
let Some(t) = self.slots[r.hw_id as usize]
.runqueue
.get_mut(r.idx as usize)
else {
return;
};
let q = t.quantum_remaining as u64;
if q > n {
// No quantum boundary within these `n` steps — the common
// case. Identical to `n` plain decrements that each hit the
// early `quantum_remaining != 0` return.
t.quantum_remaining = (q - n) as u32;
return;
}
// A rotation (quantum reload + optional peer hand-off) occurs
// within these `n` steps. Collapse the `q` leading no-op
// decrements into one rotating single-step: set the quantum to 1
// so the next `decrement_quantum` drives it to 0 and rotates with
// identical semantics (for `q == 0` the first single-step already
// rotates, consuming exactly one step).
let consumed = if q > 0 {
t.quantum_remaining = 1;
q
} else {
1
};
self.decrement_quantum();
n -= consumed;
}
}
/// Cooperative yield: the currently-running thread executed a `db16cyc`
/// spin-wait hint (see `StepResult::Yield`). It is busy-spinning on a
/// guest spinlock/barrier whose release depends on a *co-located* peer