@@ -1307,6 +1307,8 @@ static std::deque<class cpu_thread*> g_to_sleep;
13071307static atomic_t <bool > g_scheduler_ready = false ;
13081308static atomic_t <u64 > s_yield_frequency = 0 ;
13091309static atomic_t <u64 > s_max_allowed_yield_tsc = 0 ;
1310+ static atomic_t <u64 > s_lv2_timers_sum_of_ten_delay_in_us = 5000 ;
1311+ static atomic_t <u64 > s_lv2_timers_min_timer_in_us = 0 ;
13101312static u64 s_last_yield_tsc = 0 ;
13111313atomic_t <u32 > g_lv2_preempts_taken = 0 ;
13121314
@@ -1432,7 +1434,7 @@ bool lv2_obj::awake(cpu_thread* thread, s32 prio)
14321434
14331435 if (!g_postpone_notify_barrier)
14341436 {
1435- notify_all ();
1437+ notify_all (thread );
14361438 }
14371439
14381440 return result;
@@ -1573,6 +1575,11 @@ bool lv2_obj::sleep_unlocked(cpu_thread& thread, u64 timeout, u64 current_time)
15731575 {
15741576 if (it == end || it->first > wait_until)
15751577 {
1578+ if (it == g_waiting.cbegin ())
1579+ {
1580+ s_lv2_timers_min_timer_in_us.release (wait_until);
1581+ }
1582+
15761583 g_waiting.emplace (it, wait_until, &thread);
15771584 break ;
15781585 }
@@ -1835,6 +1842,8 @@ void lv2_obj::cleanup()
18351842 g_waiting.clear ();
18361843 g_pending = 0 ;
18371844 s_yield_frequency = 0 ;
1845+ s_lv2_timers_sum_of_ten_delay_in_us = 5000 ;
1846+ s_lv2_timers_min_timer_in_us = 0 ;
18381847}
18391848
18401849void lv2_obj::schedule_all (u64 current_time)
@@ -1876,7 +1885,7 @@ void lv2_obj::schedule_all(u64 current_time)
18761885 }
18771886
18781887 // Check registered timeouts
1879- while (!g_waiting.empty ())
1888+ while (!g_waiting.empty () && it != std::end (g_to_notify) )
18801889 {
18811890 const auto pair = &g_waiting.front ();
18821891
@@ -1896,15 +1905,7 @@ void lv2_obj::schedule_all(u64 current_time)
18961905 ensure (!target->state .test_and_set (cpu_flag::notify));
18971906
18981907 // Otherwise notify it to wake itself
1899- if (it == std::end (g_to_notify))
1900- {
1901- // Out of notification slots, notify locally (resizable container is not worth it)
1902- target->state .notify_one ();
1903- }
1904- else
1905- {
1906- *it++ = &target->state ;
1907- }
1908+ *it++ = &target->state ;
19081909 }
19091910 }
19101911 else
@@ -2171,7 +2172,36 @@ bool lv2_obj::wait_timeout(u64 usec, ppu_thread* cpu, bool scale, bool is_usleep
21712172#endif
21722173 // TODO: Tune for other non windows operating sytems
21732174
2174- if (g_cfg.core .sleep_timers_accuracy < (is_usleep ? sleep_timers_accuracy_level::_usleep : sleep_timers_accuracy_level::_all_timers))
2175+ const sleep_timers_accuracy_level accuarcy_type = g_cfg.core .sleep_timers_accuracy ;
2176+ const u64 avg_delay = get_avg_timer_reponse_delay ();
2177+
2178+ static atomic_t <u64 > g_success = 0 ;
2179+ static atomic_t <u64 > g_fails = 0 ;
2180+
2181+ if (accuarcy_type == sleep_timers_accuracy_level::_dynamic && avg_delay < 30 && avg_delay < (remaining + 15 ) / 2 )
2182+ {
2183+ wait_for (remaining);
2184+
2185+ if (remaining < host_min_quantum)
2186+ {
2187+ g_success += remaining;
2188+ // g_success++;
2189+ }
2190+
2191+ passed = get_system_time () - start_time;
2192+
2193+
2194+ continue ;
2195+ }
2196+ else
2197+ {
2198+ if (remaining < host_min_quantum)
2199+ {
2200+ g_fails += remaining;
2201+ // g_fails++;
2202+ }
2203+ }
2204+ if (g_cfg.core .sleep_timers_accuracy < (is_usleep ? sleep_timers_accuracy_level::_dynamic : sleep_timers_accuracy_level::_all_timers))
21752205 {
21762206 wait_for (remaining);
21772207 }
@@ -2222,7 +2252,7 @@ void lv2_obj::prepare_for_sleep(cpu_thread& cpu)
22222252 cpu_counter::remove (&cpu);
22232253}
22242254
2225- void lv2_obj::notify_all () noexcept
2255+ void lv2_obj::notify_all (cpu_thread* woke_thread ) noexcept
22262256{
22272257 for (auto cpu : g_to_notify)
22282258 {
@@ -2258,13 +2288,11 @@ void lv2_obj::notify_all() noexcept
22582288 return ;
22592289 }
22602290
2261- if (cpu->get_class () != thread_class::spu && cpu->state .none_of (cpu_flag::suspend))
2291+ if (cpu->get_class () == thread_class::ppu && cpu->state .none_of (cpu_flag::suspend + cpu_flag::signal ))
22622292 {
22632293 return ;
22642294 }
22652295
2266- std::optional<vm::writer_lock> lock;
2267-
22682296 constexpr usz total_waiters = std::size (spu_thread::g_spu_waiters_by_value);
22692297
22702298 u32 notifies[total_waiters]{};
@@ -2346,4 +2374,82 @@ void lv2_obj::notify_all() noexcept
23462374 vm::reservation_notifier_notify (addr);
23472375 }
23482376 }
2377+
2378+ if (woke_thread == cpu)
2379+ {
2380+ return ;
2381+ }
2382+
2383+ const u64 min_timer = s_lv2_timers_min_timer_in_us;
2384+ const u64 current_time = get_guest_system_time ();
2385+
2386+ if (current_time < min_timer)
2387+ {
2388+ return ;
2389+ }
2390+
2391+ atomic_bs_t <cpu_flag>* notifies_cpus[16 ];
2392+ usz count_notifies_cpus = 0 ;
2393+
2394+ std::unique_lock lock (g_mutex, std::try_to_lock);
2395+
2396+ if (!lock)
2397+ {
2398+ // Not only is that this method is an opportunistic optimization
2399+ // But if it's already locked than it is likely that soon another thread would do this check instead
2400+ return ;
2401+ }
2402+
2403+ // Do it BEFORE clearing the queue in order to measure the delay properly even if the sleeping thread notified itself
2404+ // This 'redundancy' is what allows proper measurements
2405+ if (u64 min_time2 = s_lv2_timers_min_timer_in_us; current_time >= min_time2)
2406+ {
2407+ const u64 sum = s_lv2_timers_sum_of_ten_delay_in_us.observe ();
2408+ s_lv2_timers_sum_of_ten_delay_in_us.release (sum - sum / 10 + (current_time - min_time2) / 10 );
2409+ }
2410+
2411+ // Check registered timeouts
2412+ while (!g_waiting.empty () && count_notifies_cpus < std::size (notifies_cpus))
2413+ {
2414+ const auto pair = &g_waiting.front ();
2415+
2416+ if (pair->first <= current_time)
2417+ {
2418+ const auto target = pair->second ;
2419+ g_waiting.pop_front ();
2420+
2421+ if (target != cpu)
2422+ {
2423+ // Change cpu_thread::state for the lightweight notification to work
2424+ ensure (!target->state .test_and_set (cpu_flag::notify));
2425+ notifies_cpus[count_notifies_cpus++] = &target->state ;
2426+ }
2427+ }
2428+ else
2429+ {
2430+ // The list is sorted so assume no more timeouts
2431+ break ;
2432+ }
2433+ }
2434+
2435+ if (g_waiting.empty ())
2436+ {
2437+ s_lv2_timers_min_timer_in_us.release (u64 {umax});
2438+ }
2439+ else
2440+ {
2441+ s_lv2_timers_min_timer_in_us.release (g_waiting.front ().first );
2442+ }
2443+
2444+ lock.unlock ();
2445+
2446+ for (usz i = count_notifies_cpus - 1 ; i != umax; i--)
2447+ {
2448+ atomic_wait_engine::notify_one (notifies_cpus[i]);
2449+ }
2450+ }
2451+
2452+ u64 lv2_obj::get_avg_timer_reponse_delay ()
2453+ {
2454+ return s_lv2_timers_sum_of_ten_delay_in_us / 10 ;
23492455}
0 commit comments