Skip to content

perf(agc): find a range's newest writer through a granule index instead of scanning the ring - #12264

Merged
oneandonlydean merged 2 commits into
boykopovar:mainfrom
Tehshortbus:perf/agc-written-buffer-index
Oct 29, 2026
Merged

oneandonlydean merged 2 commits into
boykopovar:mainfrom
Tehshortbus:perf/agc-written-buffer-index

Conversation

@Tehshortbus

@Tehshortbus Tehshortbus commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

What

Driver::newestWriterLocked (used by classifyPendingWrite when a capture reads memory with pending GPU writes, and by write evidence) walked the 4096-entry written-buffer ring from the newest entry on every lookup, so a range without a recorded writer cost a full walk. In GTA V cutscenes it was the hottest function on the packet thread not touched by an open pull request.

The ring is now a WrittenBufferIndex (Execution/include/Driver/Memory/WrittenBufferIndex.hpp). It keeps the same deque, capacity and eviction order, and also files each entry's position under the 64 KiB granules it covers; entries wider than 16 granules (1 MiB) go on a short list of their own so a large buffer does not make a push expensive. Newest(begin, end) walks only the query's granules and that list, newest first, applies the same overlap test as before and returns the newest match, so it returns the same entry as the walk (an empty entry is filed under the granule of its address, since the old test also matches it). Queries wider than 16 granules, and empty queries, still walk the ring. WrittenBuffer and WrittenBufferRing move into the new header unchanged; the three push sites call Push, and describeWriters iterates Entries().

Memory. The entries stay in the same 4096-entry deque as before (64 bytes each, 0.25 MiB). On top of that, the index keeps one unordered_map node with a std::deque<std::uint64_t> for each distinct 64 KiB granule that a non-wide entry covers. With its first 512-byte chunk, that is about 700 bytes per granule on libstdc++. A granule's 65th and later sequences cost 8 bytes each. A non-wide entry covers at most 16 granules, so the index files at most 4096 × 16 = 65,536 granules. Wider entries cost 8 bytes each on their own list. Measured with a counting operator new around the class (GCC 16.2.1, libstdc++), as the peak live heap of the class including its entries while pushing three times the ring capacity:

Last 4096 recorded writes Peak
one 4 KiB buffer, rewritten 0.30 MiB
4096 distinct 4 KiB buffers (1 granule each) 3.0 MiB
4096 distinct 256 KiB buffers (5 granules each) 14.0 MiB
4096 distinct buffers spanning 16 granules each (about 1 MiB, the widest that is indexed) 44.4 MiB
4096 distinct 2 MiB buffers (all on the wide list) 0.30 MiB

So the upper bound is about 44 MiB, against 0.25 MiB for the ring on main. It is reached only when the last 4096 recorded writes went to 4096 different buffers of about 1 MiB that share no granules. In GTA V (PPSA04264 01.011.001) the index stays far below that. I ran a 307 s session from the title through the save load, the first Story cutscene and 136 s of the second, with temporary counters sampled each second. The sample estimate counts the same allocations as the table and reads within 3 % of it, slightly high, for each row. The ring filled to 4096 entries within 66 s of the first recorded write. The peak was 2,360 granules and 639 wide entries, about 1.9 MiB, while the save loaded. During cutscene 1 the index held 25 to 65 granules (about 0.4 MiB), and during cutscene 2 at most 322 granules (about 0.5 MiB).

Tested

CachyOS Linux (kernel 7.3.0-4-cachyos), GCC 16.2.1, Release, NVIDIA GeForce RTX 4070 SUPER (615.78.08).

  • agc_written_buffer_index (new): pushes 20,000 random entries (empty, small, medium and wider than 1 MiB) into a 64-entry index, so it evicts constantly, and after each push compares 8 random queries (also empty and wider than 16 granules) against the linear walk over Entries(); plus explicit newest-wins and eviction cases. With the wide-entry list left out of Newest, it fails on the first differing query.

  • Focused tests, main 0cd07dc4ab and this change on it, with ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation ctest --test-dir build-tests -V -R '^(agc_driver_copied_writer_copy|agc_driver_copied_writer_label_order|agc_driver_pending_exact_surface|agc_driver_indirect_arguments_pending_producer|agc_driver_gated_dispatch_capture|agc_space_pending_writes|agc_pending_write_snapshot|agc_written_buffer_index)$': main 7/7 pass, this change 8/8 pass; no VUID- line and no missing-layer message in either log. Under gdb, WrittenBufferIndex::Push is reached 65 times in agc_driver_gated_dispatch_capture and 85 times in agc_driver_indirect_arguments_pending_producer, and Newest 4 times in the latter; the other tests do not reach the ring, so the lookup's equivalence rests mainly on the new test.

  • GTA V (PPSA04264 01.011.001), perf record -F 499 -e cpu-clock:u --call-graph dwarf over the first 20 s of the first two Story cutscenes, one run per build, both on main 86fb985abb + fix(shader): load and store 16-bit data of converted packed images #11580 (which the title needs, GTA V (PPSA04264) stops when the save loads: compute shader reads or writes a converted float image with 16-bit data #11425) + chore(agc): log the storage writeback submit and done lines only with APS5_DEBUG_LOG #12244, the second with this change cherry-picked; same machine, warm shader cache. Share of all samples:

    cutscene 1: base → this cutscene 2: base → this
    newestWriterLocked (with callees) 0.65 % → 0.21 % 0.39 % → 0.04 %
    classifyPendingWrite (with callees) 0.60 % → 0.27 % 0.44 % → 0.07 %
    WrittenBufferIndex::Push + evictOldest – → 0.02 % – → 0.02 %
    queue worker 0 busy (user time) 56.8 % → 57.5 % 64.2 % → 64.2 %

    The saving is about 1 % of the packet thread, below the run-to-run variation of whole-thread and frame-time numbers, so I am not claiming a frame-rate change.

  • Performance captures (details, alignment and trace_digest.py output in the comment below; traces attached there): aps5-summary.jsonl lines of the 75–105 s window, base 9f2d365268 then this PR e98000093a:

    {"commit":"9f2d365268de","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_EVENTS":"4194304","APS5_TRACE_SECONDS":"75:30","APS5_TRACE_SUMMARY_EVERY":"10"},"gpu":"NVIDIA GeForce RTX 4070 SUPER","driver":"NVIDIA 615.78.08","os":"Linux 7.3.0-4-cachyos","cpu":"AMD Ryzen 7 5800X 8-Core Processor","cpu_threads":16,"memory_mib":64203,"title_id":"PPSA04264","title":"Grand Theft Auto V","content_version":"01.011.001","resolution":"3840x2160","window":{"spec":"seconds 75:30","first_present":2444,"ms":30014.412},"presents":403,"frame_ms":{"p50":73.748,"p90":79.862,"p99":110.775,"max":138.755},"gpu_busy_ms_per_present":36.963,"worker_busy_pct":{"queue worker 0x0":97.973},"top_phases_ms_per_frame":{"Driver.Packet":69.797,"Graphics.Draw":41.297,"Driver.Packet.draw":37.758,"Driver.Draw":37.408,"Graphics.Draw.draw_and_resource_release":35.681,"Driver.Packet.dispatch":24.439,"Driver.Draw.shader_compile_and_link":24.424,"Shader.DrawStage":18.971},"trace_dropped":false,"trace":"aps5-trace-9f2d365268de-1333741.json.gz"}
    {"commit":"e98000093a1a","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_EVENTS":"4194304","APS5_TRACE_SECONDS":"75:30","APS5_TRACE_SUMMARY_EVERY":"10"},"gpu":"NVIDIA GeForce RTX 4070 SUPER","driver":"NVIDIA 615.78.08","os":"Linux 7.3.0-4-cachyos","cpu":"AMD Ryzen 7 5800X 8-Core Processor","cpu_threads":16,"memory_mib":64203,"title_id":"PPSA04264","title":"Grand Theft Auto V","content_version":"01.011.001","resolution":"3840x2160","window":{"spec":"seconds 75:30","first_present":2741,"ms":30014.360},"presents":451,"frame_ms":{"p50":68.988,"p90":76.357,"p99":108.921,"max":588.045},"gpu_busy_ms_per_present":33.496,"worker_busy_pct":{"queue worker 0x0":93.744},"top_phases_ms_per_frame":{"Driver.Packet":59.260,"Graphics.Draw":33.805,"Graphics.Draw.draw_and_resource_release":29.403,"Driver.Packet.draw":29.328,"Driver.Draw":29.035,"Driver.Packet.dispatch":20.850,"Driver.Draw.shader_compile_and_link":18.695,"Recorder.Sync":16.659},"trace_dropped":false,"trace":"aps5-trace-e98000093a1a-1327440.json.gz"}
  • python3 tools/check_conventions.py --base origin/main passes.

Overlap: tools/pr_overlap.py reports #11439, which changes the program argument of the writtenBuffers.push_back call in BufferCopy.cpp (L131) that this PR turns into writtenBuffers.Push. Whichever merges second keeps #11439's argument inside the other call; no behaviour overlaps. The 66 pull requests it lists as not compared (they conflict with main) share CMakeLists.txt or Driver.hpp; I checked their diffs (#5021 by its file list and its CMakeLists.txt patch, since its full diff is too large to fetch): none adds, removes or edits the writtenBuffers member, WrittenBuffer, WrittenBufferRing or newestWriterLocked.

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: NVIDIA GeForce RTX 4070 SUPER, driver 615.78.08, CachyOS Linux (kernel 7.3.0-4-cachyos)
  • AI-assisted: yes (Claude Code)

🤖 Generated with Claude Code

…ad of scanning the ring

newestWriterLocked walked the 4096-entry written-buffer ring from the newest
entry on every lookup, so a range without a recorded writer cost a full walk.
The ring is now a WrittenBufferIndex that also files each entry under the
64 KiB granules it covers (entries wider than 16 granules on a short list of
their own); a lookup walks only the query's granules and that list and returns
the same entry as the walk. Queries wider than 16 granules still walk the ring.

In GTA V (PPSA04264) cutscenes, newestWriterLocked went from 0.65 % to 0.21 %
and from 0.39 % to 0.04 % of all CPU samples, and classifyPendingWrite with its
callees from 0.60 % to 0.27 % and from 0.44 % to 0.07 %; keeping the index adds
about 0.03 %.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added perf Performance change perf numbers missing perf pull request without a before and after summary line labels Oct 28, 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.

The MinGW build takes the C++ runtime from libc, so the test failed to link
there with an undefined __gxx_personality_v0. It now links libc, gets the
Windows unwind section rename and libc's directory on PATH, like
agc_thread_priority_tests.

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

Copy link
Copy Markdown
Contributor Author

Performance captures for this PR, as the form asks. Same machine (CachyOS Linux, kernel 7.3.0-4-cachyos, RTX 4070 SUPER, NVIDIA 615.78.08, Ryzen 7 5800X), same title and save (GTA V PPSA04264 01.011.001, Story mode from load), warm shader cache, log printed to the terminal, and only the capture switches APS5_TRACE_SECONDS=75:30 APS5_TRACE_EVENTS=4194304 APS5_TRACE_SUMMARY_EVERY=10 APS5_TRACE_DIR.

Both builds are main 86fb985abb + #11580 (the title needs it to get past #11425) + #12244 (logging only): 9f2d365268 is that base, e98000093a is the base with this PR's commit cherry-picked. One run each.

Summary lines of the trace window (75–105 s after the first present, in the first cutscene):

base 9f2d365268:

{"commit":"9f2d365268de","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_EVENTS":"4194304","APS5_TRACE_SECONDS":"75:30","APS5_TRACE_SUMMARY_EVERY":"10"},"gpu":"NVIDIA GeForce RTX 4070 SUPER","driver":"NVIDIA 615.78.08","os":"Linux 7.3.0-4-cachyos","cpu":"AMD Ryzen 7 5800X 8-Core Processor","cpu_threads":16,"memory_mib":64203,"title_id":"PPSA04264","title":"Grand Theft Auto V","content_version":"01.011.001","resolution":"3840x2160","window":{"spec":"seconds 75:30","first_present":2444,"ms":30014.412},"presents":403,"frame_ms":{"p50":73.748,"p90":79.862,"p99":110.775,"max":138.755},"gpu_busy_ms_per_present":36.963,"worker_busy_pct":{"queue worker 0x0":97.973},"top_phases_ms_per_frame":{"Driver.Packet":69.797,"Graphics.Draw":41.297,"Driver.Packet.draw":37.758,"Driver.Draw":37.408,"Graphics.Draw.draw_and_resource_release":35.681,"Driver.Packet.dispatch":24.439,"Driver.Draw.shader_compile_and_link":24.424,"Shader.DrawStage":18.971},"trace_dropped":false,"trace":"aps5-trace-9f2d365268de-1333741.json.gz"}

with this PR e98000093a:

{"commit":"e98000093a1a","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_EVENTS":"4194304","APS5_TRACE_SECONDS":"75:30","APS5_TRACE_SUMMARY_EVERY":"10"},"gpu":"NVIDIA GeForce RTX 4070 SUPER","driver":"NVIDIA 615.78.08","os":"Linux 7.3.0-4-cachyos","cpu":"AMD Ryzen 7 5800X 8-Core Processor","cpu_threads":16,"memory_mib":64203,"title_id":"PPSA04264","title":"Grand Theft Auto V","content_version":"01.011.001","resolution":"3840x2160","window":{"spec":"seconds 75:30","first_present":2741,"ms":30014.360},"presents":451,"frame_ms":{"p50":68.988,"p90":76.357,"p99":108.921,"max":588.045},"gpu_busy_ms_per_present":33.496,"worker_busy_pct":{"queue worker 0x0":93.744},"top_phases_ms_per_frame":{"Driver.Packet":59.260,"Graphics.Draw":33.805,"Graphics.Draw.draw_and_resource_release":29.403,"Driver.Packet.draw":29.328,"Driver.Draw":29.035,"Driver.Packet.dispatch":20.850,"Driver.Draw.shader_compile_and_link":18.695,"Recorder.Sync":16.659},"trace_dropped":false,"trace":"aps5-trace-e98000093a1a-1327440.json.gz"}

The fixed window starts at the same time after the first present, but the first cutscene began about 15 s earlier in the base run (its 60–70 s interval against 80–90 s here), so the two windows cover different stretches of it. Aligned on the cutscene start instead, the APS5_TRACE_SUMMARY_EVERY=10 intervals from 10 to 100 s into it have a median frame p50 of 73.5 ms (69.2–75.9) on the base and 70.8 ms (68.5–73.4) with this PR, and the first 30 s of the second cutscene 156–224 ms (base, 171–201 s) and 179–208 ms (this PR, 190–220 s). Those differences are within what I see between runs of the same build, so as in the description I am not claiming a frame-time change; the measured effect is the one in the description, newestWriterLocked and classifyPendingWrite taking a smaller share of the packet thread.

The two traces are attached below (aps5-trace-9f2d365268de-1333741.json.gz, 16 MB, and aps5-trace-e98000093a1a-1327440.json.gz, 13 MB).

tools/trace_digest.py, base 9f2d365268

seconds 75:30 from present 2444: 403 presents, frame p50 73.7 / p90 79.9 / p99 110.8 / max 138.8 ms, GPU busy 37.0 ms per present
NVIDIA GeForce RTX 4070 SUPER, NVIDIA 615.78.08, Linux 7.3.0-4-cachyos, 3840x2160, commit 9f2d365268de, dropped: no

{"commit":"9f2d365268de","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_EVENTS":"4194304","APS5_TRACE_SECONDS":"75:30","APS5_TRACE_SUMMARY_EVERY":"10"},"gpu":"NVIDIA GeForce RTX 4070 SUPER

Slowest frames (largest slices inside each, inclusive)

  • present 2615: 138.8 ms at 13244 ms: Driver.Packet (queue worker 0x0) 115.1, Driver.Packet.draw (queue worker 0x0) 76.6, Driver.Draw (queue worker 0x0) 76.3, Graphics.Draw (draw committer) 67.3
  • present 2721: 126.5 ms at 20768 ms: Driver.Packet (queue worker 0x0) 108.4, Driver.Packet.draw (queue worker 0x0) 77.8, Driver.Draw (queue worker 0x0) 77.5, Driver.Draw.shader_compile_and_link (queu
  • present 2497: 116.6 ms at 4056 ms: Driver.Packet (queue worker 0x0) 97.2, Driver.Packet.draw (queue worker 0x0) 68.4, Driver.Draw (queue worker 0x0) 68.0, Graphics.Draw (draw committer) 63.6
  • present 2553: 113.5 ms at 8461 ms: Driver.Packet (queue worker 0x0) 94.1, Driver.Packet.draw (queue worker 0x0) 65.9, Driver.Draw (queue worker 0x0) 65.5, Driver.Draw.shader_compile_and_link (queue
  • present 2842: 110.8 ms at 29596 ms: Driver.Packet (queue worker 0x0) 90.7, Driver.Packet.draw (queue worker 0x0) 59.2, Driver.Draw (queue worker 0x0) 58.8, Graphics.Draw (draw committer) 45.1

Threads (ms per frame)

thread busy wait untraced in slices < 20 us largest
title 0.0 (0 %) 0.0 (0 %) 74.5 0.0 Shader.ResolveGraphicsStagesAbi 0.0
queue worker 0x0 57.6 (77 %) 1.3 (2 %) 15.6 80.0 Driver.Packet 56.1, Driver.Rewind 1.6
draw committer 44.3 (60 %) 0.0 (0 %) 30.1 26.6 Graphics.Draw 30.9, Recorder.Sync 13.2
presenter 0.4 (1 %) 13.8 (18 %) 60.3 0.1 VideoOut.Flip 14.2
gpu 36.9 (50 %) 0.0 (0 %) 37.5 0.0 batch 36.9

Largest slices (ms per frame, inclusive, all threads): Driver.Packet 56.1, batch 36.9, Graphics.Draw 30.9, Driver.Packet.draw 28.3, Driver.Draw 27.9, Graphics.Draw.draw_and_resource_release 25.8,

Longest waits

  • Driver.Present.render_fence_wait (presenter) 16.4 ms at 14724 ms, present 2634
  • Driver.Present.render_fence_wait (presenter) 16.3 ms at 10861 ms, present 2584
  • Driver.Present.render_fence_wait (presenter) 16.1 ms at 17992 ms, present 2680
  • Driver.Present.render_fence_wait (presenter) 15.8 ms at 7300 ms, present 2538
  • Driver.Present.render_fence_wait (presenter) 15.8 ms at 10326 ms, present 2577
  • Driver.Present.render_fence_wait (presenter) 15.8 ms at 17920 ms, present 2679
  • Driver.Present.render_fence_wait (presenter) 15.6 ms at 6661 ms, present 2530
  • Driver.Present.render_fence_wait (presenter) 15.5 ms at 16067 ms, present 2653
  • Driver.Present.render_fence_wait (presenter) 15.5 ms at 27162 ms, present 2809
  • Driver.Present.render_fence_wait (presenter) 15.5 ms at 11021 ms, present 2586
tools/trace_digest.py, this PR e98000093a

seconds 75:30 from present 2741: 451 presents, frame p50 69.0 / p90 76.4 / p99 108.9 / max 588.0 ms, GPU busy 33.5 ms per present
NVIDIA GeForce RTX 4070 SUPER, NVIDIA 615.78.08, Linux 7.3.0-4-cachyos, 3840x2160, commit e98000093a1a, dropped: no

{"commit":"e98000093a1a","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_EVENTS":"4194304","APS5_TRACE_SECONDS":"75:30","APS5_TRACE_SUMMARY_EVERY":"10"},"gpu":"NVIDIA GeForce RTX 4070 SUPER

Slowest frames (largest slices inside each, inclusive)

  • present 2798: 588.0 ms at 1877 ms: Driver.Packet (queue worker 0x0) 559.1, Graphics.Draw (draw committer) 307.0, Driver.Packet.draw (queue worker 0x0) 268.7, Driver.Draw (queue worker 0x0) 268.3
  • present 2799: 221.4 ms at 2465 ms: batch (gpu) 186.9, Driver.Packet (queue worker 0x0) 147.3, Recorder.Sync (draw committer) 98.6, Driver.Packet.dispatch (queue worker 0x0) 85.1
  • present 3164: 137.8 ms at 27998 ms: Driver.Packet (queue worker 0x0) 115.2, Driver.Packet.draw (queue worker 0x0) 76.6, Driver.Draw (queue worker 0x0) 76.3, Graphics.Draw (draw committer) 65.0
  • present 3043: 113.6 ms at 18890 ms: Driver.Packet (queue worker 0x0) 87.5, Graphics.Draw (draw committer) 64.4, Driver.Packet.draw (queue worker 0x0) 57.8, Driver.Draw (queue worker 0x0) 57.4
  • present 3087: 108.9 ms at 22183 ms: Driver.Packet (queue worker 0x0) 89.5, Driver.Packet.draw (queue worker 0x0) 63.4, Driver.Draw (queue worker 0x0) 63.0, Driver.Draw.shader_compile_and_link (queue

Threads (ms per frame)

thread busy wait untraced in slices < 20 us largest
title 0.0 (0 %) 0.0 (0 %) 66.5 0.0 Shader.ResolveGraphicsStagesAbi 0.0
shader prepare 0.0 (0 %) 0.0 (0 %) 66.6 0.0 Vulkan.GraphicsPipelinePrebuild 0.0
title 0.0 (0 %) 0.0 (0 %) 66.5 0.0 Recorder.Sync 0.0
queue worker 0x0 49.1 (74 %) 4.7 (7 %) 12.8 64.5 Driver.Packet 48.0, Driver.EndSubmission 1.5
draw committer 37.2 (56 %) 0.1 (0 %) 29.2 20.9 Graphics.Draw 25.4, Recorder.Sync 11.6
presenter 0.5 (1 %) 12.5 (19 %) 53.6 0.1 VideoOut.Flip 13.0
gpu 33.6 (51 %) 0.0 (0 %) 32.9 0.0 batch 33.6

Largest slices (ms per frame, inclusive, all threads): Driver.Packet 48.0, batch 33.6, Graphics.Draw 25.4, Graphics.Draw.draw_and_resource_release 21.7, Driver.Packet.draw 21.4, Driver.Draw 21.1,

Longest waits

  • Driver.Present.gpu_mutex_wait (presenter) 61.9 ms at 1877 ms, present 2798
  • Driver.Present.render_fence_wait (presenter) 33.9 ms at 2465 ms, present 2799
  • Driver.WorkerWait (queue worker 0x0) 33.3 ms at 234 ms, present 2747
  • Driver.WorkerWait (queue worker 0x0) 33.0 ms at 640 ms, present 2759
  • Driver.WorkerWait (queue worker 0x0) 32.7 ms at 370 ms, present 2751
  • Driver.WorkerWait (queue worker 0x0) 32.7 ms at 404 ms, present 2752
  • Driver.WorkerWait (queue worker 0x0) 32.7 ms at 1747 ms, present 2792
  • Driver.WorkerWait (queue worker 0x0) 32.6 ms at 201 ms, present 2746
  • Driver.WorkerWait (queue worker 0x0) 32.6 ms at 0 ms, present 2741

AI-assisted: yes (Claude Code)

@Tehshortbus

Copy link
Copy Markdown
Contributor Author

Traces for the performance comment above, zipped because GitHub rejects .json.gz (each zip holds the original trace unchanged):

Unzip and open the .json.gz in ui.perfetto.dev, or run tools/trace_digest.py on it.

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

github-actions Bot commented Oct 28, 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 11439 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/BufferCopy.cpp L131-132

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 2434 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 4449 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5021 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5609 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
PR 6185 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6186 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6187 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6188 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
PR 6192 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6209 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6280 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6300 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6412 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6481 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6804 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6905 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7048 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7275 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
PR 7369 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
PR 7457 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7464 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7514 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7604 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/WrittenBuffers.cpp
PR 7689 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7978 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
PR 8014 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8246 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8484 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8542 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8592 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9508 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/WrittenBuffers.cpp
PR 9712 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9748 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9897 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9898 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
PR 9899 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10026 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10193 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/WrittenBuffers.cpp
PR 10233 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10442 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10472 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10757 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10920 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 11554 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 11825 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/WrittenBuffers.cpp
PR 12070 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 12862 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 12904 core/libs/prx/libSceAgcDriver/CMakeLists.txt

@Tehshortbus

Copy link
Copy Markdown
Contributor Author

Checked the 60 pull requests listed as not compared by hand (their diffs, and #5021's file list and CMakeLists.txt patch since its full diff is too large to fetch): none adds, removes or edits the writtenBuffers member, WrittenBuffer, WrittenBufferRing or newestWriterLocked; they share CMakeLists.txt or Driver.hpp only elsewhere.

#11439 is the one real overlap, already noted in the description: it changes the program argument of the writtenBuffers.push_back call in BufferCopy.cpp (L131) that this PR turns into writtenBuffers.Push. Whichever merges second keeps #11439's argument inside the other call.

🤖 Addressed by Claude Code

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@Tehshortbus thanks for the captures and the trace digests; the performance report is complete, and the libc link fix in e316011 is fine. One item is still open before this lands, since write evidence runs for every title:

Memory and cost bound. Please state in the description what the index can hold. The ring is 4096 entries, each non-wide entry takes up to 17 granule slots, and each distinct granule costs a hash node plus a std::deque (its first chunk is about half a kilobyte), so I estimate a few MB for typical buffers and up to about 50 MB if 4096 distinct buffers of about 1 MiB are live. If you have the real peak from a title run, give that instead.

@github-actions github-actions Bot added build agc libSceAgc* labels Oct 28, 2026
@Tehshortbus

Copy link
Copy Markdown
Contributor Author

@oneandonlydean The description has a new Memory section: the measured bound (about 44 MiB at worst, for 4096 distinct buffers of about 1 MiB, against 0.25 MiB for the ring on main) and the peak from a full GTA V run (about 1.9 MiB during the save load, 0.4–0.5 MiB in the first two cutscenes).

@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 the index against the previous reverse linear scan. Each entry is indexed in every 64 KiB granule it overlaps (or the wide-entry list), eviction removes exactly the oldest sequence, and candidates are rechecked with the original half-open overlap predicate before choosing the newest sequence. The randomized differential test also covers empty and wide queries, eviction, and newest-wins behavior. The PR now quantifies index memory and reports paired title captures, trace summaries, same-GPU focused baseline/change tests, no new VUIDs, and validation-layer presence. Hosted Windows and conventions pass; the other product build jobs are skipped by eligibility. The reported modest lookup improvement is scoped appropriately and does not claim a frame-rate win. No remaining blocker found.

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

Labels

agc libSceAgc* build perf Performance change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants