diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 802ebd65bc..926475d981 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -3437,8 +3437,24 @@ bool spu_thread::process_mfc_cmd() // 2. Increase the chance of change detection: if GETLLAR has been called again new data is probably wanted if (!g_cfg.core.spu_accurate_getllar || (rtime == vm::reservation_acquire(addr) && cmp_rdata(rdata, data))) { - // Validation that it is indeed GETLLAR busy-waiting (large time window is intentional) - if ((g_cfg.core.spu_reservation_busy_waiting && !g_use_rtm) || last_getllar != pc || perf0.get() - last_gtsc >= 50'000) + if ([&]() -> bool + { + // Validation that it is indeed GETLLAR spinning (large time window is intentional) + if (last_getllar != pc || perf0.get() - last_gtsc >= 50'000) + { + // Seemingly not + getllar_busy_waiting_switch = umax; + return true; + } + + if (getllar_busy_waiting_switch == umax) + { + // Evalute its value (shift-right to ensure its randomness with different CPUs) + getllar_busy_waiting_switch = !g_use_rtm && ((perf0.get() >> 8) % 100 < g_cfg.core.spu_reservation_busy_waiting_percentage); + } + + return !!getllar_busy_waiting_switch; + }()) { if (g_cfg.core.mfc_debug) { @@ -3451,7 +3467,7 @@ bool spu_thread::process_mfc_cmd() last_getllar = pc; last_gtsc = perf0.get(); - if (g_cfg.core.spu_reservation_busy_waiting) + if (getllar_busy_waiting_switch == true) { busy_wait(); } @@ -3578,6 +3594,7 @@ bool spu_thread::process_mfc_cmd() mov_rdata(_ref(ch_mfc_cmd.lsa & 0x3ff80), rdata); last_getllar = pc; last_gtsc = perf0.get(); + getllar_busy_waiting_switch = umax; ch_atomic_stat.set_value(MFC_GETLLAR_SUCCESS); @@ -4250,6 +4267,8 @@ s64 spu_thread::get_ch_value(u32 ch) } } + const bool reservation_busy_waiting = !g_use_rtm && ((utils::get_tsc() >> 8) % 100) < g_cfg.core.spu_reservation_busy_waiting_percentage; + for (; !events.count; events = get_events(mask1 & ~SPU_EVENT_LR, true, true)) { const auto old = +state; @@ -4275,10 +4294,10 @@ s64 spu_thread::get_ch_value(u32 ch) continue; } - if (raddr) + if (raddr && (mask1 & ~SPU_EVENT_TM) == SPU_EVENT_LR) { // Don't busy-wait with TSX - memory is sensitive - if (g_use_rtm || !g_cfg.core.spu_reservation_busy_waiting) + if (!reservation_busy_waiting) { atomic_wait_engine::set_one_time_use_wait_callback(mask1 != SPU_EVENT_LR ? nullptr : +[](u64) -> bool { diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 083c0fa98b..6d4c26120d 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -839,7 +839,8 @@ public: u64 last_fail = 0; u64 last_succ = 0; u64 last_gtsc = 0; - u32 last_getllar = umax; // LS address of last GETLLAR (if matches current GETLLAR we can let the thread rest) + u32 last_getllar = umax; // LS address of last GETLLAR (if matches current GETLLAR we can let the thread rest) + u32 getllar_busy_waiting_switch = umax; // umax means the test needs evaluation, otherwise it's a boolean std::vector mfc_history; u64 mfc_dump_idx = 0; diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index 9ed396d0c6..2f86c5bd4b 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -31,7 +31,7 @@ struct cfg_root : cfg::node cfg::_enum thread_scheduler{this, "Thread Scheduler Mode", thread_scheduler_mode::os}; cfg::_bool set_daz_and_ftz{ this, "Set DAZ and FTZ", false }; cfg::_enum spu_decoder{ this, "SPU Decoder", spu_decoder_type::llvm }; - cfg::_bool spu_reservation_busy_waiting{ this, "SPU Reservation Busy Waiting", false, true }; + cfg::uint<0, 100> spu_reservation_busy_waiting_percentage{ this, "SPU Reservation Busy Waiting Percentage", 0, true }; cfg::_bool spu_debug{ this, "SPU Debug" }; cfg::_bool mfc_debug{ this, "MFC Debug" }; cfg::_int<0, 6> preferred_spu_threads{ this, "Preferred SPU Threads", 0, true }; // Number of hardware threads dedicated to heavy simultaneous spu tasks