From 44767a7b97b918d215f5dd700231bc1055943611 Mon Sep 17 00:00:00 2001 From: Niko Date: Tue, 13 Oct 2026 01:22:35 +0200 Subject: [PATCH] perf(libc): size each write-watch query to the region it covers 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 --- .../libs/prx/libc/include/WindowsMappings.hpp | 5 ++- core/libs/tests/GuestMemory.cpp | 38 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libc/include/WindowsMappings.hpp b/core/libs/prx/libc/include/WindowsMappings.hpp index d93c344e62..2bd449f022 100644 --- a/core/libs/prx/libc/include/WindowsMappings.hpp +++ b/core/libs/prx/libc/include/WindowsMappings.hpp @@ -346,8 +346,9 @@ public: const auto memory = query(cursor); if (memory.State != MEM_COMMIT || memory.Type != MEM_PRIVATE) return false; const auto stop = std::min(end, reinterpret_cast(memory.BaseAddress) + memory.RegionSize); - ULONG_PTR available = capacity - *count; - if (available == 0) return true; + if (capacity == *count) return true; + const auto touched = (((stop + 4095) & ~static_cast(4095)) - (cursor & ~static_cast(4095))) / 4096; + ULONG_PTR available = std::min(capacity - *count, touched); DWORD granularity = 0; if (GetWriteWatch(clear ? WRITE_WATCH_FLAG_RESET : 0, reinterpret_cast(cursor), stop - cursor, pages + *count, &available, &granularity) != 0) fail("collect private guest writes"); *count += available; diff --git a/core/libs/tests/GuestMemory.cpp b/core/libs/tests/GuestMemory.cpp index 9a12142d84..5c0916db99 100644 --- a/core/libs/tests/GuestMemory.cpp +++ b/core/libs/tests/GuestMemory.cpp @@ -1492,6 +1492,43 @@ static void CheckFailedCollectKeepsWrites() { #endif } +static void CheckSplitPrivateCollect() { +#ifdef _WIN32 + constexpr std::size_t granule = 0x4000; + constexpr std::size_t regions = 8; + constexpr std::size_t bytes = granule * regions; + constexpr std::size_t pages = bytes / 4096; + auto* block = static_cast(GuestArena::GuestArenaAllocate_nid_postfix(bytes, granule)); + GuestArena::GuestArenaCommit_nid_postfix(block, bytes, PAGE_READWRITE, granule); + const auto address = reinterpret_cast(block); + const auto collect = [&](std::size_t capacity) { + std::vector found; + std::array buffer{}; + for (auto cursor = address; cursor < address + bytes;) { + std::size_t count = capacity; + Require(GuestArena::GuestArenaCollectWrites_nid_postfix(cursor, address + bytes - cursor, buffer.data(), &count, true)); + for (std::size_t i = 0; i < count; ++i) found.push_back(reinterpret_cast(buffer[i])); + if (count < capacity) break; + cursor = found.back() + 4096; + } + return found; + }; + collect(pages); + for (const std::size_t capacity : {pages, std::size_t{5}, std::size_t{1}}) { + for (std::size_t page = 0; page < pages; ++page) static_cast(block)[page * 4096 + 7] = static_cast(capacity + page); + const auto found = collect(capacity); + Require(found.size() == pages); + for (std::size_t page = 0; page < pages; ++page) Require(found[page] == address + page * 4096); + Require(collect(pages).empty()); + } + static_cast(block)[granule * 3 + 4096 + 1] = 9; + const auto one = collect(pages); + Require(one.size() == 1 && one[0] == address + granule * 3 + 4096); + GuestArena::GuestArenaReset_nid_postfix(block, bytes); + GuestArena::GuestArenaRelease_nid_postfix(block, bytes); +#endif +} + static void CheckPinnedSharedPages() { #ifdef _WIN32 constexpr std::size_t page = 0x4000; @@ -2156,6 +2193,7 @@ int main() { CheckFixedMappingReplacesPartialOverlap(); CheckFixedMappingOverHoleInsideAllocation(); CheckDirectMemoryGpuProtBits(); + CheckSplitPrivateCollect(); CheckGpuAccessFollowsProtection(); CheckGpuMapsAreObserved(); CheckClearingTheObserverWaitsForItsCalls();