From 356a3a481c1ee9344fa9ee65f94c07c50d665b82 Mon Sep 17 00:00:00 2001 From: Elad <18193363+elad335@users.noreply.github.com> Date: Fri, 22 May 2026 09:36:46 +0300 Subject: [PATCH] PPU LLVM: Recycle current thread for exxecution Prevent invoking more than utils::get_max_threads() active thrreads at a time. Windows sees more than utils::get_max_threads() active threads as potentially malicious behavior and lowers the program's overall CPU time. --- rpcs3/Emu/Cell/PPUThread.cpp | 53 ++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index d3f31df73d..d291dad18e 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -4641,9 +4641,6 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s // Info to load to main JIT instance (true - compiled) std::vector> link_workload; - // Sync variable to acquire workloads - atomic_t work_cv = 0; - bool compiled_new = false; bool has_mfvscr = false; @@ -5250,12 +5247,16 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s // Create worker threads for compilation if (!workload.empty()) { + // Sync variable to acquire workloads + atomic_t work_cv = 0; + atomic_t work_done = 0; + // Update progress dialog g_progr_ptotal += ::size32(workload); *progress_dialog = get_localized_string(localized_string_id::PROGRESS_DIALOG_COMPILING_PPU_MODULES); - const u32 thread_count = std::min(::size32(workload), rpcs3::utils::get_max_threads()); + const u32 thread_count = std::max(std::min(::size32(workload), rpcs3::utils::get_max_threads()), 1) - 1; struct thread_index_allocator { @@ -5264,7 +5265,8 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s struct thread_op { - atomic_t& work_cv; + const std::add_pointer_t> work_cv; + const std::add_pointer_t> work_done; std::vector>>& workload; const ppu_module& main_module; const std::string& cache_path; @@ -5272,10 +5274,11 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s std::unique_lock core_lock; - thread_op(atomic_t& work_cv, std::vector>>& workload + thread_op(atomic_t* _work_cv, atomic_t* _work_done, std::vector>>& workload , const cpu_thread* cpu, const ppu_module& main_module, const std::string& cache_path, decltype(jit_core_allocator::sem)& sem) noexcept - : work_cv(work_cv) + : work_cv(_work_cv) + , work_done(_work_done) , workload(workload) , main_module(main_module) , cache_path(cache_path) @@ -5287,6 +5290,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s thread_op(const thread_op& other) noexcept : work_cv(other.work_cv) + , work_done(other.work_done) , workload(other.workload) , main_module(other.main_module) , cache_path(other.cache_path) @@ -5309,7 +5313,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s #ifdef __APPLE__ pthread_jit_write_protect_np(false); #endif - for (u32 i = work_cv++; i < workload.size(); i = work_cv++, g_progr_pdone++) + for (u32 i = (*work_cv)++; i < workload.size(); i = (*work_cv)++, (*work_done)++, g_progr_pdone++) { if (cpu ? cpu->state.all_of(cpu_flag::exit) : Emu.IsStopped()) { @@ -5331,17 +5335,20 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s } core_lock.unlock(); + + #ifdef __APPLE__ + pthread_jit_write_protect_np(true); + #endif } }; // Prevent watchdog thread from terminating g_watchdog_hold_ctr++; - named_thread_group threads(fmt::format("PPUW.%u.", ++g_fxo->get().index), thread_count - , thread_op(work_cv, workload, cpu, info, cache_path, g_fxo->get().sem) - , [&](u32 /*thread_index*/, thread_op& op) + const std::string worker_group_name = fmt::format("PPUW.%u.", ++g_fxo->get().index); + const auto try_lock_thread = [&](u32 thread_index, thread_op& op) { - const bool to_lock = work_cv < workload.size() && (cpu ? !cpu->state.all_of(cpu_flag::exit) : !Emu.IsStopped()); + const bool to_lock = (thread_index + *op.work_done) < workload.size() && (cpu ? !cpu->state.all_of(cpu_flag::exit) : !Emu.IsStopped()); if (!to_lock) { @@ -5352,7 +5359,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s op.core_lock.lock(); // Second check before creating another thread - const bool to_unlock = !(work_cv < workload.size() && (cpu ? !cpu->state.all_of(cpu_flag::exit) : !Emu.IsStopped())); + const bool to_unlock = !((thread_index + *op.work_done) < workload.size() && (cpu ? !cpu->state.all_of(cpu_flag::exit) : !Emu.IsStopped())); if (to_unlock) { @@ -5361,10 +5368,26 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s } return true; - }); + }; + + named_thread_group threads(worker_group_name, thread_count + , thread_op(&work_cv, &work_done, workload, cpu, info, cache_path, g_fxo->get().sem) + , try_lock_thread); + + const auto old_name = thread_ctrl::get_name(); + thread_ctrl::set_name(worker_group_name + std::to_string(thread_count + 1)); + + thread_op cur_op(&work_cv, &work_done, workload, cpu, info, cache_path, g_fxo->get().sem); + + if (try_lock_thread(thread_count, cur_op)) + { + // Recycle current thread: reduce overall thrread count + cur_op(); + } threads.join(); - + + thread_ctrl::set_name(old_name); g_watchdog_hold_ctr--; }