Skip to content

perf(agc): re-check only the host imports a registry change can reach - #9402

Open
janzezno wants to merge 1 commit into
boykopovar:mainfrom
janzezno:perf/incremental-import-refresh
Open

janzezno wants to merge 1 commit into
boykopovar:mainfrom
janzezno:perf/incremental-import-refresh

Conversation

@janzezno

@janzezno janzezno commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

What

refreshImports re-checked every live host import, and every remembered failure and refusal, each time the guest allocation registry changed. A title that maps memory often pays that walk constantly. SuperTuxKart (PPSA69421) maps about 1650 times a second at its menu. With #8091's budget fix, about 440 imports (~1.1 GiB) stay live there, and the walk alone costs ~118 ms of CPU per second on the frame's critical path. The frame rate falls as the import budget grows: ~34 FPS with the default budget, ~48 FPS with APS5_HOST_IMPORT_MIB=128.

This PR makes the refresh incremental:

  • The import table keeps the (address, bytes, weak_ptr) of every range in the lease it was last checked against. The snapshot holds weak_ptrs because a lease pins its ranges: unmap waits while use_count() != 1.
  • A refresh merges that snapshot with the new lease by range identity (owner_before, as sameRun does). The result is the address spans whose ranges were added, removed or replaced, whatever mutation did it, including backing renewals.
  • Only imports whose import window ([base / window * window, max(that + window, base + bytes))) touches a changed span are checked again. These are found with a lower_bound on the import map plus one predecessor; imports never overlap, as peekImport already relies on. importRun, exactRun and joinedRun read only ranges inside that span. The scan carries a cursor across the changed spans, so each import is checked at most once per refresh even when several separate spans fall in one window.
  • Imports made since the last refresh are always checked. A thread can make an import from a lease taken before another thread's refresh, and the next diff would not show that range. They are kept as a set of distinct bases (pendingImports); retiring an import removes its base, so the set never holds more than the live imports.
  • Every import is checked, as before, when the window is 0 or the window or the effective budget changed since the last refresh.
  • Remembered failures, failed joins, failed blocks and refusals are dropped by the same window test. Every use site already validates them with sameRun, so this is cleanup only.

One behaviour differs, and it is recorded in docs/dev/debt/libSceAgcDriver.md. An unchanged import whose run could now be larger, because a remembered refusal no longer holds, is kept until its window changes. A request that needs the larger run still retires it (wouldRetireImport). On main, any registry change anywhere retired it. Under a 128 MiB budget in SuperTuxKart, main retired and re-made the same three still-valid single-range imports about 1650 times a second for this reason.

New test agc_windowed_import_refresh (128 KiB window, three windows):

  • a continuing range added inside an import's window, outside the import's own bytes, retires it;
  • an import in another window survives;
  • re-mapping a range retires its import only.

It passes on main and on this PR. It fails when the window query is narrowed to lower_bound(begin): "an import whose window gained a continuing range was kept".

Two local probes (not in this PR) checked the review follow-ups:

  • 32 single-range imports in one 8 MiB window, then 32 separate ranges added between them in one mutation: the refresh made 1024 checks before the cursor and 32 with it.
  • SuperTuxKart with fix(agc): lower import budgets only on memory exhaustion #8091 counted the pending set at each of ~12,000 refreshes per 5 s. With a 128 MiB budget (71 live imports, 107,397 made in 50 s) and with the default budget (440 live), it never held more than 1 entry or more than the live imports.

Tested

Arch Linux (kernel 7.2.11), Ryzen 5 7500F, AMD Radeon RX 7900 GRE, RADV Mesa 26.2.5, on main b7d1d47a5f. SuperTuxKart (PPSA69421 00.001.000, catalog sha256 f2240bf0…b712a), relinked with --trap-sdk-syscall-stubs. Main menu with no input, headless gamescope --backend headless -W 1920 -H 1080, APS5_TRACE_SUMMARY_EVERY=10, 70 s runs. Each side's first run creates the game's configuration and starts in a different first-launch scene, so it only warms the shader caches and is not counted. Then three runs per side in the order main, PR, main, PR, main, PR. The FPS is the presents in the last 8 s of each run (VideoOut telemetry).

Why #8091 is applied on both sides. On RADV, plain main lowers the import budget after memfd-backed imports are refused (#8091). The menu then randomly settles in a GPU-bound ~3 FPS mode, with GPU ~90% busy and a CPU nearly idle, or a fast mode with ~74 MiB of imports. Both sides do this: on plain main eaff876, main ran 2.9 / 2.7 / 3.3 FPS and this PR 1.0 / 3.2 / 53.7 FPS. That noise hides the walk. Both builds below therefore carry #8091's one-line production change in importRunAt. This PR does not depend on #8091.

Side FPS, last 8 s of each run Live imports at exit GPU busy per present
main + #8091 37.2 / 35.9 / 36.8 420 / 414 / 418 (~1.04 GiB) 1.25 ms
this PR + #8091 51.8 / 54.0 / 53.2 441 / 396 / 431 (~1.05 GiB) 1.24 ms

These six runs were built from a work tree with this change applied on b7d1d47a5f (identical to the pushed commit), so their summary lines show b7d1d47a5f9f dirty on both sides. tools/trace_digest.py of the first run of each side (summary files of all six runs attached):

main + #8091

6 intervals, 0.0 to 60.1 s, 2007 presents
AMD Radeon RX 7900 GRE (RADV NAVI31), radv Mesa 26.2.5-arch1.1, Linux 7.2.11-arch1-1, 1920x1080, commit b7d1d47 (dirty)

# seconds presents p50 p90 p99 max GPU busy busiest worker largest phase
1 0-10 210 28.3 80.0 268.6 1370.7 1.3 2 % Graphics.Draw 3.2
2 10-20 359 27.8 30.8 32.8 33.3 1.3 4 % Graphics.Draw 2.6
3 20-30 354 28.0 31.1 34.0 35.7 1.2 4 % Graphics.Draw 2.7
4 30-40 347 28.1 33.6 35.3 36.6 1.2 4 % Graphics.Draw 2.8
5 40-50 366 27.2 29.8 33.1 37.4 1.2 4 % Graphics.Draw 2.6
6 50-60 371 26.9 29.2 31.5 33.0 1.2 4 % Graphics.Draw 2.6
p99 ms  @_____  0-268.6
gpu ms  @@@@@@  0-1.3
this PR + #8091

6 intervals, 0.0 to 60.0 s, 2998 presents
AMD Radeon RX 7900 GRE (RADV NAVI31), radv Mesa 26.2.5-arch1.1, Linux 7.2.11-arch1-1, 1920x1080, commit b7d1d47 (dirty)

# seconds presents p50 p90 p99 max GPU busy busiest worker largest phase
1 0-10 305 18.1 22.4 251.6 1408.4 1.3 3 % Graphics.Draw 1.8
2 10-20 544 17.9 20.8 24.8 29.8 1.2 5 % Recorder.Submit 1.7
3 20-30 543 18.1 20.9 22.3 23.6 1.2 5 % Recorder.Submit 1.7
4 30-40 533 18.4 21.1 22.7 27.6 1.2 5 % Recorder.Submit 1.8
5 40-50 530 18.6 21.1 22.8 23.9 1.2 5 % Recorder.Submit 1.8
6 50-60 543 18.1 20.8 22.9 23.7 1.2 5 % Recorder.Submit 1.8
p99 ms  @_____  0-251.6
gpu ms  @@@@@@  0-1.3

Trace windows for the perf check. APS5_TRACE_SECONDS=40:10, same scene and machine. main is b7d1d47a5f and this PR is 3b7c43eb75, each with #8091's one-line change applied, so both show as dirty. Two windows per side: main 35.9 / 35.6 FPS, p50 27.5 / 27.7 ms, p99 32.8 / 40.9 ms; this PR 45.6 / 51.8 FPS, p50 20.7 / 18.8 ms, p99 36.7 / 29.0 ms. The first PR window ends in a short run of slow frames (presents 2093–2099, the queue worker idle-waiting, as in the slow frames on main). The first window of each side, with its trace attached:

main + #8091:

{"commit":"b7d1d47a5f9f","dirty":true,"env":{"APS5_NO_PAD_INPUT":"1","APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"40:10","APS5_TRACE_SUMMARY_EVERY":"10","APS5_VIDEOOUT_TELEMETRY_DIR":"<path>"},"gpu":"AMD Radeon RX 7900 GRE (RADV NAVI31)","driver":"radv Mesa 26.2.5-arch1.1","os":"Linux 7.2.11-arch1-1","cpu":"AMD Ryzen 5 7500F 6-Core Processor","cpu_threads":12,"memory_mib":31744,"title_id":"PPSA69421","title":"SuperTuxKart","content_version":"00.001.000","resolution":"1920x1080","window":{"spec":"seconds 40:10","first_present":1302,"ms":10015.333},"presents":360,"frame_ms":{"p50":27.476,"p90":31.046,"p99":32.762,"max":33.129},"gpu_busy_ms_per_present":1.247,"worker_busy_pct":{"queue worker 0x0":3.687},"top_phases_ms_per_frame":{"Graphics.Draw":2.664,"Vulkan.DrawFromRecipe":2.280,"Graphics.DrawWithRecipe":2.270,"Graphics.DrawWithRecipe.draw_and_resource_release":2.265,"Graphics.Draw.draw_and_resource_release":2.006,"Recorder.Submit":1.802,"Driver.Packet":1.010,"Driver.Packet.draw":0.737},"trace_dropped":false,"trace":"aps5-trace-b7d1d47a5f9f-164176.json.gz"}

this PR + #8091:

{"commit":"3b7c43eb752e","dirty":true,"env":{"APS5_NO_PAD_INPUT":"1","APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"40:10","APS5_TRACE_SUMMARY_EVERY":"10","APS5_VIDEOOUT_TELEMETRY_DIR":"<path>"},"gpu":"AMD Radeon RX 7900 GRE (RADV NAVI31)","driver":"radv Mesa 26.2.5-arch1.1","os":"Linux 7.2.11-arch1-1","cpu":"AMD Ryzen 5 7500F 6-Core Processor","cpu_threads":12,"memory_mib":31744,"title_id":"PPSA69421","title":"SuperTuxKart","content_version":"00.001.000","resolution":"1920x1080","window":{"spec":"seconds 40:10","first_present":1646,"ms":10012.706},"presents":457,"frame_ms":{"p50":20.681,"p90":27.595,"p99":36.689,"max":38.388},"gpu_busy_ms_per_present":1.242,"worker_busy_pct":{"queue worker 0x0":4.973},"top_phases_ms_per_frame":{"Recorder.Submit":1.919,"Graphics.Draw":1.550,"Graphics.Draw.draw_and_resource_release":1.339,"Vulkan.DrawFromRecipe":1.155,"Graphics.DrawWithRecipe":1.145,"Graphics.DrawWithRecipe.draw_and_resource_release":1.140,"Driver.Packet":1.074,"Driver.Packet.draw":0.780},"trace_dropped":false,"trace":"aps5-trace-3b7c43eb752e-199732.json.gz"}
tools/trace_digest.py, main + #8091

seconds 40:10 from present 1302: 360 presents, frame p50 27.5 / p90 31.0 / p99 32.8 / max 33.1 ms, GPU busy 1.2 ms per present
AMD Radeon RX 7900 GRE (RADV NAVI31), radv Mesa 26.2.5-arch1.1, Linux 7.2.11-arch1-1, 1920x1080, commit b7d1d47 (dirty), dropped: no

{"commit":"b7d1d47a5f9f","dirty":true,"env":{"APS5_NO_PAD_INPUT":"1","APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"40:10","APS5_TRACE_SUMMARY_EVERY":"10","APS5_VIDEOOUT_TELEMETRY_DIR":"<path>"},"gpu":"AMD Radeon RX 7900 GRE (RADV NAVI31)","driver":"radv Mesa 26.2.5-arch1.1","os":"Linux 7.2.11-arch1-1","cpu":"AMD Ryzen 5 7500F 6-Core Processor","cpu_threads":12,"memory_mib":31744,"title_id":"PPSA69421","title":"SuperTuxKart","content_version":"00.001.000","resolution":"1920x1080","window":{"spec":"seconds 40:10","first_present":1302,"ms":10015.333},"presents":360,"frame_ms":{"p50":27.476,"p90":31.046,"p99":32.762,"max":33.129},"gpu_busy_ms_per_present":1.247,"worker_busy_pct":{"queue worker 0x0":3.687},"top_phases_ms_per_frame":{"Graphics.Draw":2.664,"Vulkan.DrawFromRecipe":2.28,"Graphics.DrawWithRecipe":2.27,"Graphics.DrawWithRecipe.draw_and_resource_release":2.265,"Graphics.Draw.draw_and_resource_release":2.006,"Recorder.Submit":1.802,"Driver.Packet":1.01,"Driver.Packet.draw":0.737},"trace_dropped":false,"trace":"aps5-trace-b7d1d47a5f9f-164176.json.gz"}

Slowest frames (largest slices inside each, inclusive)

  • present 1511: 33.1 ms at 5862 ms: Driver.WorkerWait (queue worker 0x0) 32.3, Driver.WorkerWait.idle_and_completion_poll (queue worker 0x0) 32.2, Graphics.Draw (draw committer) 3.1, Vulkan.DrawFromRecipe (draw committer) 2.8
  • present 1388: 32.9 ms at 2418 ms: Driver.WorkerWait (queue worker 0x0) 31.4, Driver.WorkerWait.idle_and_completion_poll (queue worker 0x0) 31.4, Graphics.Draw (draw committer) 2.8, Vulkan.DrawFromRecipe (draw committer) 2.5
  • present 1349: 32.9 ms at 1312 ms: Driver.WorkerWait (queue worker 0x0) 31.4, Driver.WorkerWait.idle_and_completion_poll (queue worker 0x0) 31.4, Graphics.Draw (draw committer) 2.8, Vulkan.DrawFromRecipe (draw committer) 2.5
  • present 1660: 32.8 ms at 9950 ms: Driver.WorkerWait (queue worker 0x0) 31.2, Driver.WorkerWait.idle_and_completion_poll (queue worker 0x0) 31.1, Graphics.Draw (draw committer) 2.9, Vulkan.DrawFromRecipe (draw committer) 2.6
  • present 1475: 32.7 ms at 4825 ms: Driver.WorkerWait (queue worker 0x0) 31.2, Driver.WorkerWait.idle_and_completion_poll (queue worker 0x0) 31.2, Graphics.Draw (draw committer) 2.8, Vulkan.DrawFromRecipe (draw committer) 2.5

Threads (ms per frame)

thread busy wait untraced in slices < 20 us largest
title 0.0 (0 %) 0.0 (0 %) 27.8 0.3 Recorder.Sync 0.0
queue worker 0x0 0.3 (1 %) 26.7 (96 %) 0.8 3.0 Driver.Packet 0.3
draw committer 6.7 (24 %) 0.2 (1 %) 20.9 0.1 Graphics.Draw 2.7, Vulkan.DrawFromRecipe 2.3
presenter 0.3 (1 %) 0.0 (0 %) 27.5 0.1 VideoOut.Flip 0.3
gpu 1.2 (4 %) 0.0 (0 %) 26.6 0.0 batch 1.2

Largest slices (ms per frame, inclusive, all threads): Driver.WorkerWait 26.7, Driver.WorkerWait.idle_and_completion_poll 26.6, Graphics.Draw 2.7, Vulkan.DrawFromRecipe 2.3, Graphics.DrawWithRecipe 2.3, Graphics.DrawWithRecipe.draw_and_resource_release 2.3, Graphics.Draw.draw_and_resource_release 2.0, Recorder.Submit 1.8, batch 1.2, Graphics.Draw.validate 0.6

Longest waits

  • Driver.WorkerWait (queue worker 0x0) 1.0 ms at 616 ms, present 1323
  • Driver.WorkerWait (queue worker 0x0) 1.0 ms at 4758 ms, present 1472
  • Driver.WorkerWait (queue worker 0x0) 1.0 ms at 9018 ms, present 1626
  • Driver.WorkerWait (queue worker 0x0) 1.0 ms at 8466 ms, present 1606
  • Driver.WorkerWait (queue worker 0x0) 0.9 ms at 4076 ms, present 1448
  • Driver.WorkerWait (queue worker 0x0) 0.9 ms at 8255 ms, present 1598
  • Driver.WorkerWait (queue worker 0x0) 0.9 ms at 9635 ms, present 1648
  • Driver.WorkerWait (queue worker 0x0) 0.9 ms at 962 ms, present 1336
  • Driver.WorkerWait (queue worker 0x0) 0.9 ms at 6987 ms, present 1550
  • Driver.WorkerWait (queue worker 0x0) 0.9 ms at 1368 ms, present 1350

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

  • batch 84067: 0.3 ms at 3766 ms, present 1436: GPU no ranges; recorded by Vulkan.DrawFromRecipe (draw committer) 0.1, Graphics.DrawWithRecipe (draw committer) 0.1, Graphics.DrawWithRecipe.draw_and_resource_release (draw committer) 0.1, Recorder.Submit (draw committer) 0.0
  • batch 91619: 0.3 ms at 7372 ms, present 1564: GPU no ranges; recorded by Vulkan.DrawFromRecipe (draw committer) 0.1, Graphics.DrawWithRecipe (draw committer) 0.1, Graphics.DrawWithRecipe.draw_and_resource_release (draw committer) 0.1, Recorder.Submit (draw committer) 0.0
  • batch 92317: 0.3 ms at 7672 ms, present 1576: GPU no ranges; recorded by Recorder.Submit (draw committer) 0.0, Vulkan.DrawFromRecipe (draw committer) 0.0, Graphics.DrawWithRecipe (draw committer) 0.0, Graphics.DrawWithRecipe.draw_and_resource_release (draw committer) 0.0
  • batch 88541: 0.3 ms at 5912 ms, present 1512: GPU no ranges; recorded by Recorder.Submit (draw committer) 0.0, Vulkan.DrawFromRecipe (draw committer) 0.0, Graphics.DrawWithRecipe (draw committer) 0.0, Graphics.DrawWithRecipe.draw_and_resource_release (draw committer) 0.0
  • batch 79278: 0.3 ms at 1514 ms, present 1355: GPU no ranges; recorded by Vulkan.DrawFromRecipe (draw committer) 0.0, Graphics.DrawWithRecipe (draw committer) 0.0, Graphics.DrawWithRecipe.draw_and_resource_release (draw committer) 0.0, Recorder.Submit (draw committer) 0.0
tools/trace_digest.py, this PR + #8091

seconds 40:10 from present 1646: 457 presents, frame p50 20.7 / p90 27.6 / p99 36.7 / max 38.4 ms, GPU busy 1.2 ms per present
AMD Radeon RX 7900 GRE (RADV NAVI31), radv Mesa 26.2.5-arch1.1, Linux 7.2.11-arch1-1, 1920x1080, commit 3b7c43e (dirty), dropped: no

{"commit":"3b7c43eb752e","dirty":true,"env":{"APS5_NO_PAD_INPUT":"1","APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"40:10","APS5_TRACE_SUMMARY_EVERY":"10","APS5_VIDEOOUT_TELEMETRY_DIR":"<path>"},"gpu":"AMD Radeon RX 7900 GRE (RADV NAVI31)","driver":"radv Mesa 26.2.5-arch1.1","os":"Linux 7.2.11-arch1-1","cpu":"AMD Ryzen 5 7500F 6-Core Processor","cpu_threads":12,"memory_mib":31744,"title_id":"PPSA69421","title":"SuperTuxKart","content_version":"00.001.000","resolution":"1920x1080","window":{"spec":"seconds 40:10","first_present":1646,"ms":10012.706},"presents":457,"frame_ms":{"p50":20.681,"p90":27.595,"p99":36.689,"max":38.388},"gpu_busy_ms_per_present":1.242,"worker_busy_pct":{"queue worker 0x0":4.973},"top_phases_ms_per_frame":{"Recorder.Submit":1.919,"Graphics.Draw":1.55,"Graphics.Draw.draw_and_resource_release":1.339,"Vulkan.DrawFromRecipe":1.155,"Graphics.DrawWithRecipe":1.145,"Graphics.DrawWithRecipe.draw_and_resource_release":1.14,"Driver.Packet":1.074,"Driver.Packet.draw":0.78},"trace_dropped":false,"trace":"aps5-trace-3b7c43eb752e-199732.json.gz"}

Slowest frames (largest slices inside each, inclusive)

  • present 2094: 38.4 ms at 9721 ms: Driver.WorkerWait (queue worker 0x0) 36.3, Driver.WorkerWait.idle_and_completion_poll (queue worker 0x0) 36.2, Recorder.Submit (draw committer) 2.7, Graphics.Draw (draw committer) 2.4
  • present 2095: 37.6 ms at 9759 ms: Driver.WorkerWait (queue worker 0x0) 35.9, Driver.WorkerWait.idle_and_completion_poll (queue worker 0x0) 35.8, Graphics.Draw (draw committer) 2.6, Recorder.Submit (draw committer) 2.6
  • present 2093: 37.3 ms at 9684 ms: Driver.WorkerWait (queue worker 0x0) 35.2, Driver.WorkerWait.idle_and_completion_poll (queue worker 0x0) 35.1, Recorder.Submit (draw committer) 2.7, Graphics.Draw (draw committer) 2.3
  • present 2098: 37.1 ms at 9866 ms: Driver.WorkerWait (queue worker 0x0) 35.3, Driver.WorkerWait.idle_and_completion_poll (queue worker 0x0) 35.2, Recorder.Submit (draw committer) 2.7, Graphics.Draw (draw committer) 2.7
  • present 2099: 36.7 ms at 9904 ms: Driver.WorkerWait (queue worker 0x0) 34.4, Driver.WorkerWait.idle_and_completion_poll (queue worker 0x0) 34.4, Recorder.Submit (draw committer) 2.9, Graphics.Draw (draw committer) 2.7

Threads (ms per frame)

thread busy wait untraced in slices < 20 us largest
title 0.1 (0 %) 0.0 (0 %) 21.8 0.3 Recorder.Sync 0.1
queue worker 0x0 0.3 (1 %) 20.7 (95 %) 0.9 3.1 Driver.Packet 0.3
draw committer 4.6 (21 %) 0.3 (1 %) 17.1 0.2 Recorder.Submit 1.9, Graphics.Draw 1.5
presenter 0.4 (2 %) 0.0 (0 %) 21.5 0.1 VideoOut.Flip 0.4
gpu 1.2 (6 %) 0.0 (0 %) 20.7 0.0 batch 1.2

Largest slices (ms per frame, inclusive, all threads): Driver.WorkerWait 20.7, Driver.WorkerWait.idle_and_completion_poll 20.7, Recorder.Submit 1.9, Graphics.Draw 1.5, Graphics.Draw.draw_and_resource_release 1.3, batch 1.2, Vulkan.DrawFromRecipe 1.2, Graphics.DrawWithRecipe 1.1, Graphics.DrawWithRecipe.draw_and_resource_release 1.1, VideoOut.Flip 0.4

Longest waits

  • Driver.WorkerWait (queue worker 0x0) 2.5 ms at 9816 ms, present 2096
  • Driver.WorkerWait (queue worker 0x0) 2.2 ms at 8358 ms, present 2029
  • Driver.WorkerWait (queue worker 0x0) 2.0 ms at 4458 ms, present 1852
  • Driver.WorkerWait (queue worker 0x0) 2.0 ms at 1352 ms, present 1709
  • Driver.WorkerWait (queue worker 0x0) 2.0 ms at 9828 ms, present 2096
  • Driver.WorkerWait (queue worker 0x0) 1.9 ms at 792 ms, present 1684
  • Driver.WorkerWait (queue worker 0x0) 1.9 ms at 8800 ms, present 2047
  • Driver.WorkerWait (queue worker 0x0) 1.9 ms at 8733 ms, present 2045
  • Driver.WorkerWait (queue worker 0x0) 1.8 ms at 7344 ms, present 1983
  • Driver.WorkerWait (queue worker 0x0) 1.7 ms at 8695 ms, present 2043

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

  • batch 111663: 0.3 ms at 5633 ms, present 1904: GPU no ranges; recorded by Recorder.Submit (draw committer) 0.0, Vulkan.DrawFromRecipe (draw committer) 0.0, Graphics.DrawWithRecipe (draw committer) 0.0, Graphics.DrawWithRecipe.draw_and_resource_release (draw committer) 0.0
  • batch 107189: 0.3 ms at 3970 ms, present 1828: GPU no ranges; recorded by Vulkan.DrawFromRecipe (draw committer) 0.0, Graphics.DrawWithRecipe (draw committer) 0.0, Graphics.DrawWithRecipe.draw_and_resource_release (draw committer) 0.0, Recorder.Submit (draw committer) 0.0
  • batch 115026: 0.3 ms at 6910 ms, present 1961: GPU no ranges; recorded by Recorder.Submit (draw committer) 0.0, Vulkan.DrawFromRecipe (draw committer) 0.0, Graphics.DrawWithRecipe (draw committer) 0.0, Graphics.DrawWithRecipe.draw_and_resource_release (draw committer) 0.0
  • batch 99165: 0.3 ms at 968 ms, present 1692: GPU no ranges; recorded by Vulkan.DrawFromRecipe (draw committer) 0.0, Graphics.DrawWithRecipe (draw committer) 0.0, Graphics.DrawWithRecipe.draw_and_resource_release (draw committer) 0.0, Recorder.Submit (draw committer) 0.0
  • batch 118104: 0.3 ms at 8000 ms, present 2013: GPU no ranges; recorded by Vulkan.DrawFromRecipe (draw committer) 0.0, Graphics.DrawWithRecipe (draw committer) 0.0, Graphics.DrawWithRecipe.draw_and_resource_release (draw committer) 0.0, Recorder.Submit (draw committer) 0.0
tools/performance_compare.py

Performance comparison

Capture Commit Trace Presents Window (ms)
baseline b7d1d47 aps5-trace-b7d1d47a5f9f-164176.json.gz 360 10015.333
candidate 3b7c43e aps5-trace-3b7c43eb752e-199732.json.gz 457 10012.706
  • Compare captures from the same machine, title, content version and scene; matching recorded metadata cannot verify the physical machine or scene.
  • Changes are candidate minus baseline. A negative frame-time change means a shorter frame interval.
  • A missing top phase is unreported, not zero; its change is unavailable.
  • Recorded configuration differs; these measurements do not isolate the code change.
  • baseline: source tree is dirty or its state is unknown; the commit alone does not identify the build.
  • candidate: source tree is dirty or its state is unknown; the commit alone does not identify the build.

Configuration differences

Setting Baseline Candidate
window.first_present 1302 1646

Frame and GPU times (ms)

Metric Baseline Candidate Change Change (%)
frame_ms.p50 27.476 20.681 -6.795 -24.731
frame_ms.p90 31.046 27.595 -3.451 -11.116
frame_ms.p99 32.762 36.689 +3.927 +11.986
frame_ms.max 33.129 38.388 +5.259 +15.874
mean_frame_ms 27.820 21.910 -5.911 -21.246
gpu_busy_ms_per_present 1.247 1.242 -0.005 -0.401

Top phases (ms per frame)

Metric Baseline Candidate Change Change (%)
Driver.Packet 1.010 1.074 +0.064 +6.337
Driver.Packet.draw 0.737 0.780 +0.043 +5.834
Graphics.Draw 2.664 1.550 -1.114 -41.817
Graphics.Draw.draw_and_resource_release 2.006 1.339 -0.667 -33.250
Graphics.DrawWithRecipe 2.270 1.145 -1.125 -49.559
Graphics.DrawWithRecipe.draw_and_resource_release 2.265 1.140 -1.125 -49.669
Recorder.Submit 1.802 1.919 +0.117 +6.493
Vulkan.DrawFromRecipe 2.280 1.155 -1.125 -49.342

Other measurements (same machine and scene, menu, main eaff876 + #8091):

  • The walk is the whole cost. A local build that skipped the walk (unsafe, measurement only) ran ~50 FPS with 616 live imports. APS5_PROFILE_GPU=1 shows the same recorded GPU time per present on both sides (2.4 ms in 59 batches), and APS5_NO_DRAW_INPUTS_IN_PLACE=1 changes nothing. So this is not perf: Red Dead Redemption (PPSA30528) G-buffer draws spend ~100x their GPU time reading vertex/index data in place from host imports #5895's in-place read cost.
  • perf before: importRun 4.3%, _Rb_tree_increment 2.3%, leasedIndexContaining 1.8% and sameRange 1.6% of user samples. With this PR, importRun, leasedIndexContaining and sameRange stay under 0.3%. refreshImports itself is 1.7%: the lease diff is linear in the ~800 registered ranges, and the remembered failures and refusals are still iterated.
  • Equivalence check. A local check mode ran the full walk's sameRange for every import the incremental refresh skipped. With the default budget it found 0 differences in ~3.4M skipped checks. None of the skipped unwatched imports were back in the write watch, and no imports overlapped. With a 128 MiB budget, the only differences were the refusal-dependent run shapes described above.

AGC, shader recompiler: ctest --test-dir build -R "agc|shader|recompiler" -V -j6 with ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1 and VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation, on main b7d1d47a5f and on this change, same machine. Neither log contains was not found but was requested by env var VK_INSTANCE_LAYERS.

Overlap with open pull requests, from tools/pr_overlap.py on b7d1d47a5f:

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 under Silent stubs in their library's technical debt file
  • 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
  • Fixes: none
  • Unblocks: none
  • GPUs tested: AMD Radeon RX 7900 GRE (RADV NAVI31), Mesa 26.2.5, Arch Linux kernel 7.2.11
  • AI-assisted: yes

@github-actions github-actions Bot added perf Performance change perf numbers missing perf pull request without a before and after summary line labels Oct 21, 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.

@janzezno

janzezno commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor Author

Summary files (APS5_TRACE_SUMMARY_EVERY=10) of the six SuperTuxKart runs in the table above, on main 0c6a5bd:

GitHub does not accept .jsonl, so each file is a JSON array of its interval lines. To digest one with tools/trace_digest.py, convert it back first:

python3 -c "import json,sys; [print(json.dumps(r)) for r in json.load(open(sys.argv[1]))]" aps5-summary-main+8091-run1.json > run1.jsonl
python3 tools/trace_digest.py run1.jsonl

@github-actions github-actions Bot removed the perf numbers missing perf pull request without a before and after summary line label Oct 21, 2026
@janzezno

Copy link
Copy Markdown
Contributor Author

Traces of the two windows whose summary lines are in the description: main + #8091 (0c6a5bd) and this PR + #8091 (ceefc5b), APS5_TRACE_SECONDS=40:10.
aps5-trace-0c6a5bd5938f-742372.json.gz
aps5-trace-ceefc5bcdeac-742647.json.gz

@github-actions

github-actions Bot commented Oct 21, 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
PR 8926 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp L1906-1909, L1919-1925
PR 9879 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp L1901

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 1191 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 1368 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 1395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 2434 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 4448 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 4449 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 5021 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 5058 docs/dev/debt/libSceAgcDriver.md
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5431 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6044 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6169 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6186 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6187 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6188 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6192 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6194 docs/dev/debt/libSceAgcDriver.md
PR 6197 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 6209 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6223 docs/dev/debt/libSceAgcDriver.md
PR 6273 docs/dev/debt/libSceAgcDriver.md
PR 6280 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6292 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 6300 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6412 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6481 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6501 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6548 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6549 docs/dev/debt/libSceAgcDriver.md
PR 6565 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 6612 docs/dev/debt/libSceAgcDriver.md
PR 6615 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 6639 docs/dev/debt/libSceAgcDriver.md
PR 6642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6741 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6788 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6801 docs/dev/debt/libSceAgcDriver.md
PR 6804 docs/dev/debt/libSceAgcDriver.md
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6867 docs/dev/debt/libSceAgcDriver.md
PR 6868 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6959 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6971 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6977 docs/dev/debt/libSceAgcDriver.md
PR 7201 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 7256 docs/dev/debt/libSceAgcDriver.md
PR 7275 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7457 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7604 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7705 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7803 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 8246 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8281 docs/dev/debt/libSceAgcDriver.md
PR 8365 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 8395 docs/dev/debt/libSceAgcDriver.md
PR 8424 docs/dev/debt/libSceAgcDriver.md
PR 8484 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 8527 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 8542 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 8552 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8592 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 8672 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 8999 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 9020 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 9077 docs/dev/debt/libSceAgcDriver.md
PR 9186 docs/dev/debt/libSceAgcDriver.md
PR 9335 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 9387 docs/dev/debt/libSceAgcDriver.md
PR 9431 docs/dev/debt/libSceAgcDriver.md
PR 9432 docs/dev/debt/libSceAgcDriver.md
PR 9450 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 9592 docs/dev/debt/libSceAgcDriver.md
PR 9672 docs/dev/debt/libSceAgcDriver.md
PR 9673 docs/dev/debt/libSceAgcDriver.md
PR 9674 docs/dev/debt/libSceAgcDriver.md
PR 9675 docs/dev/debt/libSceAgcDriver.md
PR 9676 docs/dev/debt/libSceAgcDriver.md
PR 9700 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 9721 docs/dev/debt/libSceAgcDriver.md
PR 9748 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 9891 docs/dev/debt/libSceAgcDriver.md
PR 9892 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 9893 docs/dev/debt/libSceAgcDriver.md
PR 9895 docs/dev/debt/libSceAgcDriver.md

@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.

This is a substantial performance investigation. The report includes same-machine SuperTuxKart comparisons, traces, an equivalence check across millions of skipped validations, a large focused test set, and paired Vulkan validation results with matching VUIDs. git diff --check passes; GitHub reports the head mergeable, and eligibility, conventions, intake, labels, and report checks pass.

I’m requesting changes for the remaining contribution-rule items:

  • The benchmark and test descriptions use bases 0c6a5bd593 and eaff87674c; current main is 4a17c71757. Please rebase onto current main, rerun the focused AGC tests on the same machine and GPU with the required validation-layer settings on both current main and the change, compare VUIDs, and refresh the performance summaries/traces against that base.
  • Please coordinate the shared refresh-loop behavior with #8926 before merge. Your overlap analysis notes that #8926 re-inserts imports in this loop and that the incremental path must add those imports to pendingImports if both changes land. Agree on order with its author and reflect any prerequisite in Depends on:. Also refresh the overlap check and coordinate any actual same-line overlaps found in the import insertion, test registration, and debt entry with their authors; the current body records adjacent conflicts with #7261, #6820/#8104, and #7844, while the current overlap scan did not complete in my shared checkout due concurrent temporary-ref updates.

No local AGC test or GPU run was possible in this review environment. The reported older-base results are useful evidence, but the repository requires current-main validation for this change before it is ready.

refreshImports checked every live host import, failure and refusal
again at each guest allocation registry change. A title that maps
memory often (SuperTuxKart, ~1650 maps a second) paid a walk that grew
with the live imports: ~34 FPS with ~1.1 GiB of imports on RADV.

The import table now keeps a weak_ptr snapshot of the ranges it was
last checked against and diffs it with the new lease. Only imports
whose import window touches a changed span, and imports made since the
last refresh, are checked again. Every import is checked when the window
is 0 or the window or budget changed. SuperTuxKart's menu goes from
~34 to ~45 FPS with the same imports live.
@janzezno
janzezno force-pushed the perf/incremental-import-refresh branch from ceefc5b to 3b7c43e Compare October 22, 2026 18:29
@janzezno

Copy link
Copy Markdown
Contributor Author

@janzezno

Copy link
Copy Markdown
Contributor Author

@MarcZX10 rebased onto b7d1d47a5f; the head is now 3b7c43eb75. On that base I reran the focused AGC tests with the validation layer on main and on this change (same three failures, same VUIDs with the same counts) and the SuperTuxKart comparison with #8091 on both sides (36.6 → 53.0 FPS average over three runs each, same live imports). The new summary files and traces are attached above, and the description has the results.

On the overlaps: I moved the new lines away from the neighbouring edits in importRunAt, retireImport, the refreshImports reset, the test registration and the debt file, without changing what they do. pr_overlap now reports only #8926. Its current version records the ranges of retired imports and rewatches them after the loop, and it no longer re-inserts imports, so pendingImports is not involved anymore. I've posted in #8926 where its two additions go in this PR's loop and proposed that neither declares a dependency and the one that merges second rebases. I'll record the answer in the description.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants