Repository navigation
perf(libc): avoid repeated Windows path component scans - #7467
Conversation
Complete the publication checklist and series cross-links for boykopovar#7467. No source or test changes.
|
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.
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.
|
|
I ran a differential test of the old and new In this example, directory
The cause: inside a case-sensitive directory, Either of these would keep the old behaviour:
The speedup is real for misses: a missing name in a 3,000-entry directory takes 14.8–17.3 ms on AI-assisted: yes (Claude Code); results above were run on this machine. |
|
@wachi35676 Thanks for the differential test and the case-sensitive directory reproducer. Fixed in The three corrections (#7467, #7469 and #7470) were also checked together against the previous series in a same-scene SuperTuxKart A-B-B-A repeat: 32.680 -> 32.722 FPS (1920x1080 internal rendering, maximized 2560x1369 client, warm caches, trace seconds 20-28, RTX 3070 Laptop/NVIDIA 617.42). Focus and loaded-module hashes were verified. This combined check does not assign a separate FPS gain to this PR. #7473 is being validated separately. |
|
Rebase handoff, with a port onto Verified on Linux (CachyOS, kernel 7.2.9, GCC 16.2.1, Ninja, Release, To pick it up: Head is |
Complete the publication checklist and series cross-links for boykopovar#7467. No source or test changes.
feed2ea to
5a1590c
Compare
|
Thanks for porting this to main's split-name interface and for the Linux validation. Picked up the handoff and rebased onto main e1dae23. Windows validation caught one iterator error in the port: after selecting the existing parent, first = last skipped the final path component. Changed it to first = std::prev(last). The existing guest_filesystem parent-traversal case fails before this correction and passes after rebuilding with it; the earlier case-sensitive NTFS and relinked-module regression coverage is retained. Published head: 5a1590c. The PR description distinguishes the completed filesystem checks from the incomplete game repeat. Negative-control and fixed-run logs, source provenance and runtime manifests: https://github.com/Dripl1x/AnyPS5/releases/download/performance-series-20261018/review-rebase-20261018.zip |
|
This pull request is labeled |
|
Re-checked
AI-assisted: yes (Claude Code); results above were run on this machine. |
|
Updated the description: before/after summary lines from the same machine and SuperTuxKart scene (main AI-assisted: yes (Claude Code) |
What
Avoid repeated case-insensitive directory scans while resolving Windows guest paths. Use
FindFirstFileExWonly when the directory explicitly reports case-insensitive behaviour, return existing non-reparse paths directly, and start component matching at the longest existing ancestor. Preserve enumeration for case-sensitive directories and failed or unsupported attribute queries.Tested
Rebased onto main
e1dae23e01a412571c2575fe794475b4c0e7adf9; source head5a1590c9f8eefc50d7080f26fae3b8ed07ff99bc. Merge compatibility was also checked against later main5057673804b28d217559010d7ac1f702824f240b. Thanks to @blawesom for porting the prefix walk to main's split-nameResolveHostPathinterface and reporting Linux coverage.The port initially advanced
firsttolastafter selecting the existing parent, skipping the final component. The Windowsguest_filesystemtest reproduced the failure at parent traversal before the fix. Changing the iterator tostd::prev(last)preserves the final component; rebuilding and rerunning the same test passes. The regression coverage for mixed-case file/directory lookup, relinked module names and ambiguousBoth/bothnames on NTFS case-sensitive directories is retained and passes. The libc runtime was rebuilt from the corrected source. @blawesom's Linux results in the review are credited as their validation, not a fresh local full-suite run.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, A-B-B-A, order main, PR, PR, main.rv21-t01-main-e1e1dae23e01a4e1dae23e01a4rv21-t04-main-e1e1dae23e01a4e1dae23e01a4rv21-t02-74675a1590c9f8eerv21-t03-74675a1590c9f8eeThe 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":"e1dae23e01a4","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":37,"ms":8035.601},"presents":76,"frame_ms":{"p50":105.687,"p90":107.866,"p99":110.766,"max":110.766},"gpu_busy_ms_per_present":5.418,"worker_busy_pct":{"queue worker 0x0":2.921},"top_phases_ms_per_frame":{"Driver.Packet":3.040,"Driver.Packet.draw":2.452,"Driver.Draw":2.383,"Driver.Draw.shader_compile_and_link":1.380,"VideoOut.Flip":0.975,"Driver.Present":0.881,"VideoOut.Flip.present":0.867,"Shader.DrawStage":0.688},"trace_dropped":false,"trace":"aps5-trace-e1dae23e01a4-46916.json.gz"} {"commit":"e1dae23e01a4","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":35,"ms":8054.635},"presents":75,"frame_ms":{"p50":105.981,"p90":112.071,"p99":115.274,"max":115.274},"gpu_busy_ms_per_present":5.406,"worker_busy_pct":{"queue worker 0x0":3.188},"top_phases_ms_per_frame":{"Driver.Packet":3.371,"Driver.Packet.draw":2.739,"Driver.Draw":2.674,"Driver.Draw.shader_compile_and_link":1.585,"VideoOut.Flip":0.985,"Driver.Present":0.877,"VideoOut.Flip.present":0.865,"Shader.DrawStage":0.808},"trace_dropped":false,"trace":"aps5-trace-e1dae23e01a4-48816.json.gz"} {"commit":"5a1590c9f8ee","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":67,"ms":8042.332},"presents":76,"frame_ms":{"p50":104.714,"p90":107.963,"p99":123.174,"max":123.174},"gpu_busy_ms_per_present":5.423,"worker_busy_pct":{"queue worker 0x0":2.975},"top_phases_ms_per_frame":{"Driver.Packet":3.103,"Driver.Packet.draw":2.478,"Driver.Draw":2.419,"Driver.Draw.shader_compile_and_link":1.383,"VideoOut.Flip":0.966,"Driver.Present":0.869,"VideoOut.Flip.present":0.856,"Shader.DrawStage":0.699},"trace_dropped":false,"trace":"aps5-trace-5a1590c9f8ee-31600.json.gz"} {"commit":"5a1590c9f8ee","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":64,"ms":8081.557},"presents":76,"frame_ms":{"p50":105.769,"p90":109.726,"p99":118.382,"max":118.382},"gpu_busy_ms_per_present":5.481,"worker_busy_pct":{"queue worker 0x0":3.024},"top_phases_ms_per_frame":{"Driver.Packet":3.169,"Driver.Packet.draw":2.576,"Driver.Draw":2.504,"Driver.Draw.shader_compile_and_link":1.447,"VideoOut.Flip":0.984,"Driver.Present":0.876,"VideoOut.Flip.present":0.865,"Shader.DrawStage":0.730},"trace_dropped":false,"trace":"aps5-trace-5a1590c9f8ee-50844.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-t01-main-e1seconds 20:8 from present 37: 76 presents, frame p50 105.7 / p90 107.9 / p99 110.8 / max 110.8 ms, GPU busy 5.4 ms per present
NVIDIA GeForce RTX 3070 Laptop GPU, NVIDIA 617.42, Windows 10.0.26300, 1920x1080, commit e1dae23, dropped: no
{"commit":"e1dae23e01a4","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":37,"ms":8035.601},"presents":76,"frame_ms":{"p50":105.687,"p90":107.866,"p99":110.766,"max":110.766},"gpu_busy_ms_per_present":5.418,"worker_busy_pct":{"queue worker 0x0":2.921},"top_phases_ms_per_frame":{"Driver.Packet":3.04,"Driver.Packet.draw":2.452,"Driver.Draw":2.383,"Driver.Draw.shader_compile_and_link":1.38,"VideoOut.Flip":0.975,"Driver.Present":0.881,"VideoOut.Flip.present":0.867,"Shader.DrawStage":0.688},"trace_dropped":false,"trace":"aps5-trace-e1dae23e01a4-46916.json.gz"}Slowest frames (largest slices inside each, inclusive)
Threads (ms per frame)
rv21-t02-7467seconds 20:8 from present 67: 76 presents, frame p50 104.7 / p90 108.0 / p99 123.2 / max 123.2 ms, GPU busy 5.4 ms per present
NVIDIA GeForce RTX 3070 Laptop GPU, NVIDIA 617.42, Windows 10.0.26300, 1920x1080, commit 5a1590c, dropped: no
{"commit":"5a1590c9f8ee","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":67,"ms":8042.332},"presents":76,"frame_ms":{"p50":104.714,"p90":107.963,"p99":123.174,"max":123.174},"gpu_busy_ms_per_present":5.423,"worker_busy_pct":{"queue worker 0x0":2.975},"top_phases_ms_per_frame":{"Driver.Packet":3.103,"Driver.Packet.draw":2.478,"Driver.Draw":2.419,"Driver.Draw.shader_compile_and_link":1.383,"VideoOut.Flip":0.966,"Driver.Present":0.869,"VideoOut.Flip.present":0.856,"Shader.DrawStage":0.699},"trace_dropped":false,"trace":"aps5-trace-5a1590c9f8ee-31600.json.gz"}Slowest frames (largest slices inside each, inclusive)
Threads (ms per frame)
The earlier seven filesystem cases and UI Lab/homebrew navigation evidence predate this rebase. No startup-duration improvement is claimed. vkQuake's known baseline flicker/black-floor issue remains outside this change.
Where it helps
Path resolution happens mostly while the title starts and loads. Over the whole run the call counts are identical on both sides, so totals compare directly (CPU time summed over the calling threads, not wall-clock startup time):
ResolveHostPathMatchNamesCounters 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.Dependencies and overlap
No required PR dependency. Main's current vector-based path interface is preserved. The earlier review correction for case-sensitive directories remains in place; #6061 and #6226 were previously identified as related path-resolution overlaps and are not claimed as this implementation.
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.Checklist
Reproduction evidence
Windows negative-control/fixed test logs, source provenance, frozen runtime manifests and completed/incomplete game-run records. Earlier series evidence is historical and tied to its original revisions. Current hosted checks are shown in the PR checks; no full-suite result is inferred from metadata checks.
Update of 2026-10-21: 7467-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.