aboutsummaryrefslogtreecommitdiff
path: root/src/common/host_memory.h
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-30 04:39:40 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-30 04:40:24 -0400
commitf260bcb5a26a6b6b9675b41969a273858f024c55 (patch)
treeefbfa56ed06b1148caca6d4e1a21b56bed6ddde6 /src/common/host_memory.h
parent0218eacfd560d0ce48b3ffe64d279ee85bbf1347 (diff)
memory: skip clearing guest memory that is still zero
The kernel clears every page it hands out, and the backing memory is a page-file section the host only gives RAM to once a page is touched. So the clears themselves made every page of a game's heap resident: Mario Kart World (12 GB layout) had a 12.5 GB working set 20 s after boot. HostMemory keeps two bits per 64 KiB of backing: used (handed out by the kernel at least once) and dirty (may hold data). Pages returned to the kernel mark their granules dirty, and a zero clear only touches dirty granules; a granule cleared in full is clean again. A new kernel on the same memory marks everything a previous one used dirty, so leaked processes can't leak data into the next game. KMemoryManager's optimized-process fill and KSharedMemory's clear go through the same path. Mario Kart World, title screen and attract demo, 180 s: working set 20 s in 4.7-6.4 GB instead of 12.5 GB, peak 14-17.6 GB instead of 20.1 GB.
Diffstat (limited to 'src/common/host_memory.h')
-rw-r--r--src/common/host_memory.h18
1 files changed, 18 insertions, 0 deletions
diff --git a/src/common/host_memory.h b/src/common/host_memory.h
index 8747a1850f..3e0142eed8 100644
--- a/src/common/host_memory.h
+++ b/src/common/host_memory.h
@@ -6,6 +6,7 @@
#pragma once
+#include <atomic>
#include <memory>
#include <optional>
#include "common/common_funcs.h"
@@ -52,8 +53,19 @@ public:
void EnableDirectMappedAddress();
+ /// Clears guest memory being handed out. Memory still zero since it was created (or since its
+ /// last full clear) is skipped: clearing it only made the host give every page real memory.
void ClearBackingRegion(size_t physical_offset, size_t length, u32 fill_value);
+ /// Pages the kernel hands out (cleared or not).
+ void MarkUsed(size_t physical_offset, size_t length);
+
+ /// Pages given back to the kernel: their contents are no longer known to be zero.
+ void MarkDirty(size_t physical_offset, size_t length);
+
+ /// A new kernel on this memory: everything a previous one used may hold data.
+ void ForgetContents();
+
[[nodiscard]] u8* BackingBasePointer() noexcept {
return backing_base;
}
@@ -88,6 +100,12 @@ private:
u8* backing_base{};
u8* virtual_base{};
size_t virtual_base_offset{};
+
+ static constexpr size_t GranuleShift = 16; ///< 64 KiB of backing per state byte
+ static constexpr u8 GranuleUsed = 1 << 0; ///< Handed out at least once
+ static constexpr u8 GranuleDirty = 1 << 1; ///< May hold non-zero data
+ std::unique_ptr<std::atomic<u8>[]> granules;
+ size_t num_granules{};
// Windows requires it for kernels whom lack proper support for some functions!
bool fallback_buffer{false};
};