Skip to content

perf(recompiler): memoize SRT evaluation in a flat table and inline IrValue's hot accessors - #415

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
oneandonlydean:perf/recompiler-srt-memo-table
Oct 3, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
oneandonlydean:perf/recompiler-srt-memo-table

Conversation

@oneandonlydean

Copy link
Copy Markdown
Collaborator

What

Every draw stage's capture walks the shader's SRT plan with Detail::Evaluator. Its per-value memo was a std::unordered_map<IrValue*, std::uint64_t>: one node allocation per evaluated value. Every step of the walk also made out-of-line calls to IrValue::Opcode, IsIdentity, Resolve and Argument. In a profile of my integration branch at PPSA21564's title, the walk took about 17 us of every capture, about 1.25 s of the graphics queue worker's 10 s.

  • The memo is now Detail::EvaluatedValues, an open-addressing table keyed by the value pointer (linear probing, power-of-two size starting at 64, grown at half load). Insert keeps the first value for a key, as emplace did, and failed evaluations are still not memoized.
  • IrValue::Opcode, Type, HasImmediate, IsIdentity, ArgumentCount, Argument and Resolve move to the header as inline definitions, unchanged, including their throws.
  • <unordered_map> is no longer used by SrtEvaluator.hpp and is dropped.

Evaluation order, results and failures are unchanged. In the same profile the capture walk drops to about 10 us (title capture total about 1.25 s to 0.7 s per 10 s).

Test: verifyEvaluatedValues in agc_shader_memory_tests inserts 1000 keys (several grows), finds each with its value (including a stored 0), checks that a second insert does not replace the first, and that empty tables and absent keys find nothing. The existing SRT walker tests cover the evaluator itself.

Relation to #218 (slick-daddy, "cut hot-path overhead in recompile cache lookups"): the two are complementary. #218 changes the evaluator's cycle stack (_visiting, a vector to an unordered_set) and the cache key, snapshot hash and memo lookups in Recompiler.cpp. This PR changes the evaluator's value memo (_cache) and the IrValue accessors. They touch adjacent lines in SrtEvaluator.hpp only: the member pair and the include block. Whichever lands second resolves to EvaluatedValues _cache; beside #218's std::unordered_set<IrValue*> _visiting;, with <unordered_set> kept. SrtEvaluator.cpp merges cleanly.

Shader disk cache: on main the cache's SourceVersion is a hash of the recompiler and AGC driver sources, so this change, like any edit there, gives a new version and cached shaders are recompiled once. The disk key format and the cached data are unchanged.

Tested

  • Linux (CachyOS, GCC 16), RTX 4090: ctest 125/125 pass, including the new verifyEvaluatedValues in agc_shader_memory_tests.
  • PPSA21564: on my integration branch through repeated passes of my visual regression check (intro, title, menu and a level). The timings above come from my profiling runs there.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source (none added)
  • Generic behaviour, not specific to one title
  • Depends on: none
  • AI-assisted: yes

…rValue's hot accessors

Every draw stage's capture walks the shader's SRT plan with an Evaluator
whose per-value memo was a std::unordered_map: a node allocation per
evaluated value, and out-of-line calls for Opcode, IsIdentity, Resolve
and Argument on every step. The walk took ~17 us of every capture at
PPSA21564's title (about 1.25 s of the queue worker's 10 s).

The memo is now an open-addressing table keyed by the value pointer,
and the accessors are inline. Evaluation order, results and failures
are unchanged. The capture walk drops to ~10 us.
@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
#218 core/shader/recompiler/Optimization/include/Optimization/SrtWalker/SrtEvaluator.hpp L9, L36-37

oneandonlydean added a commit to oneandonlydean/AnyPS5 that referenced this pull request Oct 3, 2026
…heir PR heads

The code of the three open PRs is unchanged on their heads apart from the
opt-outs and comments astrobot keeps. Their heads add tests: boykopovar#415's
EvaluatedValues table test (and drops an unused include), boykopovar#417's flip test
releases its GPU work without an interrupt select, and boykopovar#418's
linux_entry_argv relinks a fixture whose entry checks argc and argv.
@oneandonlydean
oneandonlydean merged commit 1961af9 into boykopovar:main Oct 3, 2026
3 checks passed
@oneandonlydean
oneandonlydean deleted the perf/recompiler-srt-memo-table branch October 3, 2026 22:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant