mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-28 10:25:29 +03:00
Use AdaptiveSingleWaiterConditionVariable for thread scheduling
Some games, for example PGLE, have heavy contention in code that locks mutexes for only a brief period of time. This heavy contention over multiple threads results in futex latency (often ~20us) impacting performance heavily. Using an adaptive condition variable helps to reduce this latency.
This commit is contained in:
parent
b1e57bc7bc
commit
6d582566f9
@ -138,13 +138,13 @@ namespace skyline::kernel {
|
|||||||
core.queue.push_front(thread);
|
core.queue.push_front(thread);
|
||||||
}
|
}
|
||||||
if (thread != state.thread)
|
if (thread != state.thread)
|
||||||
thread->scheduleCondition.notify_one(); // We only want to trigger the conditional variable if the current thread isn't inserting itself
|
thread->scheduleCondition.notify(); // We only want to trigger the conditional variable if the current thread isn't inserting itself
|
||||||
} else {
|
} else {
|
||||||
core.queue.insert(nextThread, thread);
|
core.queue.insert(nextThread, thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::MigrateToCore(const std::shared_ptr<type::KThread> &thread, CoreContext *¤tCore, CoreContext *targetCore, std::unique_lock<std::mutex> &lock) {
|
void Scheduler::MigrateToCore(const std::shared_ptr<type::KThread> &thread, CoreContext *¤tCore, CoreContext *targetCore, std::unique_lock<SpinLock> &lock) {
|
||||||
// We need to check if the thread was in its resident core's queue
|
// We need to check if the thread was in its resident core's queue
|
||||||
// If it was, we need to remove it from the queue
|
// If it was, we need to remove it from the queue
|
||||||
auto it{std::find(currentCore->queue.begin(), currentCore->queue.end(), thread)};
|
auto it{std::find(currentCore->queue.begin(), currentCore->queue.end(), thread)};
|
||||||
@ -152,7 +152,7 @@ namespace skyline::kernel {
|
|||||||
if (wasInserted) {
|
if (wasInserted) {
|
||||||
it = currentCore->queue.erase(it);
|
it = currentCore->queue.erase(it);
|
||||||
if (it == currentCore->queue.begin() && it != currentCore->queue.end())
|
if (it == currentCore->queue.begin() && it != currentCore->queue.end())
|
||||||
(*it)->scheduleCondition.notify_one();
|
(*it)->scheduleCondition.notify();
|
||||||
}
|
}
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
@ -243,7 +243,7 @@ namespace skyline::kernel {
|
|||||||
|
|
||||||
auto &front{core.queue.front()};
|
auto &front{core.queue.front()};
|
||||||
if (front != thread)
|
if (front != thread)
|
||||||
front->scheduleCondition.notify_one(); // If we aren't at the front of the queue, only then should we wake the thread at the front up
|
front->scheduleCondition.notify(); // If we aren't at the front of the queue, only then should we wake the thread at the front up
|
||||||
} else if (!thread->forceYield) {
|
} else if (!thread->forceYield) {
|
||||||
throw exception("T{} called Rotate while not being in C{}'s queue", thread->id, thread->coreId);
|
throw exception("T{} called Rotate while not being in C{}'s queue", thread->id, thread->coreId);
|
||||||
}
|
}
|
||||||
@ -271,7 +271,7 @@ namespace skyline::kernel {
|
|||||||
thread->averageTimeslice = (thread->averageTimeslice / 4) + (3 * (util::GetTimeTicks() - thread->timesliceStart / 4));
|
thread->averageTimeslice = (thread->averageTimeslice / 4) + (3 * (util::GetTimeTicks() - thread->timesliceStart / 4));
|
||||||
|
|
||||||
if (it != core.queue.end())
|
if (it != core.queue.end())
|
||||||
(*it)->scheduleCondition.notify_one(); // We need to wake the thread at the front of the queue, if we were at the front previously
|
(*it)->scheduleCondition.notify(); // We need to wake the thread at the front of the queue, if we were at the front previously
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Logger::Warn("T{} was not in C{}'s queue", thread->id, thread->coreId);
|
Logger::Warn("T{} was not in C{}'s queue", thread->id, thread->coreId);
|
||||||
@ -326,7 +326,7 @@ namespace skyline::kernel {
|
|||||||
if (core->queue.front() == thread)
|
if (core->queue.front() == thread)
|
||||||
thread->SendSignal(YieldSignal);
|
thread->SendSignal(YieldSignal);
|
||||||
else
|
else
|
||||||
thread->scheduleCondition.notify_one();
|
thread->scheduleCondition.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::ParkThread() {
|
void Scheduler::ParkThread() {
|
||||||
@ -364,7 +364,7 @@ namespace skyline::kernel {
|
|||||||
if (parkedThread->priority < thread->priority || (parkedThread->priority == thread->priority && (!nextThread || parkedThread->timesliceStart < nextThread->timesliceStart))) {
|
if (parkedThread->priority < thread->priority || (parkedThread->priority == thread->priority && (!nextThread || parkedThread->timesliceStart < nextThread->timesliceStart))) {
|
||||||
parkedThread->coreId = thread->coreId;
|
parkedThread->coreId = thread->coreId;
|
||||||
parkedLock.unlock();
|
parkedLock.unlock();
|
||||||
parkedThread->scheduleCondition.notify_one();
|
parkedThread->scheduleCondition.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -381,7 +381,7 @@ namespace skyline::kernel {
|
|||||||
|
|
||||||
it = core->queue.erase(it);
|
it = core->queue.erase(it);
|
||||||
if (it == core->queue.begin() && it != core->queue.end())
|
if (it == core->queue.begin() && it != core->queue.end())
|
||||||
(*it)->scheduleCondition.notify_one();
|
(*it)->scheduleCondition.notify();
|
||||||
|
|
||||||
if (it == core->queue.begin()) {
|
if (it == core->queue.begin()) {
|
||||||
// We need to send a yield signal to the thread if it's currently running
|
// We need to send a yield signal to the thread if it's currently running
|
||||||
@ -402,6 +402,6 @@ namespace skyline::kernel {
|
|||||||
InsertThread(thread);
|
InsertThread(thread);
|
||||||
else
|
else
|
||||||
// If we're not inserting the thread back into the queue ourselves then we need to notify the thread inserting it about the updated pause state
|
// If we're not inserting the thread back into the queue ourselves then we need to notify the thread inserting it about the updated pause state
|
||||||
thread->scheduleCondition.notify_one();
|
thread->scheduleCondition.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/spin_lock.h"
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ namespace skyline {
|
|||||||
struct CoreContext {
|
struct CoreContext {
|
||||||
u8 id;
|
u8 id;
|
||||||
i8 preemptionPriority; //!< The priority at which this core becomes preemptive as opposed to cooperative
|
i8 preemptionPriority; //!< The priority at which this core becomes preemptive as opposed to cooperative
|
||||||
std::mutex mutex; //!< Synchronizes all operations on the queue
|
SpinLock mutex; //!< Synchronizes all operations on the queue
|
||||||
std::list<std::shared_ptr<type::KThread>> queue; //!< A queue of threads which are running or to be run on this core
|
std::list<std::shared_ptr<type::KThread>> queue; //!< A queue of threads which are running or to be run on this core
|
||||||
|
|
||||||
CoreContext(u8 id, i8 preemptionPriority);
|
CoreContext(u8 id, i8 preemptionPriority);
|
||||||
@ -62,7 +63,7 @@ namespace skyline {
|
|||||||
* @note 'KThread::coreMigrationMutex' **must** be locked by the calling thread prior to calling this
|
* @note 'KThread::coreMigrationMutex' **must** be locked by the calling thread prior to calling this
|
||||||
* @note This is used to handle non-cooperative core affinity mask changes where the resident core is not in its new affinity mask
|
* @note This is used to handle non-cooperative core affinity mask changes where the resident core is not in its new affinity mask
|
||||||
*/
|
*/
|
||||||
void MigrateToCore(const std::shared_ptr<type::KThread> &thread, CoreContext *¤tCore, CoreContext *targetCore, std::unique_lock<std::mutex> &lock);
|
void MigrateToCore(const std::shared_ptr<type::KThread> &thread, CoreContext *¤tCore, CoreContext *targetCore, std::unique_lock<SpinLock> &lock);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Trigger a thread to yield via a signal or on SVC exit if it is the current thread
|
* @brief Trigger a thread to yield via a signal or on SVC exit if it is the current thread
|
||||||
|
@ -47,7 +47,7 @@ namespace skyline {
|
|||||||
u64 entryArgument; //!< An argument to provide with to the thread entry function
|
u64 entryArgument; //!< An argument to provide with to the thread entry function
|
||||||
void *stackTop; //!< The top of the guest's stack, this is set to the initial guest stack pointer
|
void *stackTop; //!< The top of the guest's stack, this is set to the initial guest stack pointer
|
||||||
|
|
||||||
std::condition_variable scheduleCondition; //!< Signalled to wake the thread when it's scheduled or its resident core changes
|
AdaptiveSingleWaiterConditionVariable scheduleCondition; //!< Signalled to wake the thread when it's scheduled or its resident core changes
|
||||||
std::atomic<i8> basePriority; //!< The priority of the thread for the scheduler without any priority-inheritance
|
std::atomic<i8> basePriority; //!< The priority of the thread for the scheduler without any priority-inheritance
|
||||||
std::atomic<i8> priority; //!< The priority of the thread for the scheduler including priority-inheritance
|
std::atomic<i8> priority; //!< The priority of the thread for the scheduler including priority-inheritance
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user