Repository navigation
perf(libc): size each write-watch query to the region it covers - #4875
Conversation
a643768 to
b803cbb
Compare
WindowsMappings::Collect handed GetWriteWatch the whole remaining output capacity for every private region it walked. The call's cost follows that capacity, not the size of the range: on Windows 11 (26200) a query of one 16 KiB region took 0.35 us with room for 4 entries and 6.5 us with room for the 65536 the write tracker passes. Flexible memory is committed in 16 KiB allocations, one region each, so a resetting walk over 33 MiB of it took 14.7 ms, 5.8 times a memcmp of the same bytes. Each query now gets room for the pages its region spans, which is the most it can report, so every walk returns the same pages; the 33 MiB walk takes 1.23 ms. guest_memory checks that a walk over eight 16 KiB allocations reports every stored page once, in order, with room for 32, 5 and 1 pages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
b803cbb to
44767a7
Compare
|
Measured this while profiling PPSA01317 on Windows 11 (RTX 5090 Laptop). The cost of
In game, on a local branch with the same per-segment cap in the collect loop:
That branch also calls On the count itself: rounding both ends to pages, as this PR does, matters. The simpler |
|
Thank you, that's the in-title measurement this PR was missing: per-call cost following the output capacity (0.29 µs at 49 entries vs 7.10 µs at 65,536), and the GT7 race scene's walk time falling from 2.5-3.6 s to 0.1-0.4 s per 10 s. The page count is computed as you describe: both ends are rounded to pages, so a 4 KiB range starting mid-page counts the two pages it touches, where |
…e range's pages (as upstream boykopovar#4875)
What
WindowsMappings::Collectwalks a range oneVirtualQueryregion at a time and gives eachGetWriteWatchcall the whole remaining output capacity, up to the tracker's 65536 entries. The call's cost follows that capacity, not the size of the range it walks. Flexible memory is committed in 16 KiB allocations, one region each, so a collect over it pays the full-capacity price once per 16 KiB.Each query now gets room for at most the pages its region spans, which is the most it can report. The pages returned, their order and the overflow handling when the shared buffer fills are unchanged: the capacity is still capped by what is left in it. The span counts every 4 KiB page the walked part of the region touches, so a walk that starts inside a page still has room for that page.
This is the
GetWriteWatchbuffer sizing from DotDebian's #2863, split out as oneandonlydean and adriadam10 asked in that PR's discussion and review. #2863 carries it inside its queue-worker prewalk change and conflicts withmain; once this lands, #2863 can drop its copy. #2863's formula,(stop - cursor + 4095) / 4096, is one page short when the walk starts inside a page and ends inside the next one; this version rounds both ends. Other open PRs that touchWindowsMappings.hpp(#4661, #4535, #4367) merge cleanly with it.Numbers on Windows 11 26200, i9-14900KF, median of 2000 calls or 31 rounds (other build jobs were running, load average about 21):
GetWriteWatchon a clean 16 KiB allocationmain(remaining capacity)memcmpof the same bytesThe benchmark is standalone and reproduces the region walk of
Collectoutside the repository:clamp_bench.cpp
No title trace: I have no PS5 or game files. Which titles keep most of their write-watched memory in 16 KiB flexible-memory allocations is not measured here; the saving applies to every collect over such memory.
Tested
Windows 11 Pro 26200, i9-14900KF, RTX 5080 (NVIDIA 616.92), WinLibs GCC 15.2.0, Release with tests:
CheckSplitPrivateCollectinguest_memory: a resetting collect over eight 16 KiB allocations reports every stored page exactly once and in order with an output capacity of 32, 5 and 1, then nothing until the next store; a single store in the fourth allocation reports only that page. It passes withmain'sCollectand with this change, which is the point: the change must not alter what a collect returns.guest_memory: 30 of 30 runs (ctest --repeat until-fail:30).ctest(ANYPS5_REQUIRE_VULKAN=1,--parallel 4) on this commit: 1021 tests, 0 failed.WindowsMappings.hppand the new check are Windows only.tools/check_conventions.py --base origin/mainwith this title and body: passed.main2f969f1 together with perf(agc): compare Windows direct imports only after a write-watch mark #4878 (no conflicts in this PR's files): native fullctestwith both changes, 1064 tests, 0 failed;guest_memory10 of 10 runs. On cac4e96 the only failure waspatched_libraries, which failed the same way on unmodifiedmainthen (feat(libkernel_sys): implement renameat #4877; fixed by fix(libkernel_sys): reach rename through a _nid_no_patch export #5025).In a title, measured by @gjonn on Windows 11 with an RTX 5090 Laptop GPU while profiling Gran Turismo 7 (PPSA01317), on a local branch with the same per-segment cap (comment):
GetWriteWatchcalls went from about 8 µs to about 0.5 µs on average.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