scheduler: Only work steal higher priority threads from other cores

This commit is contained in:
Zach Hilman 2018-12-03 17:29:21 -05:00
parent 3476830b26
commit b5af41a07b
3 changed files with 24 additions and 35 deletions

View file

@ -170,16 +170,6 @@ void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
ready_queue.remove(priority, thread); ready_queue.remove(priority, thread);
} }
void Scheduler::MoveThreadToBackOfPriorityQueue(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
// Thread is not in queue
ASSERT(ready_queue.contains(thread) != -1);
ready_queue.remove(priority, thread);
ready_queue.push_back(priority, thread);
}
void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex); std::lock_guard<std::mutex> lock(scheduler_mutex);
@ -190,12 +180,13 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
ready_queue.prepare(priority); ready_queue.prepare(priority);
} }
Thread* Scheduler::GetNextSuggestedThread(u32 core) const { Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const {
std::lock_guard<std::mutex> lock(scheduler_mutex); std::lock_guard<std::mutex> lock(scheduler_mutex);
const u32 mask = 1U << core; const u32 mask = 1U << core;
return ready_queue.get_first_filter( return ready_queue.get_first_filter([mask, maximum_priority](Thread const* thread) {
[mask](Thread const* thread) { return (thread->GetAffinityMask() & mask) != 0; }); return (thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority;
});
} }
void Scheduler::YieldWithoutLoadBalancing(Thread* thread) { void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
@ -206,9 +197,10 @@ void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
// Sanity check that the priority is valid // Sanity check that the priority is valid
ASSERT(thread->GetPriority() < THREADPRIO_COUNT); ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
// Yield this thread // Yield this thread -- sleep for zero time and force reschedule to different thread
WaitCurrentThread_Sleep();
GetCurrentThread()->WakeAfterDelay(0);
Reschedule(); Reschedule();
MoveThreadToBackOfPriorityQueue(thread, thread->GetPriority());
} }
void Scheduler::YieldWithLoadBalancing(Thread* thread) { void Scheduler::YieldWithLoadBalancing(Thread* thread) {
@ -222,9 +214,9 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
// Sanity check that the priority is valid // Sanity check that the priority is valid
ASSERT(priority < THREADPRIO_COUNT); ASSERT(priority < THREADPRIO_COUNT);
// Reschedule thread to end of queue. // Sleep for zero time to be able to force reschedule to different thread
Reschedule(); WaitCurrentThread_Sleep();
MoveThreadToBackOfPriorityQueue(thread, priority); GetCurrentThread()->WakeAfterDelay(0);
Thread* suggested_thread = nullptr; Thread* suggested_thread = nullptr;
@ -235,16 +227,20 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
continue; continue;
const auto res = const auto res =
Core::System::GetInstance().CpuCore(cur_core).Scheduler().GetNextSuggestedThread(core); Core::System::GetInstance().CpuCore(cur_core).Scheduler().GetNextSuggestedThread(
if (res != nullptr) { core, priority);
if (res != nullptr &&
(suggested_thread == nullptr || suggested_thread->GetPriority() > res->GetPriority())) {
suggested_thread = res; suggested_thread = res;
break;
} }
} }
// If a suggested thread was found, queue that for this core // If a suggested thread was found, queue that for this core
if (suggested_thread != nullptr) if (suggested_thread != nullptr)
suggested_thread->ChangeCore(core, suggested_thread->GetAffinityMask()); suggested_thread->ChangeCore(core, suggested_thread->GetAffinityMask());
// Perform actual yielding.
Reschedule();
} }
void Scheduler::YieldAndWaitForLoadBalancing(Thread* thread) { void Scheduler::YieldAndWaitForLoadBalancing(Thread* thread) {

View file

@ -48,14 +48,11 @@ public:
/// Unschedules a thread that was already scheduled /// Unschedules a thread that was already scheduled
void UnscheduleThread(Thread* thread, u32 priority); void UnscheduleThread(Thread* thread, u32 priority);
/// Moves a thread to the back of the current priority queue
void MoveThreadToBackOfPriorityQueue(Thread* thread, u32 priority);
/// Sets the priority of a thread in the scheduler /// Sets the priority of a thread in the scheduler
void SetThreadPriority(Thread* thread, u32 priority); void SetThreadPriority(Thread* thread, u32 priority);
/// Gets the next suggested thread for load balancing /// Gets the next suggested thread for load balancing
Thread* GetNextSuggestedThread(u32 core) const; Thread* GetNextSuggestedThread(u32 core, u32 minimum_priority) const;
/** /**
* YieldWithoutLoadBalancing -- analogous to normal yield on a system * YieldWithoutLoadBalancing -- analogous to normal yield on a system

View file

@ -984,19 +984,15 @@ static void SleepThread(s64 nanoseconds) {
scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread()); scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread());
break; break;
default: default:
UNREACHABLE_MSG( UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
"Unimplemented sleep yield type '{:016X}'! Falling back to forced reschedule...",
nanoseconds);
} }
} else {
nanoseconds = 0;
}
// Sleep current thread and check for next thread to schedule // Sleep current thread and check for next thread to schedule
WaitCurrentThread_Sleep(); WaitCurrentThread_Sleep();
// Create an event to wake the thread up after the specified nanosecond delay has passed // Create an event to wake the thread up after the specified nanosecond delay has passed
GetCurrentThread()->WakeAfterDelay(nanoseconds); GetCurrentThread()->WakeAfterDelay(nanoseconds);
}
Core::System::GetInstance().PrepareReschedule(); Core::System::GetInstance().PrepareReschedule();
} }