StaticRecomp: skip guest busy-wait loops the analyzer can identify - #19
Open
dougchansan wants to merge 1 commit into
Open
Conversation
The dispatcher already hands the rest of its slice back to CoreTiming when the guest reaches MAIN_STATICRECOMP_IDLE_PC, but that only covers loops configured ahead of time. Everything else is spun through in real time. A dispatch that returns to the address it entered at is a self-loop. Running PPCAnalyst over that address identifies whether it is an idle loop (a branch with branchIsIdleLoop back to the entry), and if so there is nothing to gain from executing it until the next event. The result is cached per address and invalidated wherever guest code can change: ClearCache and SMC re-verification. Analysis is skipped entirely when a debugger or branch watch is active, since both want every block to actually execute. Measured on x86-64 with static recompilation active, frames advanced in a fixed 20s window, identical binary in both arms with detection toggled at runtime, so nothing else differs. Input was pinned (no host device bound, BackgroundInput off) and only one emulator ran at a time: title detection on off ratio t 95% CI Skyward Sword gameplay 796.2 (n=6) 571.8 1.392 10.47 1.317-1.467 Luigi's Mansion foyer 751.9 (n=8) 672.1 1.119 3.58 1.052-1.185 Mario Kart DD bench 1033.5 (n=6) 965.3 1.071 2.64 1.017-1.124 Pokemon Colosseum (2 scenes) 0.98-1.01 neutral No title regressed. The harness noise floor was measured separately by running the same module as both control and arm: 12 of 13 such null comparisons landed within +/-1.72% of 1.0, so the three wins above sit well outside it. The win is opportunistic by nature -- detection only fires when a dispatch happens to re-enter at the loop head -- which shows up as higher variance in the detection-on arm (sd 46-60 vs 18-25 off). It is a consistent gain, not a uniform one.
Author
|
Note on the measurement environment, since it came up while working on ModernGekko #34. Every number in this PR was taken with The ratios here are unaffected. I checked this directly rather than assuming it: pinning lifts the C and LLVM arms by the same amount (+13.2% each on Colosseum, n=6 per cell), and the arm/control ratio moves +0.6%, which is noise. Both arms carried the same handicap, so the comparisons stand exactly as reported. Two things worth knowing for anyone reproducing these figures:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The dispatcher already returns the rest of its slice to
CoreTimingwhen the guest reachesMAIN_STATICRECOMP_IDLE_PC, but that only covers loops configured ahead of time. Every other idle loop is spun through in real time.A dispatch that returns to the address it entered at is a self-loop. Running
PPCAnalystover that address tells us whether it is an idle loop (a branch withbranchIsIdleLoopback to the entry); if it is, there is nothing to gain from executing it until the next event.ClearCacheand SMC re-verificationanalyzer,code_block,m_code_bufferall come fromJitBase)Measurements
x86-64, static recompilation active, frames advanced in a fixed 20s window. The same binary is used for both arms, with detection toggled at runtime, so nothing but this behaviour differs.
No title regressed. On Skyward Sword the arms do not overlap at all: the worst detection-on run (730 frames) still beats the best off run (606).
Why these numbers can be trusted
The noise floor was measured rather than assumed, by supplying the same module as both control and arm so the true ratio is exactly 1.0000:
Single runs therefore carry a rare ~15% excursion, and every figure above is a mean over repeated runs rather than one measurement.
Three confounds had to be controlled before the effect was measurable at all, each of which produced plausible-looking but wrong numbers:
BackgroundInput = Falsethe detection-on arm spreads 651–810 frames; without that pinning the same arm spread 829–7157, an 8.6x range.WiimoteNew.ini, not from the savestate, so omittingExtension = Nunchukleaves Skyward Sword sitting on a "connect the Nunchuk" prompt. That measured +119% and was an artifact of the dialog, not of this change.Honest characterisation
The win is opportunistic: detection only fires when a dispatch happens to re-enter at the loop head. That shows up as higher variance in the detection-on arm (sd 46–60, against 18–25 with it off). It is a consistent gain, not a uniform one, and Colosseum shows it can also be simply neutral.