SPU: SPURS pause based average task order duration

This commit is contained in:
Elad Ashkenazi 2024-10-11 09:02:20 +03:00 committed by Elad
parent fa707047e6
commit 8fac136056
2 changed files with 99 additions and 118 deletions

View file

@ -1673,6 +1673,13 @@ void spu_thread::cpu_init()
status_npc.raw() = {get_type() == spu_type::isolated ? SPU_STATUS_IS_ISOLATED : 0, 0}; status_npc.raw() = {get_type() == spu_type::isolated ? SPU_STATUS_IS_ISOLATED : 0, 0};
run_ctrl.raw() = 0; run_ctrl.raw() = 0;
spurs_last_task_timestamp = 0;
spurs_wait_duration_last = 0;
spurs_average_task_duration = 0;
spurs_waited = false;
spurs_entered_wait = false;
spurs_read_events = false;
int_ctrl[0].clear(); int_ctrl[0].clear();
int_ctrl[1].clear(); int_ctrl[1].clear();
int_ctrl[2].clear(); int_ctrl[2].clear();
@ -4890,14 +4897,26 @@ bool spu_thread::process_mfc_cmd()
// Avoid logging useless commands if there is no reservation // Avoid logging useless commands if there is no reservation
const bool dump = g_cfg.core.mfc_debug && raddr; const bool dump = g_cfg.core.mfc_debug && raddr;
const bool is_spurs_task_wait = pc == 0x11e4 && spurs_addr == raddr && g_cfg.core.max_spurs_threads != g_cfg.core.max_spurs_threads.def && !spurs_waited; const bool is_spurs_task_wait = pc == 0x11e4;
if (is_spurs_task_wait) do
{ {
if (!is_spurs_task_wait)
{
break;
}
if (spurs_addr != raddr || g_cfg.core.max_spurs_threads == g_cfg.core.max_spurs_threads.def || spurs_waited || spurs_read_events)
{
spurs_read_events = false;
break;
}
// Wait for other threads to complete their tasks (temporarily) // Wait for other threads to complete their tasks (temporarily)
u32 max_run = group->max_run; u32 max_run = group->max_run;
auto [prev_running, ok] = group->spurs_running.fetch_op([max_run, num = group->max_num](u32& x) auto [prev_running, ok] = spurs_entered_wait ? std::make_pair(+group->spurs_running, false) :
group->spurs_running.fetch_op([max_run, num = group->max_num](u32& x)
{ {
if (x >= max_run && max_run < num) if (x >= max_run && max_run < num)
{ {
@ -4908,42 +4927,39 @@ bool spu_thread::process_mfc_cmd()
return false; return false;
}); });
if (prev_running == max_run && ok) if (ok || spurs_entered_wait)
{ {
group->spurs_running.notify_one(); lv2_obj::prepare_for_sleep(*this);
if (group->spurs_running == max_run - 1) if (ok)
{ {
spurs_waited = true; if (prev_running == max_run)
// Try to let another thread slip in and take over execution
thread_ctrl::wait_for(300);
// Try to quit waiting
ok = !group->spurs_running.fetch_op([max_run](u32& x)
{ {
if (x < max_run) group->spurs_running.notify_one();
if (group->spurs_running == max_run - 1)
{ {
x++; // Try to let another thread slip in and take over execution
return true; thread_ctrl::wait_for(300);
// Update value
prev_running = group->spurs_running + 1;
} }
}
return false; // Restore state
}).second; prev_running--;
} }
}
if (ok)
{
// Restore state
prev_running--;
const u64 before = get_system_time(); const u64 before = get_system_time();
u64 current = before; u64 current = before;
lv2_obj::prepare_for_sleep(*this);
spurs_waited = true; spurs_waited = true;
spurs_entered_wait = true;
// Wait the duration of 4 tasks
const u64 spurs_wait_time = std::clamp<u64>(spurs_average_task_duration / spurs_task_count_to_calculate * 4, 3000, 100'000);
spurs_wait_duration_last = spurs_wait_time;
while (true) while (true)
{ {
@ -4954,7 +4970,7 @@ bool spu_thread::process_mfc_cmd()
if (prev_running >= max_run) if (prev_running >= max_run)
{ {
thread_ctrl::wait_on(group->spurs_running, prev_running, 10000 - (current - before)); thread_ctrl::wait_on(group->spurs_running, prev_running, spurs_wait_time - (current - before));
} }
max_run = group->max_run; max_run = group->max_run;
@ -4977,9 +4993,10 @@ bool spu_thread::process_mfc_cmd()
current = get_system_time(); current = get_system_time();
if (current - before >= 10000u) if (current - before >= spurs_wait_time)
{ {
// Timed-out // Timed-out
group->spurs_running++;
break; break;
} }
} }
@ -4988,12 +5005,28 @@ bool spu_thread::process_mfc_cmd()
static_cast<void>(test_stopped()); static_cast<void>(test_stopped());
} }
} }
while (false);
if (do_putllc(ch_mfc_cmd)) if (do_putllc(ch_mfc_cmd))
{ {
ch_atomic_stat.set_value(MFC_PUTLLC_SUCCESS); ch_atomic_stat.set_value(MFC_PUTLLC_SUCCESS);
spurs_waited = false;
if (is_spurs_task_wait)
{
const u64 current = get_system_time();
if (spurs_last_task_timestamp)
{
const u64 avg_entry = spurs_average_task_duration / spurs_task_count_to_calculate;
spurs_average_task_duration -= spurs_waited && !is_stopped() ? spurs_wait_duration_last + avg_entry : avg_entry;
spurs_average_task_duration += std::min<u64>(45'000, current - spurs_last_task_timestamp);
}
spurs_last_task_timestamp = current;
spurs_read_events = false;
spurs_waited = false;
spurs_entered_wait = false;
}
} }
else else
{ {
@ -5593,106 +5626,49 @@ s64 spu_thread::get_ch_value(u32 ch)
auto events = get_events(mask1, false, true); auto events = get_events(mask1, false, true);
const bool is_spurs_task_wait = pc == 0x11a8 && spurs_addr == raddr;
if (events.count) if (events.count)
{
return events.events & mask1;
}
const bool is_spurs_task_wait = pc == 0x11a8 && spurs_addr == raddr && g_cfg.core.max_spurs_threads != g_cfg.core.max_spurs_threads.def && !spurs_waited;
const auto wait_spurs_task = [&]
{ {
if (is_spurs_task_wait) if (is_spurs_task_wait)
{ {
// Wait for other threads to complete their tasks (temporarily) spurs_read_events = true;
if (!is_stopped())
{
u32 max_run = group->max_run;
u32 prev_running = group->spurs_running.fetch_op([max_run](u32& x)
{
if (x < max_run)
{
x++;
return true;
}
return false;
}).first;
if (prev_running >= max_run)
{
const u64 before = get_system_time();
u64 current = before;
lv2_obj::prepare_for_sleep(*this);
spurs_waited = true;
while (true)
{
if (is_stopped())
{
break;
}
thread_ctrl::wait_on(group->spurs_running, prev_running, 10000u - (current - before));
max_run = group->max_run;
prev_running = group->spurs_running.fetch_op([max_run](u32& x)
{
if (x < max_run)
{
x++;
return true;
}
return false;
}).first;
if (prev_running < max_run)
{
break;
}
current = get_system_time();
if (current - before >= 10000u)
{
// Timed-out
group->spurs_running++;
break;
}
}
}
}
} }
};
return events.events & mask1;
}
if (is_spurs_task_wait) if (is_spurs_task_wait)
{ {
const u32 prev_running = group->spurs_running.fetch_op([](u32& x) spurs_read_events = true;
if (g_cfg.core.max_spurs_threads != g_cfg.core.max_spurs_threads.def && !spurs_entered_wait)
{ {
if (x) const u32 prev_running = group->spurs_running.fetch_op([](u32& x)
{ {
x--; if (x)
return true; {
x--;
return true;
}
return false;
}).first;
if (prev_running)
{
spurs_entered_wait = true;
} }
return false; if (prev_running == group->max_run && prev_running < group->max_num)
}).first;
if (prev_running == group->max_run && prev_running < group->max_num)
{
group->spurs_running.notify_one();
spurs_waited = true;
if (group->spurs_running == prev_running - 1)
{ {
// Try to let another thread slip in and take over execution group->spurs_running.notify_one();
thread_ctrl::wait_for(300);
if (group->spurs_running == prev_running - 1)
{
// Try to let another thread slip in and take over execution
thread_ctrl::wait_for(300);
}
} }
} }
} }
@ -5928,7 +5904,6 @@ s64 spu_thread::get_ch_value(u32 ch)
thread_ctrl::wait_on(state, old, 100); thread_ctrl::wait_on(state, old, 100);
} }
wait_spurs_task();
wakeup_delay(); wakeup_delay();
if (is_paused(state - cpu_flag::suspend)) if (is_paused(state - cpu_flag::suspend))

View file

@ -768,6 +768,12 @@ public:
const u32 lv2_id; // The actual id that is used by syscalls const u32 lv2_id; // The actual id that is used by syscalls
u32 spurs_addr = 0; u32 spurs_addr = 0;
bool spurs_waited = false; bool spurs_waited = false;
bool spurs_entered_wait = false;
bool spurs_read_events = false;
u64 spurs_wait_duration_last = 0;
u64 spurs_average_task_duration = 0;
u64 spurs_last_task_timestamp = 0;
static constexpr u64 spurs_task_count_to_calculate = 10;
spu_thread* next_cpu{}; // LV2 thread queues' node link spu_thread* next_cpu{}; // LV2 thread queues' node link