mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-21 02:09:05 +02:00
SaveState: Only rehash functions in code a load changed
Every load hashed all known functions again, for the replacement hooks: 2.4-6 ms, about half the load. Functions in module code the load left as it was keep their hash, so only the rest is hashed now (0.5-1 ms). The code is compared on every load for this, not only with a JIT. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
a62aa69986
commit
f45f4d5c85
5 files changed
+43
-8
No files matched your search
@@ -534,6 +534,13 @@ static void __KernelModuleInit() {
|
||||
eventModuleLoadDone = CoreTiming::RegisterEvent("LoadModuleDone", &__KernelLoadModuleDone);
|
||||
}
|
||||
|
||||
// Set for the next load by the savestate code: code the load left as it was.
|
||||
static std::vector<std::pair<u32, u32>> g_unchangedCodeOnLoad;
|
||||
|
||||
void __KernelModuleSetUnchangedCodeOnLoad(std::vector<std::pair<u32, u32>> unchanged) {
|
||||
g_unchangedCodeOnLoad = std::move(unchanged);
|
||||
}
|
||||
|
||||
std::vector<std::pair<u32, u32>> __KernelModuleCodeRanges() {
|
||||
std::vector<std::pair<u32, u32>> ranges;
|
||||
u32 error;
|
||||
@@ -615,7 +622,9 @@ void __KernelModuleDoState(PointerWrap &p) {
|
||||
}
|
||||
// The functions were found in memory from before the load, where other code may have been
|
||||
// (an overlay module, say.) Hash them from what's there now, so a hook only goes where it matches.
|
||||
MIPSAnalyst::RehashFunctions();
|
||||
// Only what the load may have changed: hashing it all again takes milliseconds.
|
||||
MIPSAnalyst::RehashFunctions(g_unchangedCodeOnLoad);
|
||||
g_unchangedCodeOnLoad.clear();
|
||||
if (g_Config.bFuncReplacements) {
|
||||
MIPSAnalyst::ReplaceFunctions();
|
||||
}
|
||||
|
||||
@@ -231,6 +231,9 @@ KernelObject *__KernelModuleObject();
|
||||
void __KernelModuleDoState(PointerWrap &p);
|
||||
// The code of the loaded modules, as (address, size).
|
||||
std::vector<std::pair<u32, u32>> __KernelModuleCodeRanges();
|
||||
// For the next savestate load: code it leaves as it was, as (address, size). Its functions aren't
|
||||
// hashed again.
|
||||
void __KernelModuleSetUnchangedCodeOnLoad(std::vector<std::pair<u32, u32>> unchanged);
|
||||
void __KernelModuleShutdown();
|
||||
|
||||
class MIPSState;
|
||||
|
||||
@@ -909,7 +909,22 @@ namespace MIPSAnalyst {
|
||||
return DetermineRegisterUsage(reg, addr, instrs) == USAGE_CLOBBERED;
|
||||
}
|
||||
|
||||
void HashFunctions() {
|
||||
// Ranges are (address, size), sorted by address and not overlapping.
|
||||
static bool InsideRanges(const std::vector<std::pair<u32, u32>> &ranges, u32 start, u32 end) {
|
||||
start &= 0x3FFFFFFF;
|
||||
end &= 0x3FFFFFFF;
|
||||
auto iter = std::upper_bound(ranges.begin(), ranges.end(), start, [](u32 address, const std::pair<u32, u32> &range) {
|
||||
return address < (range.first & 0x3FFFFFFF);
|
||||
});
|
||||
if (iter == ranges.begin()) {
|
||||
return false;
|
||||
}
|
||||
--iter;
|
||||
const u32 rangeStart = iter->first & 0x3FFFFFFF;
|
||||
return start >= rangeStart && end <= rangeStart + iter->second;
|
||||
}
|
||||
|
||||
static void HashFunctions(const std::vector<std::pair<u32, u32>> &unchanged = {}) {
|
||||
std::lock_guard<std::recursive_mutex> guard(functions_lock);
|
||||
std::vector<u32> buffer;
|
||||
|
||||
@@ -918,6 +933,10 @@ namespace MIPSAnalyst {
|
||||
if (!Memory::IsValidRange(f.start, f.end - f.start + 4)) {
|
||||
continue;
|
||||
}
|
||||
// end is the last instruction.
|
||||
if (f.hasHash && InsideRanges(unchanged, f.start, f.end + 4)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Immediates are masked out, so relocations don't change the hash.
|
||||
buffer.resize((f.end - f.start + 4) / 4);
|
||||
@@ -1311,9 +1330,11 @@ namespace MIPSAnalyst {
|
||||
return false;
|
||||
}
|
||||
|
||||
void RehashFunctions() {
|
||||
void RehashFunctions(const std::vector<std::pair<u32, u32>> &unchanged) {
|
||||
std::vector<std::pair<u32, u32>> sorted = unchanged;
|
||||
std::sort(sorted.begin(), sorted.end());
|
||||
std::lock_guard<std::recursive_mutex> guard(functions_lock);
|
||||
HashFunctions();
|
||||
HashFunctions(sorted);
|
||||
UpdateHashToFunctionMap();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
@@ -118,7 +119,9 @@ namespace MIPSAnalyst {
|
||||
|
||||
const char *LookupHash(u64 hash, u32 funcSize);
|
||||
// Hashes the known functions again, from what memory holds now (after a savestate load.)
|
||||
void RehashFunctions();
|
||||
// Functions that already have a hash and lie entirely in one of the (address, size) ranges in
|
||||
// unchanged keep it: their code is what it was hashed from.
|
||||
void RehashFunctions(const std::vector<std::pair<u32, u32>> &unchanged = {});
|
||||
void ReplaceFunctions();
|
||||
|
||||
void UpdateHashMap();
|
||||
|
||||
+2
-3
@@ -235,13 +235,12 @@ int g_screenshotFailures;
|
||||
if (p.mode == p.MODE_READ) {
|
||||
hookedOps = Replacement_GetHookedOps();
|
||||
RestoreReplacedInstructions(0, 0xFFFFFFFF);
|
||||
if (MIPSComp::jit) {
|
||||
codeCompare.ranges = __KernelModuleCodeRanges();
|
||||
}
|
||||
codeCompare.ranges = __KernelModuleCodeRanges();
|
||||
}
|
||||
Memory::DoState(p, &codeCompare);
|
||||
if (p.mode == p.MODE_READ) {
|
||||
Replacement_RepairStaleHookOps(hookedOps);
|
||||
__KernelModuleSetUnchangedCodeOnLoad(codeCompare.unchanged);
|
||||
currentMIPS->KeepJitBlocksOnNextLoad(std::move(codeCompare.unchanged));
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user