Repository navigation
perf(agc): return freed memory when shader preparation drains - #5777
Merged
adriadam10 merged 1 commit intoOct 14, 2026
Merged
Conversation
Ghost of Tsushima (PPSA02225) ends its first run, with no resource plans in the shader cache, with about 4 GB more resident memory than a run with a warm cache. The threads of the registration prepare pool free what they allocate to prepare a shader, but glibc keeps the freed memory in their arenas: at 110 s, 4.6 GB of the 6.5 GB the arenas held was free. When the pool has no task left, the worker that finished last calls malloc_trim, at most once a second, which gives the free pages of every arena back to the system. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
9 tasks done
|
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.
|
10 tasks done
Collaborator
|
Checked the diff (one |
10 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Ghost of Tsushima (PPSA02225) ends its first run, with no resource plans in the shader cache, at about 13.3 GB resident, against about 9.0 GB once the cache holds them. The game is at the same point in both runs (the same 6.5 GB of guest memory resident), so the difference is host memory.
It is memory the registration prepare pool has already freed. On a first run the pool prepares the title's shaders on its threads, and each thread allocates from its own glibc arena.
/proc/<pid>/smapsnear the end of a first run shows 5.7 GB of anonymous memory in those arenas, against 1.9 GB in a warm run. Amalloc_infotaken at 110 s from inside the process counts 4.6 GB of the 6.5 GB the arenas hold as free, and amalloc_trim(0)at that point takes the process from 13.6 GB to 9.5 GB resident. glibc returns freed memory from the middle of an arena only onmalloc_trim, so without it the memory stays resident for the rest of the run.When the pool has no task left, the worker that finished last now calls
malloc_trim(0), at most once a second. That gives the free pages of every arena back to the system. Only glibc builds call it (__GLIBC__).The peak is unchanged: the first preparation burst does not drain the pool until it ends, so the arenas still reach their size before the first trim. I also tried trimming every 2 s while the pool is busy. That lowered the peak by about 1 GB (13.2 to 12.4 GB), but it took 2.8 s of trimming in a 150 s run with single trims up to 202 ms, so it is not part of this PR.
Tested
Ubuntu 24.04, GCC 13.3 (CI's compiler), glibc 2.39, Release with CI's configure flags. Measured on
main31bfc4e, then rebased ontomaindaf256b:Ghost of Tsushima (PPSA02225), relinked with
--to-inteland run on Linux (RTX 4070 Ti, NVIDIA 580.142) for 150 s with a warm Vulkan pipeline cache but no resource plans. The runs differ only inlibSceAgcDriver.prx; resident memory sampled every second:mainA warm
mainrun ends at 8,946 MB. After the rebase, a first run with this PR ends at 8,960 MB (peak 13,008 MB).With temporary timing prints (not part of this PR), a 150 s first run trimmed 5 times, 456 ms in all, the longest trim 243 ms. Each trim runs on the worker that drained the pool, and holds each arena's lock while it trims that arena, so a thread that allocates from the same arena meanwhile waits. Without the one-second limit the pool trimmed 37 times in such a run.
Some first runs on this machine stop with a glibc heap check (
corrupted size vs. prev_size,free(): invalid next size (fast),double free or corruption (!prev)), with and without this change: 3 of 22 onmainand 4 of 28 with this PR, and in 19 interleaved pairs 3 onmainand 1 with this PR. The rate is the same with and without this change, and they look like this machine's memory rather than code:memtesterinside the VM reports a word whose bit 6 reads wrong (0xffffff5fffffffbf != 0xffffff5fffffffff), and in each of the four cores I examined the damaged heap word differs from its right value in that same bit.All 514 tests in
libSceAgcDriverpass onmaindaf256b.agc_driver_stencil_ref_exportis skipped because the device lacksVK_EXT_shader_stencil_export, andagc_host_import_sparseandagc_driver_sparse_dma_buf_importare skipped because/dev/udmabufis not accessible here.A MinGW-w64 GCC 13
-fsyntax-onlycompile ofShaderRegistry.cpphas no errors.tools/check_conventions.py --base upstream/mainpasses.tools/pr_overlap.py --branchreports fix(agc): wait for queued registration preparations before accepting a submission #5007, which changes the same worker loop to record finished tasks and to notifyidleafter every task. The two conflict only in text: whichever merges second keeps its owndrainedcheck for the trim.Not checked on Windows (MinGW does not define
__GLIBC__, so nothing changes there), on another Linux libc or on a console.Checklist
main; no other open PR implements the same functionsNotImplemented_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)🤖 Generated with Claude Code