Skip to content

perf(shader): reuse an evicted result's storage for the next patched result - #10757

Draft
meh7an wants to merge 2 commits into
boykopovar:mainfrom
meh7an:perf/shader-recycle-patched-results
Draft

meh7an wants to merge 2 commits into
boykopovar:mainfrom
meh7an:perf/shader-recycle-patched-results

Conversation

@meh7an

@meh7an meh7an commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

What

This PR builds on #10423 (masked result memo). On a masked hit there, materializePreparedMemoized makes a fresh copy of the donor result (std::make_shared<RecompileResult>(*donor)) and puts it into the exact table. The insert evicts the set's least recently used entry, and when that drops the evicted result's last reference, the result is destroyed on the worker, under the source's lock.

A RecompileResult holds dozens of vectors, and every DescriptorBinding in it holds about twenty more. So each patched hit costs one full allocation of a result and one full free of another.

On Sonic Origins Plus's Sonic 2 menu page with #10423 applied, the result memo evicts about 31,000 results per second. Sampling the queue worker (APS5_SAMPLE_WORKER, 80 s on the page) attributes:

  • 13 % of its samples to destroying results, most of them in that eviction;
  • 15 % to operator delete, mostly the Windows heap's frees;
  • 4 % to the copy.

This PR keeps the storage instead:

  • When the memo evicts a result that nothing else references (use_count() == 1 under the source's lock, followed by an acquire fence), it moves the result into a small list owned by the calling thread (ThreadOwned, at most 8 results) instead of freeing it.
  • The next masked hit on that thread copy-assigns the donor into a kept result (*result = donor) instead of allocating one. The vectors keep their capacity, so most of the copy allocates nothing.
  • applyMemoPatch then writes the snapshot's words as before. ResultKeyMemo's assignment already clears the draw-key memo, so nothing of the old result survives.

Nothing holds a weak_ptr to a RecompileResult, so a result whose count is 1 under the lock cannot gain a new owner while it is reused. Every other owner (a recorded draw, the other table) keeps the count above 1, and that result is freed as before when its last owner drops it.

  • APS5_NO_RESULT_RECYCLE=1 frees every evicted result, as perf(shader): reuse a memoized result by patching its copied words #10423 does.
  • ShaderRecompiler::RecycledResults() counts the reuses.
  • agc_shader_table_snapshot_reuse now ends by patching the program for 640 distinct output buffers, enough to fill the memo and evict.
    • Each patched result must hold its own buffer's descriptor, recycled storage or not.
    • The counter must rise exactly when recycling is on.
    • agc_shader_table_snapshot_reuse_unrecycled (new) runs the test with APS5_NO_RESULT_RECYCLE=1. It expects no reuse, and the two existing variants (default and _unmasked) still pass.

Overlap.

Tested

Windows 11 Pro 10.0.26300, NVIDIA GeForce RTX 2080 Ti, driver 617.14, i9-9900K.

Sonic Origins Plus (PPSA05324), Sonic 2 page of the main menu, main's defaults. Two clean builds on main e7f46f5: #10423's head 54d7c78 and this PR's head bdca440. Each got one warm-up boot, then two interleaved traced rounds (APS5_TRACE_SECONDS=200:15):

frame p50 (ms) p90 (ms) Driver.Packet (ms/frame) Driver.Draw (ms/frame) title FPS (median of six)
#10423 54d7c78 80.1 / 85.1 86.8 / 116.7 80.1 / 89.2 59.6 / 68.0 12.21 / 12.70
this PR bdca440 84.6 / 80.1 116.3 / 87.7 88.3 / 80.5 65.6 / 59.9 12.53 / 12.55
  • No frame-time difference within this page's noise. The page switches between a fast mode (p90 about 87 ms) and a slow one (about 116 ms), and each build landed in each mode once. Averaged, frame p50 goes 82.6 -> 82.4 ms and Driver.Draw 63.8 -> 62.8 ms.

  • Why: the queue worker now waits on the draw thread. A sampled profile of the queue worker (APS5_SAMPLE_WORKER, 80 s) shows:

    • materializePreparedMemoized going from 19 % to 14 % of its samples and operator delete from 15 % to 9 %;
    • its wait in DrawPipeline::Enqueue on a full draw queue growing from 11 % to 19 %.

    The CPU this PR saves turns into waiting, so on this page it cannot show as frame time until the draw thread gets faster. I'm keeping the PR as a draft until then and will re-measure.

  • The earlier flag A/B was noise. It ran APS5_NO_RESULT_RECYCLE=1 against the default in one uncommitted build (the traces say 2ef1dd3beb, dirty) and read p50 89.2 / 84.2 -> 81.8 / 82.8 ms. Its first "off" round landed in the slow mode, which this clean rerun shows is noise. Its traces: recycle-s2-round1.zip, recycle-s2-round2.zip.

  • No mismatches. APS5_VERIFY_MASKED_RESULT_MEMO=1 with recycling on: 0 patch mismatches over about 440,000 patched results.

Tests

The summaries of all four clean rounds follow, then the full digests of round 1. All four traces are attached:

recycle-clean-s2-round1.zip
recycle-clean-s2-round2.zip

round build summary
1 #10423 54d7c78 seconds 200:15 from present 3354: 182 presents, frame p50 80.1 / p90 86.8 / p99 116.5 / max 137.9 ms, GPU busy 74.6 ms per present
1 this PR bdca440 seconds 200:15 from present 3333: 165 presents, frame p50 84.6 / p90 116.3 / p99 192.6 / max 193.5 ms, GPU busy 74.7 ms per present
2 #10423 54d7c78 seconds 200:15 from present 3427: 164 presents, frame p50 85.1 / p90 116.7 / p99 126.9 / max 128.3 ms, GPU busy 77.4 ms per present
2 this PR bdca440 seconds 200:15 from present 3408: 181 presents, frame p50 80.1 / p90 87.7 / p99 114.6 / max 115.5 ms, GPU busy 74.3 ms per present

Round 1, #10423 54d7c78

seconds 200:15 from present 3354: 182 presents, frame p50 80.1 / p90 86.8 / p99 116.5 / max 137.9 ms, GPU busy 74.6 ms per present
NVIDIA GeForce RTX 2080 Ti, NVIDIA 617.14, Windows 10.0.26300, 3840x2160, commit 54d7c78, dropped: yes

{"commit":"54d7c780a583","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"200:15"},"gpu":"NVIDIA GeForce RTX 2080 Ti","driver":"NVIDIA 617.14","os":"Windows 10.0.26300","cpu":"Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz","cpu_threads":16,"memory_mib":32684,"title_id":"PPSA05324","title":"Sonic Origins","content_version":"02.002.000","resolution":"3840x2160","window":{"spec":"seconds 200:15","first_present":3354,"ms":15050.577},"presents":182,"frame_ms":{"p50":80.127,"p90":86.84,"p99":116.533,"max":137.889},"gpu_busy_ms_per_present":74.62,"worker_busy_pct":{"queue worker 0x0":97.001},"top_phases_ms_per_frame":{"Driver.Packet":80.115,"Graphics.Draw":62.677,"Driver.Packet.draw":60.628,"Driver.Draw":59.584,"Graphics.Draw.draw_and_resource_release":52.265,"Driver.Draw.shader_compile_and_link":37.047,"VideoOut.Flip":32.487,"Driver.Present":32.397},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":6.102,"events_lost":386130,"totals_full":false},"draw committer":{"events_full_after_s":12.778,"events_lost":47537,"totals_full":false}},"trace":"aps5-trace-54d7c780a583-9420.json.gz"}

Slowest frames (largest slices inside each, inclusive)

  • present 3373: 137.9 ms at 1605 ms: batch (gpu) 131.7, Driver.Packet (queue worker 0x0) 96.4, Graphics.Draw (draw committer) 90.7, Graphics.Draw.draw_and_resource_release (draw committer) 85.4
  • present 3451: 116.5 ms at 7947 ms: batch (gpu) 75.4, Graphics.Draw (draw committer) 68.6, Graphics.Draw.draw_and_resource_release (draw committer) 63.6, VideoOut.Flip (presenter) 32.3
  • present 3403: 113.4 ms at 4064 ms: batch (gpu) 75.0, Driver.Packet (queue worker 0x0) 72.2, Graphics.Draw (draw committer) 66.5, Graphics.Draw.draw_and_resource_release (draw committer) 62.0
  • present 3495: 113.1 ms at 11690 ms: batch (gpu) 75.1, Graphics.Draw (draw committer) 68.8, Graphics.Draw.draw_and_resource_release (draw committer) 62.8, VideoOut.Flip (presenter) 31.6
  • present 3485: 113.0 ms at 10850 ms: batch (gpu) 74.2, Graphics.Draw (draw committer) 68.1, Graphics.Draw.draw_and_resource_release (draw committer) 61.9, VideoOut.Flip (presenter) 30.0

Threads (ms per frame)

thread busy wait untraced in slices < 20 us largest
title 1.4 (2 %) 0.0 (0 %) 81.3 0.7 Shader.ResolveGraphicsStagesAbi 0.9, Shader.ResolveAbi 0.5
queue worker 0x0 17.5 (21 %) 0.0 (0 %) 65.2 75.6 Driver.Packet 17.5, Driver.EndSubmission 0.0
draw committer 33.3 (40 %) 0.6 (1 %) 48.8 48.3 Graphics.Draw 31.7, Recorder.Submit 1.1
title 0.4 (0 %) 0.0 (0 %) 82.3 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 82.3 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 82.3 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 82.3 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.1
presenter 0.7 (1 %) 31.9 (39 %) 50.0 0.0 VideoOut.Flip 32.5, Driver.Present 0.2
title 0.5 (1 %) 0.0 (0 %) 82.2 0.1 Shader.ResolveGraphicsStagesAbi 0.3, Shader.ResolveAbi 0.2
title 0.3 (0 %) 0.0 (0 %) 82.4 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.1
title 0.4 (0 %) 0.0 (0 %) 82.3 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (1 %) 0.0 (0 %) 82.3 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (1 %) 0.0 (0 %) 82.3 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (1 %) 0.0 (0 %) 82.3 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 82.3 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.5 (1 %) 0.0 (0 %) 82.2 0.1 Shader.ResolveGraphicsStagesAbi 0.3, Shader.ResolveAbi 0.2
gpu 74.5 (90 %) 0.0 (0 %) 8.2 0.0 batch 74.5

Largest slices (ms per frame, inclusive, all threads): batch 74.5, VideoOut.Flip 32.5, Driver.Present 32.4, VideoOut.Flip.present 32.2, Driver.Present.render_fence_wait 31.9, Vulkan.Retire 31.9, Vulkan.Retire.wait 31.9, Graphics.Draw 31.7, Graphics.Draw.draw_and_resource_release 26.1, Driver.Packet 17.5

Longest waits

  • Driver.Present.render_fence_wait (presenter) 87.9 ms at 1417 ms, present 3371
  • Driver.Present.render_fence_wait (presenter) 82.5 ms at 1330 ms, present 3370
  • Driver.Present.render_fence_wait (presenter) 78.7 ms at 1605 ms, present 3373
  • Driver.Present.render_fence_wait (presenter) 70.6 ms at 1524 ms, present 3372
  • Driver.Present.render_fence_wait (presenter) 68.2 ms at 1248 ms, present 3369
  • Driver.Present.render_fence_wait (presenter) 60.5 ms at 1160 ms, present 3368
  • Driver.Present.render_fence_wait (presenter) 52.3 ms at 1074 ms, present 3367
  • Driver.Present.render_fence_wait (presenter) 40.8 ms at 992 ms, present 3366
  • Driver.Present.render_fence_wait (presenter) 35.8 ms at 7399 ms, present 3444
  • Driver.Present.render_fence_wait (presenter) 35.4 ms at 11605 ms, present 3494

Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)

  • batch 1354: 17.6 ms at 1649 ms, present 3373: GPU no ranges; recorded by Driver.Packet (queue worker 0x0) 1.3, Driver.Packet.dispatch (queue worker 0x0) 0.6, Driver.Dispatch (queue worker 0x0) 0.5, Driver.Packet.pipeline_drain (queue worker 0x0) 0.3
  • batch 7443: 15.5 ms at 8544 ms, present 3457: GPU no ranges; recorded by no CPU slices
  • batch 10023: 15.3 ms at 11604 ms, present 3493: GPU no ranges; recorded by no CPU slices
  • batch 9453: 15.3 ms at 10961 ms, present 3485: GPU no ranges; recorded by no CPU slices
  • batch 3280: 15.0 ms at 3818 ms, present 3399: GPU no ranges; recorded by Driver.Packet (queue worker 0x0) 1.3, Driver.Packet.dispatch (queue worker 0x0) 0.6, Driver.Dispatch (queue worker 0x0) 0.5, Driver.Packet.pipeline_drain (queue worker 0x0) 0.4

Round 1, this PR bdca440

seconds 200:15 from present 3333: 165 presents, frame p50 84.6 / p90 116.3 / p99 192.6 / max 193.5 ms, GPU busy 74.7 ms per present
NVIDIA GeForce RTX 2080 Ti, NVIDIA 617.14, Windows 10.0.26300, 3840x2160, commit bdca440, dropped: yes

{"commit":"bdca440d390c","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"200:15"},"gpu":"NVIDIA GeForce RTX 2080 Ti","driver":"NVIDIA 617.14","os":"Windows 10.0.26300","cpu":"Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz","cpu_threads":16,"memory_mib":32684,"title_id":"PPSA05324","title":"Sonic Origins","content_version":"02.002.000","resolution":"3840x2160","window":{"spec":"seconds 200:15","first_present":3333,"ms":15011.098},"presents":165,"frame_ms":{"p50":84.575,"p90":116.336,"p99":192.64,"max":193.492},"gpu_busy_ms_per_present":74.693,"worker_busy_pct":{"queue worker 0x0":97.218},"top_phases_ms_per_frame":{"Driver.Packet":88.322,"Graphics.Draw":69.74,"Driver.Packet.draw":66.729,"Driver.Draw":65.594,"Graphics.Draw.draw_and_resource_release":58.618,"Driver.Draw.shader_compile_and_link":38.525,"VideoOut.Flip":30.932,"Driver.Present":30.913},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":6.173,"events_lost":385570,"totals_full":false},"draw committer":{"events_full_after_s":13.162,"events_lost":38828,"totals_full":false}},"trace":"aps5-trace-bdca440d390c-24144.json.gz"}

Slowest frames (largest slices inside each, inclusive)

  • present 3344: 193.5 ms at 947 ms: batch (gpu) 191.9, Driver.Packet (queue worker 0x0) 155.2, VideoOut.Flip (presenter) 122.9, VideoOut.Flip.present (presenter) 122.8
  • present 3333: 192.6 ms at 0 ms: batch (gpu) 156.2, Driver.Packet (queue worker 0x0) 118.8, Graphics.Draw (draw committer) 79.7, Driver.Packet.draw (queue worker 0x0) 76.3
  • present 3390: 133.7 ms at 5212 ms: Driver.Packet (queue worker 0x0) 108.2, Graphics.Draw (draw committer) 95.6, Driver.Packet.draw (queue worker 0x0) 93.9, Driver.Draw (queue worker 0x0) 91.9
  • present 3413: 121.8 ms at 7357 ms: Graphics.Draw (draw committer) 90.2, batch (gpu) 77.3, Graphics.Draw.draw_and_resource_release (draw committer) 61.1, VideoOut.Flip (presenter) 32.7
  • present 3478: 120.8 ms at 13148 ms: batch (gpu) 73.9, VideoOut.Flip (presenter) 23.8, VideoOut.Flip.present (presenter) 23.7, Driver.Present (presenter) 23.7

Threads (ms per frame)

thread busy wait untraced in slices < 20 us largest
title 1.5 (2 %) 0.0 (0 %) 89.5 0.7 Shader.ResolveGraphicsStagesAbi 0.9, Shader.ResolveAbi 0.6
queue worker 0x0 22.2 (24 %) 0.1 (0 %) 68.6 74.1 Driver.Packet 22.3, Driver.EndSubmission 0.0
draw committer 41.0 (45 %) 0.5 (1 %) 49.5 51.2 Graphics.Draw 38.8, Recorder.Submit 1.6
title 0.4 (0 %) 0.0 (0 %) 90.6 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 90.5 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.5 (1 %) 0.0 (0 %) 90.5 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 90.6 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
presenter 0.7 (1 %) 30.4 (33 %) 59.9 0.0 VideoOut.Flip 30.9, Driver.Present 0.2
title 0.5 (1 %) 0.0 (0 %) 90.5 0.1 Shader.ResolveGraphicsStagesAbi 0.3, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 90.6 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.5 (0 %) 0.0 (0 %) 90.5 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 90.6 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 90.5 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
title 0.3 (0 %) 0.0 (0 %) 90.7 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.1
title 0.5 (1 %) 0.0 (0 %) 90.5 0.1 Shader.ResolveGraphicsStagesAbi 0.3, Shader.ResolveAbi 0.2
title 0.4 (0 %) 0.0 (0 %) 90.5 0.1 Shader.ResolveGraphicsStagesAbi 0.2, Shader.ResolveAbi 0.2
gpu 74.7 (82 %) 0.0 (0 %) 16.3 0.0 batch 74.7

Largest slices (ms per frame, inclusive, all threads): batch 74.7, Graphics.Draw 38.8, Graphics.Draw.draw_and_resource_release 31.6, VideoOut.Flip 30.9, Driver.Present 30.9, VideoOut.Flip.present 30.8, Driver.Present.render_fence_wait 30.4, Vulkan.Retire 30.4, Vulkan.Retire.wait 30.3, Driver.Packet 22.3

Longest waits

  • Driver.Present.render_fence_wait (presenter) 122.6 ms at 947 ms, present 3344
  • Driver.Present.render_fence_wait (presenter) 118.0 ms at 1141 ms, present 3345
  • Driver.Present.render_fence_wait (presenter) 71.0 ms at 1260 ms, present 3346
  • Driver.Present.render_fence_wait (presenter) 47.5 ms at 1355 ms, present 3347
  • Driver.Present.render_fence_wait (presenter) 34.7 ms at 7811 ms, present 3418
  • Driver.Present.render_fence_wait (presenter) 34.6 ms at 14200 ms, present 3489
  • Driver.Present.render_fence_wait (presenter) 34.5 ms at 6822 ms, present 3407
  • Driver.Present.render_fence_wait (presenter) 34.4 ms at 14286 ms, present 3490
  • Driver.Present.render_fence_wait (presenter) 34.4 ms at 1438 ms, present 3348
  • Driver.Present.render_fence_wait (presenter) 34.2 ms at 8639 ms, present 3427

Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)

  • batch 853: 32.2 ms at 1185 ms, present 3345: GPU no ranges; recorded by Graphics.Draw (draw committer) 0.8, Graphics.Draw.draw_and_resource_release (draw committer) 0.7, Resources.Prepare (draw committer) 0.1, Recorder.Submit (draw committer) 0.1
  • batch 851: 30.3 ms at 1149 ms, present 3345: GPU no ranges; recorded by Graphics.Draw (draw committer) 1.3, Graphics.Draw.draw_and_resource_release (draw committer) 1.2, Recorder.Submit (draw committer) 0.0, Vulkan.DrawFromRecipe (draw committer) 0.0
  • batch 782: 29.8 ms at 975 ms, present 3344: GPU no ranges; recorded by Driver.Packet (queue worker 0x0) 0.5, Driver.Packet.dispatch (queue worker 0x0) 0.3, Driver.Dispatch (queue worker 0x0) 0.3, Vulkan.Dispatch (queue worker 0x0) 0.2
  • batch 780: 27.7 ms at 936 ms, present 3343: GPU no ranges; recorded by Graphics.Draw (draw committer) 2.4, Graphics.Draw.draw_and_resource_release (draw committer) 2.2, Recorder.Submit (draw committer) 0.1, Resources.Prepare (draw committer) 0.1
  • batch 836: 23.2 ms at 1101 ms, present 3344: GPU no ranges; recorded by Driver.Packet (queue worker 0x0) 0.4, Driver.Packet.draw (queue worker 0x0) 0.2, Driver.Draw (queue worker 0x0) 0.2, Driver.Packet.pipeline_drain (queue worker 0x0) 0.2

Checklist

@github-actions github-actions Bot added perf Performance change perf numbers missing perf pull request without a before and after summary line labels Oct 24, 2026
@github-actions

Copy link
Copy Markdown

This pull request is labeled perf, so it needs the summary line of a capture on main and one with the change, from the same title, scene and machine, as the Performance report form asks. Please paste both aps5-summary.jsonl lines, attach the two traces and add the output of tools/trace_digest.py.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed exact head bdca440d390c7b833a14e15ee38664e16a902033 against its stated dependency #10423. The feature-flag comparison and attached traces are useful, but I found integration and validation gaps that need resolution before this can merge:

  • The performance summaries compare APS5_NO_RESULT_RECYCLE=1 with recycling enabled in the same dirty build, and both report commit 2ef1dd3bebef. That is a useful feature-flag ablation, but it is not the required two-commit base/change comparison. Please run the same scene on the #10423 dependency head and this exact PR head (or the agreed current integration base and this head), attach both trace captures, and put both distinct commit IDs in the summaries. The hosted product build/test jobs are currently skipped, so they do not supply this comparison.
  • Please coordinate the new retention path with #9989. That PR's current budget/trim walk counts source->memo entries; this change adds a second 256-entry source.maskedMemo table that owns full RecompileResults and a per-thread pool retaining up to eight more full results. Those references and their vector capacities are outside the current byte accounting, so the configured memo budget would not bound the retained memory after these changes are combined. Update the accounting/trim behavior and add a test that exercises the bound with both tables and the recycle pool, or agree on an explicit integration order that resolves this before merge.
  • Please also reconfirm the interaction with #9049. Its current change reuses an evicted exact entry's bindings only when use_count() == 1; the added masked table can keep a second reference to that same result, while this PR separately recycles the complete result when an entry is evicted. This can change when #9049's reuse path is eligible, contrary to the earlier expectation that the two optimizations add up. Coordinate with that author, declare the agreed dependency/order, and validate the combined path.

I did not run the GPU/AGC tests locally: this checkout has no usable configured full build, and the latest hosted Linux/Windows product jobs are skipped. I therefore have not independently validated runtime equivalence or performance.

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 1368 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1896 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 2434 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5021 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 5609 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.hpp
PR 6187 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6188 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.hpp
PR 6192 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6209 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6235 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.hpp
PR 6280 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6300 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6412 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
PR 6481 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6488 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
PR 6539 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
PR 6642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6924 core/shader/recompiler/Recompiler.cpp
docs/dev/ENVIRONMENT.md
PR 7048 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7201 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
PR 7275 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7449 docs/dev/ENVIRONMENT.md
PR 7457 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 7484 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
docs/dev/ENVIRONMENT.md
PR 7604 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 7642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
docs/dev/ENVIRONMENT.md
PR 7684 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 7689 core/shader/recompiler/Recompiler.hpp
PR 8004 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
docs/dev/ENVIRONMENT.md
PR 8014 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8077 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
docs/dev/ENVIRONMENT.md
PR 8161 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 8246 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 8484 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 8542 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 8592 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8896 docs/dev/ENVIRONMENT.md
PR 9176 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 9248 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 9287 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 9289 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 9345 docs/dev/ENVIRONMENT.md
PR 9356 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
docs/dev/ENVIRONMENT.md
PR 9387 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
docs/dev/ENVIRONMENT.md
PR 9402 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9508 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
docs/dev/ENVIRONMENT.md
PR 9669 docs/dev/ENVIRONMENT.md
PR 9676 docs/dev/ENVIRONMENT.md
PR 9748 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
PR 9852 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.hpp
docs/dev/ENVIRONMENT.md
PR 10193 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
docs/dev/ENVIRONMENT.md
PR 10442 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 10547 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
docs/dev/ENVIRONMENT.md

@github-actions github-actions Bot added build shader core/shader docs agc libSceAgc* labels Oct 24, 2026
@meh7an
meh7an marked this pull request as draft October 24, 2026 23:48
@github-actions github-actions Bot removed the perf numbers missing perf pull request without a before and after summary line label Oct 24, 2026
@meh7an

meh7an commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

@MarcZX10 Thanks, all three points hold, and the first one changed the picture.

The checklist now says Depends on: #10423, #9049, #9989, pending both authors' confirmation.

@QDarkRex

Copy link
Copy Markdown
Contributor

A second title and a second vendor for this change: ASTRO BOT's Crash Site hub on Windows / AMD. The worker-side saving shows up clearly there too. The frame does not move, because on this title the draw committer bounds the frame rather than the queue 0 worker.

  • Host: Windows 11 (26300), Ryzen 7 5700X, 32 GB RAM, Radeon RX 9060 XT, AMD driver 26.10.1, WinLibs GCC 15.2.0 r7, Release.
  • Builds: this PR's head bdca440 against its parent 54d7c78 (the perf(shader): reuse a memoized result by patching its copied words #10423 commit), so the A/B isolates the second commit.
  • Runs: ASTRO BOT PPSA21564 01.007.000, -lvl hub_crashsite, no input, 300 s each. One warm-up per arm, then two rounds in the order A B / B A. APS5_HOST_IMPORT_MIB=14336, APS5_TRACE_SUMMARY_EVERY=10, APS5_SAMPLE_WORKER and APS5_SAMPLE_COMMITTER were set in every run. Frame figures come from the 10 s windows from 120 s on. The worker shares are inclusive, as a fraction of all of the queue 0 worker's samples (about 19,000 per run).
Run Build FPS frame p50 Destroying a RecompileResult operator delete materializePreparedMemoized
1 parent 6.43 154.2 ms 5.43 % 5.83 % 10.18 %
2 this PR 6.39 154.8 ms 0.36 % 3.56 % 8.18 %
3 this PR 6.44 154.7 ms 0.42 % 4.27 % 9.34 %
4 parent 6.39 155.4 ms 6.15 % 6.54 % 11.52 %
  • Result destruction on the worker drops from 5.4–6.2 % to 0.4 %. operator delete drops by about 2 points, and the whole memoized materialize by about 2 points. As a share of the worker's non-blocked samples, materializePreparedMemoized goes from 28 % to 22 %.
  • On this scene the memo hits rarely before perf(shader): reuse a memoized result by patching its copied words #10423: on main 5d3ab77 with APS5_PROFILE_DRAW=1, the line reads 11739 hits, 173897 misses (6.3% hits) … 173878 evictions per 10 s. So nearly every draw stage takes the eviction this PR reuses.
  • FPS, frame p50 and GPU busy (100.7–101.0 ms per present) are the same in both arms. The queue 0 worker spends 29–30 % of its samples in DrawPipeline::Enqueue, waiting for the committer, so a worker saving does not reach the frame on this scene.
  • No errors in any run.

AI-assisted: yes (Claude Code). The runs were made on my machine.

This branch has not been deployed

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

Labels

agc libSceAgc* build docs perf Performance change shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants