[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:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user