Merge pull request #8896 from teepean/perf/recent-host-imports

perf(agc): find a host import among the last ones found first
This commit is contained in:
Dean authored and GitHub committed 2026-10-27 10:52:34 +13:00
commit 910e14f3c9
3 files changed
+55

No files matched your search

@@ -297,6 +297,10 @@ struct HostImports {
std::uint8_t reportNoted = 0;
std::map<std::uint64_t, std::uint64_t> spaceUses;
std::uint64_t budgetRefusals = 0;
std::array<const HostImport*, 4> recentImports{};
std::uint64_t recentEpoch = std::numeric_limits<std::uint64_t>::max();
std::size_t recentSize = 0;
std::size_t recentNext = 0;
};
HostImports& Imports() {
@@ -304,6 +308,31 @@ HostImports& Imports() {
return imports;
}
bool recentImportsEnabled() {
static const bool enabled = std::getenv("APS5_NO_RECENT_IMPORTS") == nullptr;
return enabled;
}
const HostImport* recentImport(HostImports& state, std::uint64_t begin, std::uint64_t end) {
if (state.recentEpoch != state.epoch || state.recentSize != state.imports.size()) return nullptr;
for (const auto* entry : state.recentImports) {
if (entry != nullptr && begin >= entry->base && end <= entry->base + entry->bytes) {
entry->lastUse = ++state.useCounter;
return entry;
}
}
return nullptr;
}
void rememberImport(HostImports& state, const HostImport* entry) {
if (state.recentEpoch != state.epoch || state.recentSize != state.imports.size()) {
state.recentImports = {};
state.recentEpoch = state.epoch;
state.recentSize = state.imports.size();
}
state.recentImports[state.recentNext++ % state.recentImports.size()] = entry;
}
#ifdef _WIN32
void waitSettleBatch(const ImportSettle& settle, VkDevice device, const SettleBatch& batch) {
if (batch.busy && settle.waitForFences != nullptr) Check(settle.waitForFences(device, 1, &batch.fence, VK_TRUE, 60'000'000'000ull), "vkWaitForFences (settle batch)");
@@ -4023,8 +4052,15 @@ const HostImport* HostImportFor(const Context& context, std::uint64_t address, s
#endif
// A hit is only valid while the registry has not changed since the imports were reconciled.
if (!importsStale(context, state)) {
if (recentImportsEnabled()) {
if (const auto* entry = recentImport(state, address, address + bytes)) {
entry->lastUse = ++state.useCounter;
return entry;
}
}
if (const auto* entry = findImport(state, address, address + bytes)) {
entry->lastUse = ++state.useCounter;
if (recentImportsEnabled()) rememberImport(state, entry);
return entry;
}
}
@@ -360,6 +360,23 @@ void BusyRetireOutlivesALaterBatch() {
Expect(!Live(retired), "the dropped import outlived every batch of its hold");
}
void RecentImportFollowsReplacement() {
Heap heap;
ActiveRecorder active;
const auto context = mockContext();
std::lock_guard gpu(AgcDriver::GuestMemory::GpuMutex());
const auto* first = HostImportFor(context, heap.Address(), 4096);
Expect(first != nullptr, "the first range was not imported");
if (first == nullptr) return;
Expect(HostImportFor(context, heap.Address(), 4096) == first, "a repeated lookup did not find the same import");
const auto retired = first->buffer;
heap.Grow();
const auto* grown = HostImportFor(context, heap.Address(RangeBytes), 4096);
Expect(grown != nullptr && grown->buffer != retired, "the grown run was not imported as a new import");
if (grown == nullptr) return;
Expect(HostImportFor(context, heap.Address(), 4096) == grown, "a lookup after the replacement served the retired import instead of the one that replaced it");
}
}
int main() {
@@ -374,6 +391,7 @@ int main() {
IdleRetireOutlivesTheHold();
BusyRetireOutlivesALaterBatch();
AgcDriver::Tests::RunHostImportFailureTests(mockContext(), false);
RecentImportFollowsReplacement();
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());
return 1;
+1
View File
@@ -305,6 +305,7 @@ The code reads environment names through `getenv`, `IsEnvironmentSet`, `kibSetti
| `APS5_NO_PROC_TABLE` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp` | | resolves the device's Vulkan entry points per call instead of filling the function table once |
| `APS5_NO_QUEUED_DRAW_READ_ORDER` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawPipeline.cpp` | | stops queued draws and DMA copies from holding back write-backs over the guest memory they read at commit |
| `APS5_NO_QUEUED_LABELS` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/Driver/Synchronization/DeferredLabels.cpp` | | disables the queued-label table |
| `APS5_NO_RECENT_IMPORTS` | presence | `core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp` | | looks every host import up in the import map instead of first checking the last four found while the map is unchanged |
| `APS5_NO_RECORDED_CLEAR` | presence | `core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp` | | waits for a storage image's fast clear in a batch of its own instead of recording it |
| `APS5_NO_RECORDER_SYNC_DRAWS` | presence | `core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp` | | gives the draws the recorder waits for their own batch instead of recording them with the wait |
| `APS5_NO_RECORD_COPIED_DRAWS` | presence | `core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp` | | waits for copied-write draws at once instead of listing their write-back for later |