From 8df40f6579f8e35e9c3bc5434e48a7b095c11460 Mon Sep 17 00:00:00 2001 From: nahshongraham97 Date: Fri, 9 Oct 2026 19:27:18 -0600 Subject: [PATCH] perf(libkernel): shard SyncOnAddress waiters across hashed buckets A single global waiter lock serializes unrelated sceKernelSyncOnAddressWait/Wake calls. Hash the address into 64 cache-line-aligned buckets so unrelated waiters no longer contend, without changing wake semantics. --- .../SyncOnAddress/src/SyncOnAddress.cpp | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp b/core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp index f75b779338..563c67f585 100644 --- a/core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp +++ b/core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -22,8 +23,19 @@ struct AddressWaiter { bool woken = false; }; -std::mutex g_waitersLock; -std::unordered_map> g_waiters; +constexpr std::size_t kBucketCount = 64; + +struct alignas(64) WaiterBucket { + std::mutex mutex; + std::unordered_map> waiters; +}; + +std::array g_buckets; + +WaiterBucket& BucketOf(std::uintptr_t address) { + const auto hashed = (address >> 2) * UINT64_C(0x9e3779b97f4a7c15); + return g_buckets[hashed >> 58]; +} template bool IsAlignedAddress(std::uintptr_t address) { @@ -33,13 +45,14 @@ bool IsAlignedAddress(std::uintptr_t address) { template int WaitOnAddress(TValue* address, TValue expected, const KernelUseconds* timeout, const void* caller) { const auto key = reinterpret_cast(address); - std::unique_lock lock(g_waitersLock); + auto& bucket = BucketOf(key); + std::unique_lock lock(bucket.mutex); if (std::atomic_ref(*address).load() != expected) { return SYNC_ON_ADDRESS_OK; } AddressWaiter waiter; - auto& queue = g_waiters[key]; + auto& queue = bucket.waiters[key]; const auto position = queue.insert(queue.end(), &waiter); const auto isWoken = [&] { return waiter.woken; }; const auto waitStart = std::chrono::steady_clock::now(); @@ -54,7 +67,7 @@ int WaitOnAddress(TValue* address, TValue expected, const KernelUseconds* timeou if (!woken) { queue.erase(position); if (queue.empty()) { - g_waiters.erase(key); + bucket.waiters.erase(key); } } lock.unlock(); @@ -112,9 +125,10 @@ int APS5_VABI sceKernelSyncOnAddressWake(void* address, std::int32_t count) { APS5_INVALID_ARG_EX; } - std::lock_guard lock(g_waitersLock); - const auto entry = g_waiters.find(key); - if (entry == g_waiters.end()) { + auto& bucket = BucketOf(key); + std::lock_guard lock(bucket.mutex); + const auto entry = bucket.waiters.find(key); + if (entry == bucket.waiters.end()) { return SYNC_ON_ADDRESS_OK; } auto& queue = entry->second; @@ -125,7 +139,7 @@ int APS5_VABI sceKernelSyncOnAddressWake(void* address, std::int32_t count) { waiter->condition.NotifyOne(); } if (queue.empty()) { - g_waiters.erase(entry); + bucket.waiters.erase(entry); } return SYNC_ON_ADDRESS_OK; }