Repository navigation
perf(libc,agc): skip never-written guest pages in Windows heap mirror zero scans - #15967
sktharunbalaji13-cmd wants to merge 2 commits into
Conversation
|
This pull request is labeled |
|
Added to the description under Tested: both 🤖 Addressed by Claude Code |
|
Hi Thanks for this. The known-zero bitmap and the single One ordering I could not settle from the source. A dropped report hides any CPU store that leaves a known-zero page reading zero. Suppose a non-imported resident page is written by the GPU into its device copy (a storage image or buffer takes its layer generation). Nothing clears the page's zero bit until the result is flushed back. If the guest CPU then stores zeros to that page, for example resetting a counter or clearing a readback area, Could you either point to where a GPU-written range clears its zero bits when the generation is taken, or add a test in the style of the storage-image cases (fresh page, GPU write, CPU stores 0, flush, expect 0)? If that test fails, clearing the bits for the range when a GPU write is recorded would close it. |
|
Windows check of head Setup: Windows 11 Enterprise 22621, WinLibs GCC 15.2 UCRT, Release. The hosted Windows check does not run these tests on pull requests.
AI-assisted: yes. |
✅ AMD GPU test: passedPR head
Unstable on main (5, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (13, 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)Validation IDs already present on main (4 IDs in 810 tests)Skipped (68)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. |
|
@oneandonlydean You were right, and the test reproduces it: a fresh heap block, an image store into it, a CPU store of 0, then a read returned the GPU's Fixed in bea14eb:
🤖 Addressed by Claude Code |
✅ AMD GPU test: passedPR head
Unstable on main (4, 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)Validation IDs already present on main (4 IDs in 810 tests)Skipped (69)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. |
❌ NVIDIA GPU test: failedPR head
Failing testsThis PR changes test files, so each failing test was also built and run on main with only the PR's test changes applied. ✅ there means the failure comes from the PR's other changes; ❌ means the test fails on main too, so this run does not show the PR caused it.
agc_driver_job_manager_compute_capacity: Failed (also failed when re-run alone), assertion and diagnosticsUnstable on main (2, 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 (9 IDs in 810 tests)Skipped (60)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. |
… zero scans Windows has no pagemap, so the heap mirror zero scans read every page of their range, and reading a committed page that was never written makes Windows allocate a physical zero page for it. The guest arena now keeps the 4 KiB pages it committed fresh under a write watch that no write watch report, host write or host import has touched since, and the scans skip them. The first read of such a page also sets its write-watch bit, so a later collect reported it as a CPU store. A reported page that is still known zero and still reads as zero is no longer reported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A GPU write lands in a device copy or an import first, so the guest page keeps its known-zero bit, and a later CPU store of zero to it was dropped as a first-touch report: the GPU result then won over the CPU store. Every recorded GPU write (pending writes, fills, label stores, storage image layers, MarkWritten and MarkPossiblyWritten) now forgets the range, and a storage image upload reads its still known-zero pages before it takes its write generation, so their first-touch reports come before it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bea14eb to
7fd4e17
Compare
|
Thanks for the NVIDIA run. I looked at
I rebased the branch onto current main ( 🤖 Generated with Claude Code, OpenCode, Command Code and Gemini |
|
The only failing check on head I do not have rights to re-run it. A maintainer re-run of that job would settle it. 🤖 Generated with Claude Code, OpenCode, Command Code and Gemini |
|
Hi @sktharunbalaji13-cmd, thanks for the PR and for flagging the red check. The |
✅ NVIDIA GPU test: passedPR head
Unstable on main (2, 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)Passed on retry (6, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)Validation IDs already present on main (9 IDs in 807 tests)Skipped (60)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. |
❌ AMD GPU test: failedPR head
Failing testsThis PR changes test files, so each failing test was also built and run on main with only the PR's test changes applied. ✅ there means the failure comes from the PR's other changes; ❌ means the test fails on main too, so this run does not show the PR caused it.
agc_graphics_profile_draw: Timeout (also failed when re-run alone), assertion and diagnosticsUnstable on main (7, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (10, 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 (1, not in the main baseline's validation pass)Validation IDs already present on main (4 IDs in 807 tests)Skipped (71)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. |
What
On Windows, the heap mirror zero scans (
GuestNonzeroBlocks,GuestBytesZeroinResidentMirror.cpp) read every page of their range, because Windows has no/proc/self/pagemapto skip unpopulated pages the waypossiblyNonzerodoes on Linux. Reading a committed page that was never written makes Windows allocate a physical zero page for it, so a scan over a large, mostly empty heap fills the RAM by itself.WindowsMappings.hpp) keeps a bitmap of the 4 KiB pages it committed fresh under a write watch (Commitwith write watch,commitResident) that nothing has written since. Bits are cleared for every page aGetWriteWatchcall reports (all calls go through one wrapper), atomically with the reset that replacedResetWriteWatchwhere bits remain, and onreset,demote,evict, restored saved content, host writes and host imports (GPU writes through an import are not seen by the write watch).GuestArenaKnownZero_nid_no_patchreports the pages of a range that are still known zero, after a non-resettingGetWriteWatchof the range and taking the arena lock one 2 MiB chunk at a time.possiblyNonzeroandGuestBytesZeroon Windows skip those pages.MEM_WRITE_WATCH, read pages 0 and 5,GetWriteWatchreports 0 and 5). The old scans read everything early, so that report was consumed before anyone cared. With the scans skipping those pages, the first read could happen after a storage image took its layer generation and then pass for a CPU store, so the flush hook's CPU-kept fast path dropped a GPU result (agc_driver_resident_heap_mirror, StorageImageTests). A reported page that is still known zero and still reads as zero (checked withReadProcessMemory, only when the report is complete) is therefore no longer reported: its bytes have not changed since the commit.Generic behaviour: nothing depends on a title, address or size.
Tested
Windows 11 26200, RTX 4060 Laptop GPU (8 GB), NVIDIA driver 617.42, 16 GB RAM, MinGW GCC (winlibs) Release, on
main0540ef0 and on this branch, same machine:core/libs/tests/WindowsWriteWatch.cpp(CheckKnownZeroPrivate,CheckKnownZeroResident,CheckKnownZeroShared,CheckKnownZeroFirstReads): fresh commits are known zero and clipped to the query, CPU stores, resetting/fresh/armed collects, host write windows, host imports, resets, an evict plus remap with restored bytes, memory without a write watch and shared memory; a first read or a zero store of a fresh page is not collected as a write while a real store is.StorageImageZeroStoreTestsinResidentHeapMirror.cpp(review follow-up): an image store into a never-written block, a CPU store of 0, then a read must return 0. It fails without the GPU-write forgetting (the GPU value0x12345678wins) and passes with it. Every recorded GPU write (pending writes, fills, label stores, storage image layers,MarkWritten/MarkPossiblyWritten) forgets the range, and a storage image upload reads its still known-zero pages before taking its write generation.ctest -R "windows_write_watch|agc_driver_resident_heap_mirror|heap_mirror|agc_write_tracking$"withANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1: 12/12 pass (agc_driver_resident_heap_mirrorfailed with the first version of this change and passes now).host_import_*,guest_memory*,guest_owned_memory*,committed_read_mapping,copied_writer_*,linear_target_*,private_collect,write_watch_coverage,storage_write_back_*,read_only_lease_writer,partially_resident_snapshot,copy_guest_bytes): pass, exceptagc_driver_direct_write_completion, which fails the same way onmainon this machine (3 of 3 runs, "the completion of a buffer write dropped image results recorded after it").VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validationruns of the focused tests: the 12 focused tests above onmainand on this branch: noVUID-errors in either log, and neither log containswas not found but was requested by env var VK_INSTANCE_LAYERS.tools/trace_digest.pysummary lines,APS5_TRACE_SECONDS=0:40, same machine and scene, back-to-back runs (APS5_SKIP_REFUSED_DRAWS=1 APS5_PROFILE_DRAW=1):Traces: main, this branch (summaries: main, this branch). Both traces lost events once their buffers filled (about 35-38 s).
aps5-summary.jsonllines of the two captures:main0540ef0:{"commit":"0540ef0ef2ec","dirty":false,"env":{"APS5_PROFILE_DRAW":"1","APS5_SKIP_REFUSED_DRAWS":"1","APS5_TRACE_SECONDS":"0:40"},"gpu":"NVIDIA GeForce RTX 4060 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26200","cpu":"AMD Ryzen 7 7840HS w/ Radeon 780M Graphics","cpu_threads":16,"memory_mib":15654,"title_id":"PPSA02225","title":"Ghost of Tsushima","content_version":"02.024.000","resolution":"3840x2160","window":{"spec":"seconds 0:40","first_present":0,"ms":41661.251},"presents":206,"frame_ms":{"p50":16.941,"p90":87.768,"p99":3326.594,"max":18738.701},"gpu_busy_ms_per_present":19.443,"worker_busy_pct":{"queue worker 0x0":68.571,"queue worker 0x40":0.108,"queue worker 0x20":43.126,"queue worker 0x48":2.304,"queue worker 0x38":58.010,"queue worker 0x52":8.212,"queue worker 0x30":11.413},"top_phases_ms_per_frame":{"Driver.Packet":386.779,"Driver.Packet.memory_wait":221.979,"Driver.Packet.dispatch":153.109,"Driver.Dispatch":143.336,"Vulkan.Dispatch":102.638,"Vulkan.Dispatch.shader_resources":101.679,"Shader.Register":24.634,"Driver.Dispatch.prepared_invocation":12.667},"trace_dropped":true,"trace_dropped_threads":{"title":{"events_full_after_s":38.372,"events_lost":29304,"totals_full":false}},"trace":"aps5-trace-0540ef0ef2ec-24556.json.gz"}this branch d28560c:
{"commit":"d28560cdca1a","dirty":false,"env":{"APS5_PROFILE_DRAW":"1","APS5_SKIP_REFUSED_DRAWS":"1","APS5_TRACE_SECONDS":"0:40"},"gpu":"NVIDIA GeForce RTX 4060 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26200","cpu":"AMD Ryzen 7 7840HS w/ Radeon 780M Graphics","cpu_threads":16,"memory_mib":15654,"title_id":"PPSA02225","title":"Ghost of Tsushima","content_version":"02.024.000","resolution":"3840x2160","window":{"spec":"seconds 0:40","first_present":0,"ms":40110.376},"presents":310,"frame_ms":{"p50":17.756,"p90":414.348,"p99":1017.909,"max":5108.052},"gpu_busy_ms_per_present":24.137,"worker_busy_pct":{"queue worker 0x0":64.681,"queue worker 0x40":0.685,"queue worker 0x20":31.676,"queue worker 0x48":3.541,"queue worker 0x38":46.533,"queue worker 0x52":4.772,"queue worker 0x30":7.795},"top_phases_ms_per_frame":{"Driver.Packet":205.300,"Driver.Packet.memory_wait":103.823,"Driver.Packet.dispatch":89.295,"Driver.Dispatch":80.776,"Vulkan.Dispatch":53.573,"Vulkan.Dispatch.shader_resources":52.561,"Shader.Register":16.180,"Recorder.Sync":10.411},"trace_dropped":true,"trace_dropped_threads":{"title":{"events_full_after_s":34.843,"events_lost":35178,"totals_full":false}},"trace":"aps5-trace-d28560cdca1a-25536.json.gz"}tools/trace_digest.pyoutput,mainseconds 0:40 from present 0: 206 presents, frame p50 16.9 / p90 87.8 / p99 3326.6 / max 18738.7 ms, GPU busy 19.4 ms per present
NVIDIA GeForce RTX 4060 Laptop GPU, NVIDIA 617.42, Windows 10.0.26200, 3840x2160, commit 0540ef0, dropped: yes
{"commit":"0540ef0ef2ec","dirty":false,"env":{"APS5_PROFILE_DRAW":"1","APS5_SKIP_REFUSED_DRAWS":"1","APS5_TRACE_SECONDS":"0:40"},"gpu":"NVIDIA GeForce RTX 4060 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26200","cpu":"AMD Ryzen 7 7840HS w/ Radeon 780M Graphics","cpu_threads":16,"memory_mib":15654,"title_id":"PPSA02225","title":"Ghost of Tsushima","content_version":"02.024.000","resolution":"3840x2160","window":{"spec":"seconds 0:40","first_present":0,"ms":41661.251},"presents":206,"frame_ms":{"p50":16.941,"p90":87.768,"p99":3326.594,"max":18738.701},"gpu_busy_ms_per_present":19.443,"worker_busy_pct":{"queue worker 0x0":68.571,"queue worker 0x40":0.108,"queue worker 0x20":43.126,"queue worker 0x48":2.304,"queue worker 0x38":58.01,"queue worker 0x52":8.212,"queue worker 0x30":11.413},"top_phases_ms_per_frame":{"Driver.Packet":386.779,"Driver.Packet.memory_wait":221.979,"Driver.Packet.dispatch":153.109,"Driver.Dispatch":143.336,"Vulkan.Dispatch":102.638,"Vulkan.Dispatch.shader_resources":101.679,"Shader.Register":24.634,"Driver.Dispatch.prepared_invocation":12.667},"trace_dropped":true,"trace_dropped_threads":{"title":{"events_full_after_s":38.372,"events_lost":29304,"totals_full":false}},"trace":"aps5-trace-0540ef0ef2ec-24556.json.gz"}Incomplete trace: recorded data was lost. Slice-based timings, waits and batch inventories cover retained events only; missing slices are not idle time. Summary statistics are reported separately.
Slowest frames (largest slices inside each, inclusive)
Persistent spikes (frame time above the fastest frame that separates it from a longer frame; one entry per stall, not per frame)
Threads (ms per frame)
Largest slices (ms per frame, inclusive, all threads): Driver.WorkerWait 414.1, Driver.WorkerWait.idle_and_completion_poll 413.9, Driver.Packet 386.4, Driver.Packet.memory_wait 222.0, Driver.Packet.dispatch 153.1, Driver.Dispatch 143.3, Vulkan.Dispatch 102.6, Vulkan.Dispatch.shader_resources 101.7, batch 19.3, Driver.Dispatch.prepared_invocation 12.7
Longest waits
Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)
tools/trace_digest.pyoutput, this branchseconds 0:40 from present 0: 310 presents, frame p50 17.8 / p90 414.3 / p99 1017.9 / max 5108.1 ms, GPU busy 24.1 ms per present
NVIDIA GeForce RTX 4060 Laptop GPU, NVIDIA 617.42, Windows 10.0.26200, 3840x2160, commit d28560c, dropped: yes
{"commit":"d28560cdca1a","dirty":false,"env":{"APS5_PROFILE_DRAW":"1","APS5_SKIP_REFUSED_DRAWS":"1","APS5_TRACE_SECONDS":"0:40"},"gpu":"NVIDIA GeForce RTX 4060 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26200","cpu":"AMD Ryzen 7 7840HS w/ Radeon 780M Graphics","cpu_threads":16,"memory_mib":15654,"title_id":"PPSA02225","title":"Ghost of Tsushima","content_version":"02.024.000","resolution":"3840x2160","window":{"spec":"seconds 0:40","first_present":0,"ms":40110.376},"presents":310,"frame_ms":{"p50":17.756,"p90":414.348,"p99":1017.909,"max":5108.052},"gpu_busy_ms_per_present":24.137,"worker_busy_pct":{"queue worker 0x0":64.681,"queue worker 0x40":0.685,"queue worker 0x20":31.676,"queue worker 0x48":3.541,"queue worker 0x38":46.533,"queue worker 0x52":4.772,"queue worker 0x30":7.795},"top_phases_ms_per_frame":{"Driver.Packet":205.3,"Driver.Packet.memory_wait":103.823,"Driver.Packet.dispatch":89.295,"Driver.Dispatch":80.776,"Vulkan.Dispatch":53.573,"Vulkan.Dispatch.shader_resources":52.561,"Shader.Register":16.18,"Recorder.Sync":10.411},"trace_dropped":true,"trace_dropped_threads":{"title":{"events_full_after_s":34.843,"events_lost":35178,"totals_full":false}},"trace":"aps5-trace-d28560cdca1a-25536.json.gz"}Incomplete trace: recorded data was lost. Slice-based timings, waits and batch inventories cover retained events only; missing slices are not idle time. Summary statistics are reported separately.
Slowest frames (largest slices inside each, inclusive)
Persistent spikes (frame time above the fastest frame that separates it from a longer frame; one entry per stall, not per frame)
Threads (ms per frame)
Largest slices (ms per frame, inclusive, all threads): Driver.WorkerWait 320.5, Driver.WorkerWait.idle_and_completion_poll 320.2, Driver.Packet 204.7, Driver.Packet.memory_wait 103.8, Driver.Packet.dispatch 89.3, Driver.Dispatch 80.6, Vulkan.Dispatch 53.5, Vulkan.Dispatch.shader_resources 52.5, batch 24.2, Recorder.Sync 10.4
Longest waits
Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)
From the same two runs (
APS5_PROFILE_DRAWand the process counters):In the
mainrun the game's working set rose from 3.2 to 8.2 GB between 10 and 14 s, during that first build, while its commit stayed at 11.8-12.1 GB: the scan allocating zero pages. A separate check on the same machine: reading 1 GiB of committed, never-written memory adds 1024 MiB to the working set.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)tools/pr_overlap.py: #15900 also editsWindowsWriteWatch.cpp(a different test); 14 conflicting PRs shareResidentMirror.cpp,GuestArena.cpporWindowsMappings.hppand could not be compared automatically; none of them adds a known-zero query (checked by hand: #13809, #15295, #15296, #15900).🤖 Generated with Claude Code, OpenCode, Command Code and Gemini