Skip to content

perf(shader): rewrite high-fan-out IR uses in linear time - #4035

Closed
MrMha wants to merge 1 commit into
boykopovar:mainfrom
MrMha:perf/linear-shader-use-rewrites
Closed

MrMha wants to merge 1 commit into
boykopovar:mainfrom
MrMha:perf/linear-shader-use-rewrites

Conversation

@MrMha

@MrMha MrMha commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

What

The maintainer identified shader recompilation overhead as a priority (comment). This change removes an unclaimed quadratic operation in the shared IR rewrite primitive.

IrValue::ReplaceAllUsesWith previously called ReplaceArgument once per operand. Each call searched and erased entries in the source's use vectors; replacing a highly shared value therefore cost O(n²). Update the referring operands in one reverse traversal, append the replacement's uses in the same order, and clear the old lists once. Existing operands, use ordering, opcodes, metadata, phi predecessors and parent ownership are preserved.

Reserve both destination lists before changing references, with geometric capacity growth. Allocation failure leaves the graph intact; the old implementation can remove a source use and redirect its argument before a destination allocation fails.

No instruction lowering, shader-cache policy or optimization proof is changed. #3726 changes an SRT caller to use this existing helper; it edits different files and is not a dependency. The reported masked-walk, host-import-budget and Windows red-zone changes already have other proposals; this PR does not duplicate them.

Tested

Linux x86-64, GCC 13.3, C++20, AMD EPYC 9V74 host.

  • Seven native suites pass with -O2 -Wall -Wextra -Werror, including duplicate operands and existing replacement uses, self/null replacements, phi/identity users, follow-up edits, both destination allocation failures, geometric growth, and 200,000 references.
  • 2,000 deterministic differential use graphs, with 48,000 mutation comparisons against the original algorithm, preserve exact ordered arguments/use lists and metadata. These are synthetic IR graphs, not title shader replays.
  • ASan/UBSan passes. Restoring the original IrValue.cpp fails the new allocation-failure-integrity regression; the fixed implementation passes.
  • A dependency-free native target is registered as shader_ir_use_rewrite under BUILD_TESTING, including relinker-only builds, with a 20-second timeout. The complete release suite runs in approximately 0.20 s locally.
  • Conventions and whitespace checks pass.

Five alternating baseline/fixed runs per benchmark size, using fresh graphs and excluding construction/destruction:

Operand references to replace Baseline median Fixed median Operation speedup
4,000 3.343 ms 0.0320 ms 104x
16,000 51.605 ms 0.1760 ms 293x
64,000 853.822 ms 0.9776 ms 873x
128,000 3,410.449 ms 2.0543 ms 1,660x

Every run verifies all redirected operands and final use counts. Separate tests of 2,000–32,000 small two-reference rewrites into one replacement improved by 1.23–1.45x, checking that repeated reservations do not introduce quadratic growth.

These timings isolate the IR helper. End-to-end shader compilation, byte-identical SPIR-V on title captures, retail-title FPS, full runtime CTest and Windows/macOS execution remain unverified locally. No game-wide speedup or direct resolution of #3504/#2809/#361 is claimed.

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
  • Generic behaviour, not specific to one title
  • Depends on: none
  • AI-assisted: yes

@github-actions github-actions Bot added build shader core/shader labels Oct 11, 2026
@oneandonlydean

Copy link
Copy Markdown
Collaborator

Which shader replaces a value with thousands of uses? The table times ReplaceAllUsesWith alone on synthetic graphs of 4,000 to 128,000 references, and perf PRs need before/after numbers for a real case (#218): the recompile time of a dumped request through agc_shader_replay, on main and with this PR, is enough.

@MrMha

MrMha commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

Acknowledge that your measurements are synthetic and that real shader replay timings remain unavailable.

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked on the PR merged onto current main: full build and ctest are green apart from agc_driver_tessellation (lavapipe SEGFAULT, unrelated to this code), shader_ir_use_rewrite passes, and it fails with IrValue.cpp reverted. The rewrite is equivalent to the old loop (use order, nullptr, self-replacement, phi/identity).

One thing is missing before this can merge:

  • The benefit is only shown on synthetic graphs (ReplaceAllUsesWith alone, 4,000–128,000 references). As @oneandonlydean asked, perf PRs need before/after numbers on a real case (CONTRIBUTING, perf PRs): the recompile time of a dumped request through agc_shader_replay on main vs this branch (e.g. the Stray requests from #3504/#4331, or any title with a high-fan-out value), plus a profile showing ReplaceAllUsesWith is a meaningful share of it. Without it we can't tell this is not a no-op in practice (#3726 touched the same helper and was closed without that).

Happy to merge once that number is in the PR.

RINNECODER added a commit to RINNECODER/AnyPS5 that referenced this pull request Oct 14, 2026
…time CPU (#482)

* perf(shader): rewrite high-fan-out IR uses in linear time

Ported from upstream PR boykopovar#4035

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(shader): index specialization tables by SPIR-V id

Ported from upstream PR boykopovar#4772

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(shader): honor disabled optimization during specialization

Ported from upstream PR boykopovar#4827

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(shader): discard helper functions unreachable after specialization

Ported from upstream PR boykopovar#5064

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(shader): drop module constants nothing references after specialization

Ported from upstream PR boykopovar#5315

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(shader): cap SPIR-V scalar replacement

Ported from upstream PR boykopovar#4288

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(recompiler): iterate structurizer fixpoints in post-order and bound shared merge splitting growth

Includes the stacked upstream commit that structurizes refused control flow
with reach flags, which this change builds on.

Ported from upstream PR boykopovar#5028

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(shader): emit image_bvh_intersect_ray as one shared function per variant

Ported from upstream PR boykopovar#5455

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(shader): avoid redundancy traversal map copies

Patches the bundled SPIRV-Tools redundancy elimination (only built with
ANYPS5_ENABLE_SPIRV_TOOLS=ON).

Ported from upstream PR boykopovar#5511

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(metal): accept a specialized BDA module that dropped one of its two BDA descriptors

Specialization removes descriptor variables the final module no longer
references. With helper functions now discarded after specialization, a
compute module can drop its page-table variable while keeping the fault
buffer, and the bridge then rejected the dispatch ("BDA ABI requires
page-table and fault-buffer descriptor metadata"). Require at least one of
the two roles instead of both; the module's own descriptors are still
checked against the prepared plan.

Not a port; needed by the port of upstream PR boykopovar#5064.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@oneandonlydean

Copy link
Copy Markdown
Collaborator

Closing: no title is known to reach this. With the queue at 700+ open PRs, please hold off on new PRs that don't name a title needing the change until it's down. If a title stops on this on current main, reopen with the title ID and the log line.

@MrMha

MrMha commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

Evidence follow-up: the existing timings are synthetic IR-helper measurements. I still do not have a real title request with before/after agc_shader_replay timings or a profile attributing meaningful compile time to IrValue::ReplaceAllUsesWith, so this PR is not demonstrated merge-ready.

I checked the cited Stray reports: #3504 was closed as fixed by #4331, which changes MaskedSelectEliminator. Those results cannot be attributed to this IR-use rewrite. The new RINNECODER/AnyPS5#482 carry also explicitly says performance was not measured and combines multiple changes.

To complete the requested comparison, could a contributor attach a downloadable APS5_DUMP_SHADERS request that exercises this helper, with its title ID/version and source commit? A current-main versus current-main-plus-this-patch replay on the same machine, matching target/optimization options with cache disabled, repeated timings, a helper profile and SPIR-V comparison would establish whether the optimization matters in that real case. A small replay request would be sufficient; the numbers from unrelated shader optimizations are not evidence for this PR.

AI-assisted: yes (Codex).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants