Repository navigation
perf(agc): read committed guest pages directly on Windows - #14799
Conversation
MarcZX10
left a comment
There was a problem hiding this comment.
Reviewed — changes requested
The performance report is incomplete because the two required capture trace files are not attached to this PR.
What I ran
Reviewed head 63dce0027a2bea23047c800148ab20eaafe74c8c against base f2d874c1fdccf781c0fa5d86619bb5dd8894b958. The stack check found a clean merge preview. Environment: Linux x86_64, Python 3.11.2, GCC 12.2.0. I did not build or compile the Windows changes.
git diff --check f2d874c1fdccf781c0fa5d86619bb5dd8894b958...63dce0027a2bea23047c800148ab20eaafe74c8c
python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 14799
python3 tools/pr_abi_audit.py --repo /marc/AnyPS5 --base f2d874c1fdccf781c0fa5d86619bb5dd8894b958 --head 63dce0027a2bea23047c800148ab20eaafe74c8c --json
python3 tools/pr_test_quality.py --root /marc/AnyPS5 --json core/libs/prx/libSceAgcDriver/tests/execution/CommittedReadMapping.cpp core/libs/tests/WindowsBacktraceReport.cpp
python3 tools/pr_stack_check.py 14799 --repo boykopovar/AnyPS5Results
| Check | Result | Note |
|---|---|---|
| Patch whitespace | passed | No diff errors. |
| Conventions and template | passed | Local repository check passed. |
| ABI source audit | passed | No findings; this is a lexical check only. |
| Test registration heuristic | passed after inspection | Heuristic flagged the CMake glob; both test paths are picked up by it. |
| Merge preview | passed | Clean against the PR base. |
| Windows runtime tests | not run | They require a Windows build and runtime. |
| Hosted CI | not run to completion | Required jobs were queued; earlier conventions runs were cancelled. |
| Performance trace artifacts | failed | Summary and digest text are present, but the two raw traces are absent. |
Unedited local check output
git diff --check:
Conventions:
All conventions checks passed.
Precheck:
/tmp/anyps5-pr-precheck/boykopovar-AnyPS5-pr-14799-63dce0027a2b/report.md
/tmp/anyps5-pr-precheck/boykopovar-AnyPS5-pr-14799-63dce0027a2b/report.json
Conclusion: checks failed
The precheck's two helper launches failed because those local review-helper scripts are not part of the isolated PR checkout; I ran the ABI audit from the main workspace successfully. The test-quality warning was resolved by inspecting the CMake file(GLOB ... tests/execution/*.cpp) registration and the test target.
ABI audit:
{
"status": "passed",
"findings": [],
"limitations": [
"This lexical audit does not parse C++ fully, prove calling-convention or binary ABI correctness, verify runtime version tables, or replace compilation.",
"An absent source declaration or explicit alias can be intentional for assembly exports, conditional platform code, or generated interfaces; inspect findings before treating them as defects."
]
}
Test-quality heuristic:
{
"findings": [
{
"path": "core/libs/prx/libSceAgcDriver/tests/execution/CommittedReadMapping.cpp",
"line": 1,
"kind": "possible-unregistered-cpp-test",
"message": "Changed C++ test source path was not found literally in CMake files; inspect registration, generated source lists, and workflow coverage."
},
{
"path": "core/libs/tests/WindowsBacktraceReport.cpp",
"line": 1,
"kind": "possible-unregistered-cpp-test",
"message": "Changed C++ test source path was not found literally in CMake files; inspect registration, generated source lists, and workflow coverage."
}
],
"negative_control_candidates": [],
"limits": [
"Heuristics can miss weak assertions and can flag valid tests; inspect every finding.",
"A test that passes against base is not by itself proof that the PR test is inadequate.",
"Negative controls are only candidates until the test actually runs against base and fails by assertion rather than import/setup error."
]
}
Stack check:
# PR stack check — fresh
Target: https://github.com/boykopovar/AnyPS5/pull/14799
Target branch tip: `f2d874c1fdccf781c0fa5d86619bb5dd8894b958`
All heads unchanged: **True**
Dependency order: #14799
## Individual merge previews
- PR #14799: **clean** against current base; head `63dce0027a2bea23047c800148ab20eaafe74c8c`; changed paths: none reported
## Dependency relationships
No declared PR dependencies were found in the target PR chain.
## Limits
Merge previews apply each exact PR head independently to the current base. A dependency head that is absent from its dependent is reported separately from merge conflicts; file overlap is only a signal and does not prove a conflict. This tool does not build, resolve conflicts, publish comments, or update branches.
Negative control
Not run. The tests are Windows-specific and their exact-head binaries are unavailable without building.
Findings
Blocking
- The
perfreport says the two.json.gztraces “are not attached yet,” and no later PR comment contains them. The PR template requires the baseline and change traces, while the performance report form marks the trace attachments as required. Please attach the exact baseline and candidate traces so the captures and pasted digests can be checked against the reported commits.
Non-blocking
- None found in the source review. The no-build checks do not establish that the Windows code compiles or that the reported runtime measurements reproduce.
Sources
- PR template at this head requires traces for performance changes.
- Performance report form at this head marks trace attachments required and requests the digest output.
Not verified
- Windows compilation and CTest, Linux behavior, console behavior, and independent reproduction of the GPU performance data were not run. The hosted checks were still queued or cancelled at the final check.
- Negative controls were not run.
To get this to a good state
Attach both capture trace files. I will check that they match the supplied summary lines and rerun the same no-build checks on the updated head.
|
Traces and raw files attached:
The trace files carry no commit, so AI-assisted: yes (Claude Code) |
|
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.
|
MarcZX10
left a comment
There was a problem hiding this comment.
Re-checked — traces verified, CI pending
The new evidence supplies both capture traces and reproducible digests; six macOS jobs remain pending, so I am holding approval.
What I ran
Re-checked the new evidence for unchanged PR head 63dce0027a2bea23047c800148ab20eaafe74c8c against base f2d874c1fdccf781c0fa5d86619bb5dd8894b958. Environment: Linux x86_64, Python 3.11.2; no compiler or build was used. The earlier source review still applies because the PR head is unchanged.
curl -fL --silent --show-error 'https://github.com/user-attachments/files/34245625/aps5-trace-a6111d1f071c-31820.json.gz' -o /tmp/pr14799-baseline.json.gz
curl -fL --silent --show-error 'https://github.com/user-attachments/files/34245642/aps5-trace-ee8448fc2ee5-29424.json.gz' -o /tmp/pr14799-candidate.json.gz
curl -fL --silent --show-error 'https://github.com/user-attachments/files/34245605/pr14799-evidence-v2.zip' -o /tmp/pr14799-evidence-v2.zip
gzip -t /tmp/pr14799-baseline.json.gz /tmp/pr14799-candidate.json.gz
unzip -t /tmp/pr14799-evidence-v2.zip | tail -1
unzip -oq /tmp/pr14799-evidence-v2.zip -d /tmp/pr14799-evidence
sha256sum /tmp/pr14799-baseline.json.gz /tmp/pr14799-evidence/aps5-trace-a6111d1f071c-31820.json.gz /tmp/pr14799-candidate.json.gz /tmp/pr14799-evidence/aps5-trace-ee8448fc2ee5-29424.json.gz
python3 tools/trace_digest.py /tmp/pr14799-evidence/aps5-trace-a6111d1f071c-31820.json.gz --summary /tmp/pr14799-evidence/cap-main.jsonl > /tmp/pr14799-run-main.txt
python3 tools/trace_digest.py /tmp/pr14799-evidence/aps5-trace-ee8448fc2ee5-29424.json.gz --summary /tmp/pr14799-evidence/cap-fix.jsonl > /tmp/pr14799-run-fix.txt
head -2 /tmp/pr14799-run-main.txt
head -2 /tmp/pr14799-run-fix.txt
diff -q --strip-trailing-cr /tmp/pr14799-evidence/digest-M3T-rl5.txt /tmp/pr14799-run-main.txt
diff -q --strip-trailing-cr /tmp/pr14799-evidence/digest-GnT-rl4.txt /tmp/pr14799-run-fix.txt
gh api repos/boykopovar/AnyPS5/pulls/14799 --jq '[.state, .head.sha, .base.sha] | @tsv'
gh pr checks 14799 --repo boykopovar/AnyPS5Results
| Check | Result | Note |
|---|---|---|
| Trace archive integrity | passed | Both gzip files and the evidence ZIP validate. |
| Attachment identity | passed | Downloaded traces match the archive copies by SHA-256. |
| Digest reproduction | passed | Both attached digests match local output after CRLF normalization. |
| Hosted CI | not run | Six macOS jobs are pending; other reported checks pass; Linux is skipped. |
Commands and unedited output
unzip -t /tmp/pr14799-evidence-v2.zip | tail -1
No errors detected in compressed data of /tmp/pr14799-evidence-v2.zip.
sha256sum /tmp/pr14799-baseline.json.gz /tmp/pr14799-evidence/aps5-trace-a6111d1f071c-31820.json.gz /tmp/pr14799-candidate.json.gz /tmp/pr14799-evidence/aps5-trace-ee8448fc2ee5-29424.json.gz
a6c73cb6cc830ac46b051e7c881d19a3ef4c900864487e8463b01cd500ebba68 /tmp/pr14799-baseline.json.gz
a6c73cb6cc830ac46b051e7c881d19a3ef4c900864487e8463b01cd500ebba68 /tmp/pr14799-evidence/aps5-trace-a6111d1f071c-31820.json.gz
a86905ba5d1c8ba304e10b101a00366f511277f10d4c5d4fff64fe1932f7221d /tmp/pr14799-candidate.json.gz
a86905ba5d1c8ba304e10b101a00366f511277f10d4c5d4fff64fe1932f7221d /tmp/pr14799-evidence/aps5-trace-ee8448fc2ee5-29424.json.gz
Rerun digest output, baseline and candidate summary lines:
**seconds 240:20** from present 4999: 95 presents, frame p50 206.6 / p90 232.4 / p99 266.7 / max 266.7 ms, GPU busy 44.5 ms per present
NVIDIA GeForce RTX 3080, NVIDIA 621.01, Windows 10.0.26220, 3840x2160, commit a6111d1f071c, dropped: yes
**seconds 240:20** from present 3978: 107 presents, frame p50 183.2 / p90 217.5 / p99 245.0 / max 260.1 ms, GPU busy 43.2 ms per present
NVIDIA GeForce RTX 3080, NVIDIA 621.01, Windows 10.0.26220, 3840x2160, commit ee8448fc2ee5, dropped: yes
diff -q --strip-trailing-cr /tmp/pr14799-evidence/digest-M3T-rl5.txt /tmp/pr14799-run-main.txt
diff -q --strip-trailing-cr /tmp/pr14799-evidence/digest-GnT-rl4.txt /tmp/pr14799-run-fix.txt
Both commands produced no output and exited successfully.
gh api repos/boykopovar/AnyPS5/pulls/14799 --jq '[.state, .head.sha, .base.sha] | @tsv'
open\t63dce0027a2bea23047c800148ab20eaafe74c8c\tf2d874c1fdccf781c0fa5d86619bb5dd8894b958
gh pr checks 14799 --repo boykopovar/AnyPS5
linux\tskipping\t0\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477469/job/124937763412
report\tpass\t1m7s\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477343/job/124934280102
labels\tpass\t5s\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335798022/job/124937696259
register-context (ubuntu-latest, Debug)\tpass\t31s\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477469/job/124937762076
relinker (ubuntu-latest, g++)\tpass\t4m20s\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477469/job/124937762066
relinker (windows-latest, g++)\tpass\t7m22s\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477469/job/124937762103
register-context (macos-15-intel, Debug)\tpending\t0\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477469/job/124937762008
conventions\tpass\t15s\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335798015/job/124935236833
register-context (ubuntu-latest, Release)\tpass\t29s\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477469/job/124937762092
relinker-asan\tpass\t2m42s\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477469/job/124937762041
write-watch (macos-15)\tpending\t0\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477469/job/124937762002
write-watch (macos-15-intel)\tpending\t0\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477469/job/124937762054
relinker (macos-latest, clang++)\tpending\t0\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477469/job/124937762110
relinker (macos-latest, clang++, x86_64)\tpending\t0\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477469/job/124937762061
register-context (macos-15-intel, Release)\tpending\t0\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477469/job/124937762024
library-portability\tpass\t1m30s\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477353/job/124934279896
eligibility\tpass\t7s\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477469/job/124934281661
intake\tpass\t21s\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477261/job/124934281111
windows\tpass\t8m13s\thttps://github.com/boykopovar/AnyPS5/actions/runs/41335477469/job/124937762065
Negative control
Not applicable to this evidence-only follow-up: the source and tests did not change, and I did not run a build or modify the code under test.
Findings
Blocking
None found in the newly supplied trace and digest evidence. The missing-trace finding from my earlier review is resolved.
Non-blocking
Approval remains pending the six hosted macOS jobs. I did not independently reproduce the performance run on the stated PC or game scene.
Sources
- Author's evidence comment with the trace attachments
trace_digest.pymatches summary records to trace filenames at this headtrace_digest.pylimits its digest to the summary window at this head
Not verified
The physical PC, title scene, and timing boots were not reproduced here; those remain author-supplied measurements. Six hosted macOS checks are pending. Linux was skipped by CI.
To get this to a good state
No additional trace evidence is needed. Once the pending macOS checks finish, I will re-check the exact head and approve if they pass; if the head changes, I will rerun the same integrity and digest commands on the new evidence.
✅ AMD GPU test: passedPR head
Unstable on main (6, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (9, not counted against this PR)Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)Validated but not compared (2, not in the main baseline's validation pass)Validation IDs already present on main (5 IDs in 715 tests)Skipped (58)ReproduceAutomated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
CnRJay
left a comment
There was a problem hiding this comment.
Removing a syscall per compare on the committer is the right target, and the recovery is scoped tightly: read faults only, exact source-load RIPs, fault address inside the interval. Three points on cost and coverage that the description does not address.
1. The loops are 16 bytes per iteration. GuardedCopy and GuardedCompare do one movdqu pair, one pcmpeqb/pmovmskb/cmp/jne, one add and one bound check per 16 bytes, with no unrolling and no prefetch. The path they replace was ReadProcessMemory into a staging copy plus the CRT memcmp, which uses AVX2 where available. The GTA V scene is dominated by the syscall, so the net is positive there. For a large compare (a 33–66 MB target, or the 256 MiB swept ranges), a loop at this width can be slower per byte than the old path, and the gain disappears once the syscall is amortized over many pages. Please unroll to 64 bytes per iteration (four independent pcmpeqb results OR-ed or AND-ed before the branch), and show a microbenchmark of GuardedCompare against memcmp for 4 KiB, 256 KiB and 64 MiB. The fault handler needs one more load label per width, which the RIP check already supports.
2. The cost of a hit is not measured. Every compare or copy that contains a released page now takes an access violation, runs the vectored handler, and then does the whole read again through the page-by-page ReadProcessMemory path. That is strictly slower than main for that range, by one exception dispatch (tens of microseconds on Windows) plus the discarded partial read. The test shows correctness with a thread decommitting a page in a loop, but not how often a title hits it. Please add a counter for the fallback after a fault, and report it for the GTA V scene and for a streaming-heavy title, so we know whether a heap that decommits and recommits pages makes this a regression. If the count is not negligible, remember that a range just recovered as released could be skipped for direct reads until its page state changes.
3. OwnedByGuest runs per call. Arena::Get().Contains and OverlapsHostRegion are on the fast path for every compare, and the description does not say whether the second takes a lock or walks a list. With 135k proofs per 10 s on this title (see #12921), please confirm it is lock-free and O(1), or state its cost.
Coverage. The guard-page case: after the first access to a PAGE_GUARD page the OS clears the guard bit and raises STATUS_GUARD_PAGE_VIOLATION. ReadProcessMemory only failed and left the bit set. The description says nothing in core sets a guard on arena memory, which is fine today. But a guest mprotect or stack-growth probe on arena memory would now be consumed by a CPU-side compare and produce an unhandled exception, where main returned a quiet failure. Please state which guest-visible calls can reach a guard bit, or make OwnedByGuest refuse ranges whose state has a guard.
The measurement itself is clear and reproducible. 8 boots at 180.0 vs 161.0 ms with non-overlapping ranges, and the traces are attached.
Every committed copy and compare on Windows went through ReadProcessMemory, so a page released during the read counts as uncommitted instead of faulting. Revalidation issues that system call for each compare, and it dominates the committer thread. Ranges the guest arena owns are now read by leaf copy and compare routines. A fault on one of their source loads resumes at a failure return, and the read falls back to the existing page by page path, so the result for released pages is unchanged. Host regions and addresses outside the arena keep ReadProcessMemory. Hosts with AVX2 take 64 bytes per pass, with 16-byte and byte tails; others keep the 16-byte loop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
63dce00 to
79ee45a
Compare
|
@CnRJay thanks, this was useful. The head is now
The earlier main capture had a stray input script tapping during it, so please use the new traces. The result is the same: 178.9 against 159.5 ms over two boots a side. AI-assisted: yes (Claude Code) |
✅ NVIDIA GPU test: passedPR head
Intermittent validation messages (1, seen once in the parallel run, not reproduced when the test was re-run alone 2 times; not counted against this PR)Unstable on main (4, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (3, not counted against this PR)Validation IDs already present on main (5 IDs in 715 tests)Skipped (51)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
MarcZX10
left a comment
There was a problem hiding this comment.
Reviewed — looks good
At head 79ee45a6e2a60846373a1e03708b26f5214ab35a, the Windows guarded-read path, fallback behavior, regression coverage, and attached performance evidence are consistent with the reviewed change.
What I ran
Reviewed all eight changed files against base b2a47847651b0c1281241c5f6da51b4bdf1c83ae. Environment: Debian Linux 6.1, Python 3.11.2, GCC 12.2.0. No local build or CTest was run. Hosted Windows, relinker, sanitizer, register-context, and write-watch checks passed on this head; Linux was skipped. The performance captures and GPU results were supplied by the author.
python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 14799 --output-dir /tmp/review-14799
git diff --check b2a47847651b0c1281241c5f6da51b4bdf1c83ae...79ee45a6e2a60846373a1e03708b26f5214ab35a
gh pr checks 14799 --repo boykopovar/AnyPS5Results
| Check | Result | Note |
|---|---|---|
| Diff and conventions | passed | No whitespace or convention errors |
| Merge preview | passed | Clean against current main |
| Windows and relinker CI | passed | Required Windows and relinker matrix completed |
| Focused runtime and performance | passed | Author-provided Windows/GTA V results and matched captures |
| Local build and CTest | not run | Not run locally |
| Linux hosted checks | not run | Workflow skipped Linux for this PR |
Precheck table and relevant raw output
| Check | Status | Note |
|---|---|---|
| build_and_compile | not_applicable | Precheck does not build or compile |
| diff_check | passed | git diff --check b2a47847651b0c1281241c5f6da51b4bdf1c83ae...79ee45a6e2a60846373a1e03708b26f5214ab35a |
| conventions_and_pr_template | passed | Repository convention and template check |
| changed_export_abi_audit | failed | Workspace helper absent from isolated PR snapshot; no public exports changed |
| Python checks | not_applicable | No changed Python files |
| live_dependency_and_mergeability_check | passed | Current-base merge preview clean |
| build_dependent_cpp_tests | not_applicable | Requires a build |
| test_quality_heuristics | failed | Workspace helper absent from isolated PR snapshot; test registration inspected directly |
| negative_controls | not_applicable | No supported no-build control |
All conventions checks passed.
/usr/bin/python3: can't open file '/tmp/review-14799/head-79ee45a6e2a6-26ce0555/tools/pr_abi_audit.py': [Errno 2] No such file or directory
/usr/bin/python3: can't open file '/tmp/review-14799/head-79ee45a6e2a6-26ce0555/tools/pr_test_quality.py': [Errno 2] No such file or directory
The heuristic's suspected missing test registration is a false positive: CommittedReadMapping.cpp is included by the test target, and the changed Windows cases are registered from libc/CMakeLists.txt.
Negative control
| Assertion | Change to code under test | Did the new test fail? |
|---|---|---|
| Released pages are zeroed or skipped, while wild destination/expected faults remain fatal | Not changed locally | Not run locally; author supplied focused test results |
Findings
Blocking
None found in the reviewed diff.
Non-blocking
- Both attached traces report dropped detailed events, while their summary totals are marked complete. The author also provides repeated timing boots; this limits trace-level diagnosis but does not invalidate the reported summary measurements.
- Linux was skipped, and I did not run a local build or CTest.
Sources
- Guarded copy/compare and exception recovery: GuestArena.cpp at the reviewed head.
- Integration and regression coverage: GuestMemory.cpp, CommittedReadMapping.cpp.
- Author-provided performance traces, digest, and Windows GPU report: PR description.
Not verified
- I did not independently reproduce Windows runtime tests, the GTA V measurements, or GPU behavior.
- Linux CI was skipped; no physical console test was run.
To get this to a good state
No changes are required from this review. The Windows and relinker checks passed; Linux coverage remains unverified here.
|
Windows check of exact head Setup: Windows 11 Enterprise 10.0.22621, Ryzen 9 5950X (AVX2), Radeon RX 6700 XT, AMD proprietary driver 26.10.1, WinLibs GCC 15.2.0 UCRT r7, Release. The full CTest suite ran once through the shared GPU queue with
No performance measurement here: my display was off, so no title ran. 🤖 Generated with Claude Code |
What
On Windows every committed guest copy and compare goes through
ReadProcessMemory, added in 4201b82 so that a page released during the read counts as uncommitted instead of faulting. Revalidation issues one system call per compare and then compares a second copy of the bytes. In GTA V (PPSA04263)ZwReadVirtualMemorywas 12% of the committer thread's samples, and the committer is the thread that bounds the frame: it waited for only 12% of the steady window.Ranges the guest arena owns, outside recorded host regions, are now read by leaf copy and compare routines in
GuestArena.cpp. Hosts with AVX2 take 64 bytes per pass, with 16-byte and byte tails; other hosts keep the 16-byte loop. A fault on one of their source loads resumes at a failure return, and the read falls back to the existing page by page path, so released pages keep their behaviour: zero in a copy, left out of a compare. Host regions and addresses outside the arena keepReadProcessMemory, which covers the host heap case of 4201b82. Linux is unchanged.GuestArenaRecoverRead_nid_no_patchruns first inReportCrashandReturnBreakHandler. It takes only read access violations and in-page errors, at an exact source load, with the fault address inside the interval being read. A fault on the destination or expected buffer, or anywhere else, still reaches the crash path.Guard pages are not recovered.
AccessibleandCommittedRangesexclude a guarded page on a fresh query, so a direct read only meets one through stale page state, which means a guard was set on arena memory without going throughGuestAllocations. No non-test code setsPAGE_GUARD. OS stack guards are out of reach: a stack inside the arena bounds at startup is in_hostRegions, later stacks cannot land in the reserved placeholders, and guest threads run on_beginthreadexstacks. That relies on Windows honouring reservations. Such a guard ends as an unhandled exception; it may not print the usualFATALreport. The existingCommittedReadMappingcase that set a raw guard now announces it throughGuestAllocations::Mutation, the contract real code follows.Tested
Windows 11, Ryzen 7 5800X, RTX 3080, GTA V (PPSA04263), save at the first autosave, standing still, 3840x2160. Main
e0caf13600and this PR79ee45a6e2each with #11580 applied, because main aborts at Story load without it (#11425), giving commitsb9c59ab4094aandcfbd1938d453. Boots interleaved. Frame time is the median from videoout present stamps, +20 to +140 s after the player stands still.About 19 ms, 10.8%, about 5.6 to 6.3 fps. GPU busy per present stayed at about 42 ms. No fatal in any boot. The previous head of this PR, without the wider loops and on
f2d874c1fd, read 180.0 and 161.0 ms over four boots a side.Warm captures as the form describes: the scene ran once, the title was restarted on that cache, same save and input, one extra boot a side after the timing boots,
APS5_TRACE_SECONDS=240:20. These replace the previous captures: an input script left over from the earlier fix capture was still tapping during the earlier main capture, so that one should not be used. The four boots above are the claim.Deviations from the form: the log is redirected and frames are dumped on every run, because the script that drives the menus reads both, so the two sides pay the same cost. A d-pad tap every 15 s holds the normal camera, because the idle cinematic camera otherwise adds about 45 ms at +72 s.
APS5_ADDRESS_COPY_MAX_MIB=512is set on both sides. CPU and GPU utilisation loggers (typeperf,nvidia-smi) ran during every boot. The timing boots keepAPS5_TRACE_SUMMARY_EVERY=10and no trace. The order was a warm-up boot of this PR, then this PR, main, this PR, main; no run was thrown away. The first main boot waited about 50 s longer at the title because the menu script missed a press; the frame window starts when the player stands still, so it is unaffected. The game window had focus for the timing boots, but only 22 of 56 samples in the capture of this PR and 52 of 55 in the capture of main, which if anything favours main. Both traces report dropped events.aps5-summary.jsonllines, warm capture main and this PR, then one 10 s window of a timing boot of each:{"commit":"b9c59ab4094a","dirty":false,"env":{"APS5_ADDRESS_COPY_MAX_MIB":"512","APS5_DUMP_FRAMES":"80","APS5_DUMP_FRAMES_EVERY":"300","APS5_LOG_TIMESTAMPS":"1","APS5_NO_PAD_INPUT":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"240:20","APS5_TRACE_SUMMARY_EVERY":"10","APS5_VIDEOOUT_TELEMETRY_DIR":"<path>"},"gpu":"NVIDIA GeForce RTX 3080","driver":"NVIDIA 621.01","os":"Windows 10.0.26220","cpu":"AMD Ryzen 7 5800X 8-Core Processor","cpu_threads":16,"memory_mib":32676,"title_id":"PPSA04263","title":"Grand Theft Auto V","content_version":"01.011.001","resolution":"3840x2160","window":{"spec":"seconds 240:20","first_present":3925,"ms":20174.690},"presents":109,"frame_ms":{"p50":183.912,"p90":190.396,"p99":199.967,"max":240.345},"gpu_busy_ms_per_present":41.690,"worker_busy_pct":{"queue worker 0x0":98.147},"top_phases_ms_per_frame":{"Driver.Packet":174.318,"Graphics.Draw":139.392,"Driver.Packet.draw":131.167,"Driver.Draw":130.073,"Graphics.Draw.draw_and_resource_release":113.566,"Driver.Draw.shader_compile_and_link":57.957,"Driver.Draw.draw_enqueued":53.492,"Shader.DrawStage":44.767},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":5.325,"events_lost":729388,"totals_full":false},"draw committer":{"events_full_after_s":12.244,"events_lost":169351,"totals_full":false}},"trace":"aps5-trace-b9c59ab4094a-15408.json.gz"} {"commit":"cfbd1938d453","dirty":false,"env":{"APS5_ADDRESS_COPY_MAX_MIB":"512","APS5_DUMP_FRAMES":"80","APS5_DUMP_FRAMES_EVERY":"300","APS5_LOG_TIMESTAMPS":"1","APS5_NO_PAD_INPUT":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"240:20","APS5_TRACE_SUMMARY_EVERY":"10","APS5_VIDEOOUT_TELEMETRY_DIR":"<path>"},"gpu":"NVIDIA GeForce RTX 3080","driver":"NVIDIA 621.01","os":"Windows 10.0.26220","cpu":"AMD Ryzen 7 5800X 8-Core Processor","cpu_threads":16,"memory_mib":32676,"title_id":"PPSA04263","title":"Grand Theft Auto V","content_version":"01.011.001","resolution":"3840x2160","window":{"spec":"seconds 240:20","first_present":4009,"ms":20075.328},"presents":128,"frame_ms":{"p50":156.078,"p90":163.000,"p99":180.329,"max":214.877},"gpu_busy_ms_per_present":41.470,"worker_busy_pct":{"queue worker 0x0":97.898},"top_phases_ms_per_frame":{"Driver.Packet":146.557,"Graphics.Draw":113.653,"Driver.Packet.draw":106.732,"Driver.Draw":105.778,"Graphics.Draw.draw_and_resource_release":88.372,"Driver.Draw.shader_compile_and_link":55.492,"Shader.DrawStage":42.977,"Driver.Draw.draw_enqueued":32.582},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":4.682,"events_lost":853701,"totals_full":false},"draw committer":{"events_full_after_s":10.621,"events_lost":233960,"totals_full":false}},"trace":"aps5-trace-cfbd1938d453-16724.json.gz"} {"commit":"b9c59ab4094a","dirty":false,"env":{"APS5_ADDRESS_COPY_MAX_MIB":"512","APS5_DUMP_FRAMES":"80","APS5_DUMP_FRAMES_EVERY":"300","APS5_LOG_TIMESTAMPS":"1","APS5_NO_PAD_INPUT":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_DIR":"<path>","APS5_TRACE_SUMMARY_EVERY":"10","APS5_VIDEOOUT_TELEMETRY_DIR":"<path>"},"gpu":"NVIDIA GeForce RTX 3080","driver":"NVIDIA 621.01","os":"Windows 10.0.26220","cpu":"AMD Ryzen 7 5800X 8-Core Processor","cpu_threads":16,"memory_mib":32676,"title_id":"PPSA04263","title":"Grand Theft Auto V","content_version":"01.011.001","resolution":"3840x2160","window":{"spec":"summary_every 10","first_present":3973,"ms":10038.433},"interval_start_s":251.526,"interval_end_s":261.564,"presents":55,"frame_ms":{"p50":179.600,"p90":195.494,"p99":221.940,"max":221.940},"gpu_busy_ms_per_present":41.926,"worker_busy_pct":{"queue worker 0x0":98.092},"top_phases_ms_per_frame":{"Driver.Packet":171.244,"Graphics.Draw":136.024,"Driver.Packet.draw":130.164,"Driver.Draw":129.147,"Graphics.Draw.draw_and_resource_release":110.092,"Driver.Draw.shader_compile_and_link":61.053,"Driver.Draw.draw_enqueued":49.263,"Shader.DrawStage":47.806},"trace_dropped":false,"trace":""} {"commit":"cfbd1938d453","dirty":false,"env":{"APS5_ADDRESS_COPY_MAX_MIB":"512","APS5_DUMP_FRAMES":"80","APS5_DUMP_FRAMES_EVERY":"300","APS5_LOG_TIMESTAMPS":"1","APS5_NO_PAD_INPUT":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_DIR":"<path>","APS5_TRACE_SUMMARY_EVERY":"10","APS5_VIDEOOUT_TELEMETRY_DIR":"<path>"},"gpu":"NVIDIA GeForce RTX 3080","driver":"NVIDIA 621.01","os":"Windows 10.0.26220","cpu":"AMD Ryzen 7 5800X 8-Core Processor","cpu_threads":16,"memory_mib":32676,"title_id":"PPSA04263","title":"Grand Theft Auto V","content_version":"01.011.001","resolution":"3840x2160","window":{"spec":"summary_every 10","first_present":4109,"ms":10053.063},"interval_start_s":251.723,"interval_end_s":261.776,"presents":62,"frame_ms":{"p50":160.081,"p90":167.314,"p99":221.590,"max":221.590},"gpu_busy_ms_per_present":41.102,"worker_busy_pct":{"queue worker 0x0":97.897},"top_phases_ms_per_frame":{"Driver.Packet":151.573,"Graphics.Draw":117.322,"Driver.Packet.draw":110.083,"Driver.Draw":109.189,"Graphics.Draw.draw_and_resource_release":91.297,"Driver.Draw.shader_compile_and_link":57.744,"Shader.DrawStage":44.796,"Driver.Draw.draw_enqueued":33.035},"trace_dropped":false,"trace":""}Traces: warm main, warm this PR. Raw files of every boot, digests, comparison and benchmark source and results: pr14799-evidence-v3.zip.
tools/trace_digest.pysummaries:seconds 240:20 from present 3925: 109 presents, frame p50 183.9 / p90 190.4 / p99 200.0 / max 240.3 ms, GPU busy 41.7 ms per present
NVIDIA GeForce RTX 3080, NVIDIA 621.01, Windows 10.0.26220, 3840x2160, commit b9c59ab4094a, dropped: yes
{"commit":"b9c59ab4094a","dirty":false,"env":{"APS5_ADDRESS_COPY_MAX_MIB":"512","APS5_DUMP_FRAMES":"80","APS5_DUMP_FRAMES_EVERY":"300","APS5_LOG_TIMESTAMPS":"1","APS5_NO_PAD_INPUT":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"240:20","APS5_TRACE_SUMMARY_EVERY":"10","APS5_VIDEOOUT_TELEMETRY_DIR":"<path>"},"gpu":"NVIDIA GeForce RTX 3080","driver":"NVIDIA 621.01","os":"Windows 10.0.26220","cpu":"AMD Ryzen 7 5800X 8-Core Processor","cpu_threads":16,"memory_mib":32676,"title_id":"PPSA04263","title":"Grand Theft Auto V","content_version":"01.011.001","resolution":"3840x2160","window":{"spec":"seconds 240:20","first_present":3925,"ms":20174.69},"presents":109,"frame_ms":{"p50":183.912,"p90":190.396,"p99":199.967,"max":240.345},"gpu_busy_ms_per_present":41.69,"worker_busy_pct":{"queue worker 0x0":98.147},"top_phases_ms_per_frame":{"Driver.Packet":174.318,"Graphics.Draw":139.392,"Driver.Packet.draw":131.167,"Driver.Draw":130.073,"Graphics.Draw.draw_and_resource_release":113.566,"Driver.Draw.shader_compile_and_link":57.957,"Driver.Draw.draw_enqueued":53.492,"Shader.DrawStage":44.767},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":5.325,"events_lost":729388,"totals_full":false},"draw committer":{"events_full_after_s":12.244,"events_lost":169351,"totals_full":false}},"trace":"aps5-trace-b9c59ab4094a-15408.json.gz"}Slowest frames (largest slices inside each, inclusive)
Threads (ms per frame)
Largest slices (ms per frame, inclusive, all threads): Graphics.Draw 75.4, Graphics.Draw.draw_and_resource_release 60.0, batch 41.6, Driver.Packet 40.1, Driver.Packet.draw 31.1, Driver.Draw 30.8, VideoOut.Flip 14.3, VideoOut.Flip.present 14.2, Driver.Present 14.2, Driver.Present.render_fence_wait 13.7
Longest waits
Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)
seconds 240:20 from present 4009: 128 presents, frame p50 156.1 / p90 163.0 / p99 180.3 / max 214.9 ms, GPU busy 41.5 ms per present
NVIDIA GeForce RTX 3080, NVIDIA 621.01, Windows 10.0.26220, 3840x2160, commit cfbd1938d453, dropped: yes
{"commit":"cfbd1938d453","dirty":false,"env":{"APS5_ADDRESS_COPY_MAX_MIB":"512","APS5_DUMP_FRAMES":"80","APS5_DUMP_FRAMES_EVERY":"300","APS5_LOG_TIMESTAMPS":"1","APS5_NO_PAD_INPUT":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"240:20","APS5_TRACE_SUMMARY_EVERY":"10","APS5_VIDEOOUT_TELEMETRY_DIR":"<path>"},"gpu":"NVIDIA GeForce RTX 3080","driver":"NVIDIA 621.01","os":"Windows 10.0.26220","cpu":"AMD Ryzen 7 5800X 8-Core Processor","cpu_threads":16,"memory_mib":32676,"title_id":"PPSA04263","title":"Grand Theft Auto V","content_version":"01.011.001","resolution":"3840x2160","window":{"spec":"seconds 240:20","first_present":4009,"ms":20075.328},"presents":128,"frame_ms":{"p50":156.078,"p90":163.0,"p99":180.329,"max":214.877},"gpu_busy_ms_per_present":41.47,"worker_busy_pct":{"queue worker 0x0":97.898},"top_phases_ms_per_frame":{"Driver.Packet":146.557,"Graphics.Draw":113.653,"Driver.Packet.draw":106.732,"Driver.Draw":105.778,"Graphics.Draw.draw_and_resource_release":88.372,"Driver.Draw.shader_compile_and_link":55.492,"Shader.DrawStage":42.977,"Driver.Draw.draw_enqueued":32.582},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":4.682,"events_lost":853701,"totals_full":false},"draw committer":{"events_full_after_s":10.621,"events_lost":233960,"totals_full":false}},"trace":"aps5-trace-cfbd1938d453-16724.json.gz"}Slowest frames (largest slices inside each, inclusive)
Threads (ms per frame)
Largest slices (ms per frame, inclusive, all threads): Graphics.Draw 52.0, batch 41.3, Graphics.Draw.draw_and_resource_release 38.5, Driver.Packet 29.1, Driver.Packet.draw 21.8, Driver.Draw 21.6, VideoOut.Flip 14.2, VideoOut.Flip.present 14.1, Driver.Present 14.1, Driver.Present.render_fence_wait 13.6
Longest waits
Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)
Benchmark, one pinned thread, median of 21, GB/s, equal data, 4 KiB / 256 KiB / 4 MiB / 64 MiB:
ReadProcessMemory+memcmpmemcmpThe 4 KiB and 4 MiB results varied between runs, so ranges are given. Copy,
ReadProcessMemorythen this PR: 5.3 then 107-145, 27 then 60-67, 26-29 then 38-63, 11 then 14 GB/s.memcpyreaches about 21 GB/s at 64 MiB, ahead of this loop at that size only. Prefetching gave no gain and was left out.Fault cost: a local counter build, not part of this PR, recorded 0 recovered faults in about 1.44 million guarded reads on this scene. There is no data from a streaming-heavy title.
OwnedByGuesttakes no lock:Containsis arithmetic andOverlapsHostRegionwalks_hostRegions, which on Windows is only written at startup. It held 80 entries here, 46.6 ns a call in a standalone timing, about 0.03 ms a frame at about 720 calls a frame.Tests:
agc_driver_committed_read_mapping(released page, mismatch beside a released page, straddling copy and compare across the 64-byte, 16-byte and byte loops, concurrent decommit),windows_guarded_read_wild_destination,_expected,_outside,windows_backtrace_*,windows_crash_*,agc_driver_guest_memory_lifetime*: 16 of 16 pass. Red controls: with recovery disabled the mapping test segfaults; with the classifier's address and instruction checks removed,windows_guarded_read_wild_expectedand_outsidefail.Paired gate on
e0caf13600and79ee45a6e2withANYPS5_REQUIRE_VULKAN=1,ANYPS5_NO_SHADER_CACHE=1,VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation,ctest -R "committed_read_mapping|agc_driver_guest_memory_lifetime" -V: all pass on both, no missing-layer line, noVUID-on either side.Not tested: Linux (no change there), the non-AVX2 path as the main loop on a host without AVX2, AMD or Intel GPUs locally, Demon's Souls or any title but GTA V, and the unannounced guard path at run time. Overlap:
pr_overlapreports shared files only.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.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code