GXRuntime: inline the hot paths of the FP gate and paired-single access - #9
Open
dougchansan wants to merge 1 commit into
Open
Conversation
Profiling a recompiled Mario Kart: Double Dash on Apple Silicon (`sample`, race
scene) put these near the top of self time inside StaticRecompCore::Run:
ppc_fp_available 869 samples ~7.1%
psq_load_value + psq_store_value 1101 samples ~9.0%
Generated code calls them once per instruction site -- 121,874 FP sites and
19,642 paired-single sites on this title.
ppc_fp_available_inline() puts the MSR[FP] test in the header and leaves only
the exception raise out of line. A running game always has MSR[FP] set, so the
cost was the call, not the test.
ppc_psq_load_inline()/ppc_psq_store_inline() handle GQR type 0 -- plain IEEE
singles, no quantisation, no scale, which is what games leave GQR0 at -- as two
32-bit accesses and a conversion. Quantised types (4..7), invalid types and the
LSQE illegal-instruction check all fall through to the existing out-of-line
functions, so behaviour is unchanged. Nothing new had to be exposed:
f64_value, convert_to_double, convert_to_single_ftz and f64_bits were already
static inline in core/types.h.
ppc_fp_available(), ppc_psq_load() and ppc_psq_store() all remain real symbols.
The LLVM backend emits calls to them by name.
Measured on an idle Apple M5, alternating A/B with a reversed-order block:
inline FP gate +11.8% ranges do not overlap
inline paired-single +4.0% ranges do not overlap
Both beat their profiled share, because an out-of-line call is also an
optimisation barrier: at every generated site w, gqr_index, indexed and cia are
literals, so inlining lets the compiler fold the LSQE test and the w branch away
per site. The paired-single module came out slightly SMALLER despite 19,642
inlined sites, which is that folding showing up.
Together with PGO on top (+7.5%), MKDD under static recompilation on Metal went
51.1 -> 62.5 fps single-player and 40.6 -> 50.8 fps split-screen.
Pairs with the DolRecomp change that emits the _inline forms. This one should
land first: with the emitter updated and these absent, a port fails to compile.
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.
Profiling a recompiled Mario Kart: Double Dash on Apple Silicon (
sample, race scene) put two runtime helpers near the top of self time insideStaticRecompCore::Run:ppc_fp_availablepsq_load_value+psq_store_valueGenerated code calls them once per instruction site — 121,874 FP sites and 19,642 paired-single sites on this title.
What changed
ppc_fp_available_inline()puts the MSR[FP] test in the header, leaving only the exception raise out of line. A running game always has MSR[FP] set, so the cost was the call, not the test.ppc_psq_load_inline()/ppc_psq_store_inline()handle GQR type 0 — plain IEEE singles, no quantisation, no scale, which is what games leave GQR0 at — as two 32-bit accesses and a conversion. Quantised types (4..7), invalid types, and the LSQE illegal-instruction check all fall through to the existing out-of-line functions, so behaviour is unchanged.Nothing new had to be exposed:
f64_value,convert_to_double,convert_to_single_ftzandf64_bitswere alreadystatic inlineincore/types.h, and the memory accessors andPPC_HID2_LSQEwere already header-visible.ppc_fp_available(),ppc_psq_load()andppc_psq_store()all remain real symbols — the LLVM backend emits calls to them by name.Measured
Idle Apple M5, alternating A/B with a reversed-order block, largest competing process under 70%:
Both beat their profiled share. An out-of-line call is also an optimisation barrier: at every generated site
w,gqr_index,indexedandciaare literals, so inlining lets the compiler fold the LSQE test and thewbranch away per site. The paired-single module came out slightly smaller despite 19,642 inlined sites, which is that folding showing up.With PGO on top (+7.5%), MKDD under static recompilation on Metal went 51.1 → 62.5 fps single-player and 40.6 → 50.8 fps split-screen.
Ordering
Pairs with a DolRecomp change that emits the
_inlineforms. This one should land first — with the emitter updated and these absent, a port fails to compile.