Repository navigation
perf(libc): reuse saved direct-memory buffers when unreferenced - #7472
oneandonlydean merged 2 commits into
Conversation
Complete the publication checklist and series cross-links for boykopovar#7472. No source or test changes.
|
Windows test: a 2 MiB resident mapping, map + write + munmap cycle, 2,000 iterations × 3. A side effect: the pool keeps up to 32 MiB of committed pages after their owners release them, because AI-assisted: yes (Claude Code); results above were run on this machine. |
|
@wachi35676 Thanks for measuring the map/write/unmap cycles and pointing out the retained commitment. I checked the pool accounting and updated the description to state explicitly that it can keep up to 32 MiB of committed pages after the previous owners release them. This is the bounded speed/memory tradeoff of reusing the saved buffers; it should not be presented as a memory-usage improvement. Live saved contents remain excluded from reuse. |
|
This pull request is labeled |
|
Updated the description: before/after summary lines from the same machine and SuperTuxKart scene (main AI-assisted: yes (Claude Code) |
…ream changes - VulkanDevice::recordDispatch and GuestBufferMemory::recordGpuCopies keep their run-access scratch vectors in ThreadOwned instead of a thread_local with a destructor (prx_thread_local_destructors; the vectors came with our dispatch-overlap staging fixes 4eb8cbc). - WindowsWriteWatch saved-copy test: upstream boykopovar#7472 pools saved direct-memory blocks, so the test checks the saved pages relative to the state after the unmap instead of exact block counts. - PartiallyResidentSnapshot large-tracked (boykopovar#6982): our snapshots keep block hashes, so a driver store of the same bytes re-reads and compares the surface and may keep the texture; the test checks the re-read. AI-assisted: yes
What
This PR is part of a series of graphics and memory-management performance improvements for AnyPS5, with SuperTuxKart as the main profiling workload. This is item 09/10; the UI Lab startup/cache fix follows as a separate eleventh PR. Each topic's measurements are distinguished from the combined result.
Reuse same-sized saved direct-memory buffers only after their previous owner releases them. Bound retained buffers to 32 MiB and preserve live saved contents under pressure. The pool can retain up to 32 MiB of committed host pages after their previous owners release them. Cached commitment is reused with each idle buffer and evicted under budget pressure. This is a bounded speed/memory tradeoff, not a reduction in memory usage. Live saved contents remain excluded from reuse.
Tested
Pinned benchmark base:
6447ed1ed7fd11c7eef60df336e8d66fac271188. Tested topic snapshot:1d9bf1e0299ebf986cef2333bc9ca011ef3276aa. Windows 11 Pro build 26300, Ryzen 7 5800H, RTX 3070 Laptop, NVIDIA 617.42. Frozen runtime module hashes and raw traces were verified. No opt-in optimization flags are required.3/3 memory/write-watch cases passed. The ownership test also passes on main. Retain and verify 18 x 2 MiB of live nonzero saved buffers while performing 24 temporary unmap/remap/release cycles before owner release and 24 after it. Check new allocations for zero contents and remapped data for stale or corrupted bytes.
SuperTuxKart captures for the bot's before/after requirement: Windows 11 Pro build 26300, Ryzen 7 5800H, RTX 3070 Laptop GPU, NVIDIA 617.42, WinLibs GCC 15.2.0 r7, Release; SuperTuxKart (PPSA69421) tutorial prompt over the main menu, 1920x1080 internal rendering in a maximized 2560x1369 window, warm shader caches, audio muted, no input;
APS5_TRACE_SECONDS=20:8withAPS5_TRACE_EVENTS=1048576, console output not redirected, no profiling switches; 2026-10-21, captures in the order main, (#7470), #7472, main, order main, (#7470), PR, main.rv21-a3-main-64476447ed1ed7fd6447ed1ed7fdrv21-a6-main-64476447ed1ed7fd6447ed1ed7fdrv21-a5-74724ba87c6b28beThe FPS difference is within the run-to-run spread of main; this PR claims no FPS change. What it saves is shown under Where it helps.
Summary lines (main first, then this PR)
{"commit":"6447ed1ed7fd","dirty":false,"env":{"APS5_TRACE_EVENTS":"1048576","APS5_TRACE_SECONDS":"20:8"},"gpu":"NVIDIA GeForce RTX 3070 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26300","resolution":"1920x1080","window":{"spec":"seconds 20:8","first_present":39,"ms":8042.799},"presents":77,"frame_ms":{"p50":103.957,"p90":107.464,"p99":109.753,"max":109.753},"gpu_busy_ms_per_present":5.263,"worker_busy_pct":{"queue worker 0x0":2.995},"top_phases_ms_per_frame":{"Driver.Packet":3.081,"Driver.Packet.draw":2.485,"Driver.Draw":2.431,"Driver.Draw.shader_compile_and_link":1.442,"Driver.Present":0.839,"Shader.DrawStage":0.759,"Driver.Present.queue_present":0.584,"Vulkan.QueuePresent":0.583},"trace_dropped":false,"trace":"aps5-trace-6447ed1ed7fd-3204.json.gz"} {"commit":"6447ed1ed7fd","dirty":false,"env":{"APS5_TRACE_EVENTS":"1048576","APS5_TRACE_SECONDS":"20:8"},"gpu":"NVIDIA GeForce RTX 3070 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26300","resolution":"1920x1080","window":{"spec":"seconds 20:8","first_present":40,"ms":8001.567},"presents":77,"frame_ms":{"p50":103.326,"p90":106.501,"p99":111.961,"max":111.961},"gpu_busy_ms_per_present":5.280,"worker_busy_pct":{"queue worker 0x0":2.974},"top_phases_ms_per_frame":{"Driver.Packet":3.045,"Driver.Packet.draw":2.466,"Driver.Draw":2.411,"Driver.Draw.shader_compile_and_link":1.424,"Driver.Present":0.848,"Shader.DrawStage":0.773,"Driver.Present.queue_present":0.598,"Vulkan.QueuePresent":0.596},"trace_dropped":false,"trace":"aps5-trace-6447ed1ed7fd-52092.json.gz"} {"commit":"4ba87c6b28be","dirty":false,"env":{"APS5_TRACE_EVENTS":"1048576","APS5_TRACE_SECONDS":"20:8"},"gpu":"NVIDIA GeForce RTX 3070 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26300","resolution":"1920x1080","window":{"spec":"seconds 20:8","first_present":38,"ms":8082.680},"presents":79,"frame_ms":{"p50":102.035,"p90":104.597,"p99":113.546,"max":113.546},"gpu_busy_ms_per_present":5.259,"worker_busy_pct":{"queue worker 0x0":2.869},"top_phases_ms_per_frame":{"Driver.Packet":2.892,"Driver.Packet.draw":2.343,"Driver.Draw":2.297,"Driver.Draw.shader_compile_and_link":1.367,"Driver.Present":0.817,"Shader.DrawStage":0.738,"Driver.Present.queue_present":0.572,"Vulkan.QueuePresent":0.570},"trace_dropped":false,"trace":"aps5-trace-4ba87c6b28be-20300.json.gz"}tools/performance_compare.py(first capture of each side)Performance comparison
Configuration differences
Frame and GPU times (ms)
Top phases (ms per frame)
tools/trace_digest.pyoutput (first capture of each side; all digests are in the evidence archive)rv21-a3-main-6447seconds 20:8 from present 39: 77 presents, frame p50 104.0 / p90 107.5 / p99 109.8 / max 109.8 ms, GPU busy 5.3 ms per present
NVIDIA GeForce RTX 3070 Laptop GPU, NVIDIA 617.42, Windows 10.0.26300, 1920x1080, commit 6447ed1, dropped: no
{"commit":"6447ed1ed7fd","dirty":false,"env":{"APS5_TRACE_EVENTS":"1048576","APS5_TRACE_SECONDS":"20:8"},"gpu":"NVIDIA GeForce RTX 3070 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26300","resolution":"1920x1080","window":{"spec":"seconds 20:8","first_present":39,"ms":8042.799},"presents":77,"frame_ms":{"p50":103.957,"p90":107.464,"p99":109.753,"max":109.753},"gpu_busy_ms_per_present":5.263,"worker_busy_pct":{"queue worker 0x0":2.995},"top_phases_ms_per_frame":{"Driver.Packet":3.081,"Driver.Packet.draw":2.485,"Driver.Draw":2.431,"Driver.Draw.shader_compile_and_link":1.442,"Driver.Present":0.839,"Shader.DrawStage":0.759,"Driver.Present.queue_present":0.584,"Vulkan.QueuePresent":0.583},"trace_dropped":false,"trace":"aps5-trace-6447ed1ed7fd-3204.json.gz"}Slowest frames (largest slices inside each, inclusive)
Threads (ms per frame)
rv21-a5-7472seconds 20:8 from present 38: 79 presents, frame p50 102.0 / p90 104.6 / p99 113.5 / max 113.5 ms, GPU busy 5.3 ms per present
NVIDIA GeForce RTX 3070 Laptop GPU, NVIDIA 617.42, Windows 10.0.26300, 1920x1080, commit 4ba87c6, dropped: no
{"commit":"4ba87c6b28be","dirty":false,"env":{"APS5_TRACE_EVENTS":"1048576","APS5_TRACE_SECONDS":"20:8"},"gpu":"NVIDIA GeForce RTX 3070 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26300","resolution":"1920x1080","window":{"spec":"seconds 20:8","first_present":38,"ms":8082.68},"presents":79,"frame_ms":{"p50":102.035,"p90":104.597,"p99":113.546,"max":113.546},"gpu_busy_ms_per_present":5.259,"worker_busy_pct":{"queue worker 0x0":2.869},"top_phases_ms_per_frame":{"Driver.Packet":2.892,"Driver.Packet.draw":2.343,"Driver.Draw":2.297,"Driver.Draw.shader_compile_and_link":1.367,"Driver.Present":0.817,"Shader.DrawStage":0.738,"Driver.Present.queue_present":0.572,"Vulkan.QueuePresent":0.57},"trace_dropped":false,"trace":"aps5-trace-4ba87c6b28be-20300.json.gz"}Slowest frames (largest slices inside each, inclusive)
Threads (ms per frame)
Where it helps
Every unmap of a resident 64-KiB block saves its contents (
WindowsMappings::evict), about 48 times per frame. On main each save reserves and commits fresh saved memory; with this PR a released saved buffer of the same size is reused.2185dbf92eWindowsMappings::evict (save on unmap)calls per frameWindowsMappings::evict (save on unmap)µs per callWindowsMappings::evict (save on unmap)ms per frameCounters come from a local probe build that is not part of this PR (
review-probe.patchin the evidence archive): the same counters and timers were inserted into main2185dbf92eand into main with all eleven series PRs merged (#7464 in its rebased form), anchored on function definitions present in both trees. Runs were in the mirrored order main, series with #7464 reduced to its wake only, series, series, reduced series, main, in the scene above withAPS5_PROFILE_DRAW=1; values are medians of two runs over the same 8-second window as the trace, starting 20 s after the first flip, per flip. These runs carry profiling overhead and are not used for FPS. Each optimised function below is changed by this PR only; counters show where the work goes, not an isolated FPS gain.Other homebrew checks
UI Lab: the 21 default concepts were rendered and visually reviewed in a navigation cycle for this topic. Artwork was present; this covers default navigation, not all themes or every widget interaction.
Known baseline rendering issue: vkQuake still has intermittent flickering and an intermittently black floor on main and the tested performance branches. These changes do not fix it. Yamagi Quake II gameplay stops at unsupported last-provoking-vertex flat interpolation, and UZDoom at unsupported clip/cull-distance handling on the baseline; neither supplies a valid gameplay FPS comparison.
Dependencies, series and overlap
#6650 caches AGC small host-heap blocks per thread. This topic reuses libc saved direct-memory backing by size/ownership under a 32-MiB limit; it is a different pool and has no dependency on #6650.
The project overlap scan compared the local topics against 785 open PRs and checked merges into main
e5c2b3c325e485ba7fd417c47c46f48aa2c89115. This topic merges cleanly in that snapshot. Some other PRs already conflict with main, so the tool cannot compare all their shared-file hunks; the full report is retained with the common series evidence package. No exact duplicate was identified in the inspected implementations. This is not a certification that every conflicting foreign branch is composition-tested.Conventions and whitespace checks passed for the tested topic range. The benchmark base remains pinned for comparability; this is not a fresh-current-main build/test claim. Measurements and focused tests refer to the pinned source revisions above; no full-suite CI run is claimed.
Re-checked on 2026-10-21: this head still merges cleanly into main
2185dbf92e. #6658, which the earlier combined runs carried, merged into main on 2026-10-20.Combined series result
Re-measured on 2026-10-21 on current main
2185dbf92e(Windows 11 Pro build 26300, Ryzen 7 5800H, RTX 3070 Laptop GPU, NVIDIA 617.42, WinLibs GCC 15.2.0 r7, Release; SuperTuxKart (PPSA69421) tutorial prompt over the main menu, 1920x1080 internal rendering in a maximized 2560x1369 window, warm shader caches, audio muted, no input;APS5_TRACE_SECONDS=20:8withAPS5_TRACE_EVENTS=1048576, console output not redirected, no profiling switches). Runs in the order A-B-C-D-D-C-B-A, two per configuration; FPS is presents over the 8-s window.2185dbf92e#7464 and #7465 make the largest step together: the title's per-frame host imports disappear (see their PRs). With #7464 reduced to its CPU-label wake only, the whole series measured 10.07 / 9.01 FPS against main 9.24 / 9.30 in a separate block of the same session. Combined numbers are not the gain of any single PR. The combined builds are local merges of the published (#7465-#7470, #7472, #7474) and rebased (#7464, #7471, #7473) heads into main
2185dbf92e; the commits in their summary lines are those merges, not pushed. Summary lines, traces and digests: series-evidence.zip.Checklist
6447ed1ed7fd; merges cleanly into current main2185dbf92e(git merge-tree, 2026-10-21); related overlaps are listed aboveNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)The published head adds publication metadata only; its complete source tree is identical to the tested snapshot above.
Series index
These are the ten performance topics from the same profiling effort, followed by a separate UI Lab cache-preservation fix. Membership in the series does not imply a dependency.
Reproduction evidence
Series traces, summary lines, module hashes, focused-test logs and UI contact sheets. Game measurements use the pinned base and source revisions described above; assets contain no game binaries.
Update of 2026-10-21: 7472-evidence.zip (traces, summary lines,
trace_digest.pyandperformance_compare.pyoutput, loaded-module hashes), counters where.json and the probe review-probe.patch with probe_window.py.