CELL: Rewrite reservation notification postponing

This commit is contained in:
Elad 2024-11-30 10:46:33 +02:00
parent 729826ec40
commit a4ea71d18f
6 changed files with 60 additions and 33 deletions

View File

@ -3541,49 +3541,62 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value)
// Avoid notifications from lwmutex or sys_spinlock
if (new_data != old_data && (ppu.cia < liblv2_begin || ppu.cia >= liblv2_end))
{
const u32 notify = ppu.res_notify;
u32 notify = ppu.res_notify;
if (notify)
{
bool notified = false;
if (ppu.res_notify_time == (vm::reservation_acquire(notify) & -128))
if (ppu.res_notify_time == vm::reservation_notifier_count_index(notify).second)
{
ppu.state += cpu_flag::wait;
vm::reservation_notifier_notify(notify);
notified = true;
}
if (vm::reservation_notifier_count(addr))
{
if (!notified)
{
ppu.res_notify = addr;
ppu.res_notify_time = rtime + 128;
}
else if ((addr ^ notify) & -128)
{
vm::reservation_notifier_notify(addr);
ppu.res_notify = 0;
}
}
else
{
ppu.res_notify = 0;
notify = 0;
}
static_cast<void>(ppu.test_stopped());
ppu.res_notify = 0;
}
else
if ((addr ^ notify) & -128)
{
// Try to postpone notification to when PPU is asleep or join notifications on the same address
// This also optimizes a mutex - won't notify after lock is aqcuired (prolonging the critical section duration), only notifies on unlock
if (vm::reservation_notifier_count(addr))
const auto [count, index] = vm::reservation_notifier_count_index(addr);
switch (count)
{
ppu.res_notify = addr;
ppu.res_notify_time = rtime + 128;
case 0:
{
// Nothing to do
break;
}
case 1:
{
if (!notify)
{
ppu.res_notify = addr;
ppu.res_notify_time = index;
break;
}
// Notify both
[[fallthrough]];
}
default:
{
if (!notify)
{
ppu.state += cpu_flag::wait;
}
vm::reservation_notifier_notify(addr);
break;
}
}
}
static_cast<void>(ppu.test_stopped());
}
if (addr == ppu.last_faddr)
@ -3601,7 +3614,7 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value)
// And on failure it has some time to do something else
if (notify && ((addr ^ notify) & -128))
{
if (ppu.res_notify_time == (vm::reservation_acquire(notify) & -128))
if (ppu.res_notify_time == vm::reservation_notifier_count_index(notify).second)
{
ppu.state += cpu_flag::wait;
vm::reservation_notifier_notify(notify);

View File

@ -446,7 +446,7 @@ waitpkg_func static void __tpause(u32 cycles, u32 cstate)
namespace vm
{
std::array<atomic_t<reservation_waiter_t>, 512> g_resrv_waiters_count{};
std::array<atomic_t<reservation_waiter_t>, 1024> g_resrv_waiters_count{};
}
void do_cell_atomic_128_store(u32 addr, const void* to_write);

View File

@ -1335,7 +1335,7 @@ bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout)
{
static_cast<ppu_thread&>(cpu).res_notify = 0;
if (static_cast<ppu_thread&>(cpu).res_notify_time != (vm::reservation_acquire(addr) & -128))
if (static_cast<ppu_thread&>(cpu).res_notify_time != vm::reservation_notifier_count_index(addr).second)
{
// Ignore outdated notification request
}
@ -1388,7 +1388,7 @@ bool lv2_obj::awake(cpu_thread* thread, s32 prio)
{
ppu->res_notify = 0;
if (ppu->res_notify_time != (vm::reservation_acquire(addr) & -128))
if (ppu->res_notify_time != vm::reservation_notifier_count_index(addr).second)
{
// Ignore outdated notification request
}

View File

@ -45,14 +45,24 @@ namespace vm
static inline std::pair<atomic_t<reservation_waiter_t>*, atomic_t<reservation_waiter_t>*> reservation_notifier(u32 raddr)
{
extern std::array<atomic_t<reservation_waiter_t>, 512> g_resrv_waiters_count;
extern std::array<atomic_t<reservation_waiter_t>, 1024> g_resrv_waiters_count;
// Storage efficient method to distinguish different nearby addresses (which are likely)
const usz index = std::popcount(raddr & -512) + ((raddr / 128) % 4) * 32;
auto& waiter = g_resrv_waiters_count[index * 4];
return { &g_resrv_waiters_count[index * 4 + waiter.load().waiters_index % 4], &waiter };
constexpr u32 wait_vars_for_each = 8;
constexpr u32 unique_address_bit_mask = 0b11;
const usz index = std::popcount(raddr & -1024) + ((raddr / 128) & unique_address_bit_mask) * 32;
auto& waiter = g_resrv_waiters_count[index * wait_vars_for_each];
return { &g_resrv_waiters_count[index * wait_vars_for_each + waiter.load().waiters_index % wait_vars_for_each], &waiter };
}
// Returns waiter count and index
static inline std::pair<u32, u32> reservation_notifier_count_index(u32 raddr)
{
const auto notifiers = reservation_notifier(raddr);
return { notifiers.first->load().waiters_count, static_cast<u32>(notifiers.first - notifiers.second) };
}
// Returns waiter count
static inline u32 reservation_notifier_count(u32 raddr)
{
return reservation_notifier(raddr).first->load().waiters_count;

View File

@ -947,6 +947,7 @@
<ClInclude Include="Emu\Memory\vm.h" />
<ClInclude Include="Emu\Memory\vm_ptr.h" />
<ClInclude Include="Emu\Memory\vm_ref.h" />
<ClInclude Include="Emu\Memory\vm_reservation.h" />
<ClInclude Include="Emu\Memory\vm_var.h" />
<ClInclude Include="Emu\RSX\rsx_methods.h" />
<ClInclude Include="Emu\RSX\rsx_utils.h" />

View File

@ -1461,6 +1461,9 @@
<ClInclude Include="Emu\Memory\vm_ref.h">
<Filter>Emu\Memory</Filter>
</ClInclude>
<ClInclude Include="Emu\Memory\vm_reservation.h">
<Filter>Emu\Memory</Filter>
</ClInclude>
<ClInclude Include="Emu\Memory\vm_var.h">
<Filter>Emu\Memory</Filter>
</ClInclude>