scheduler: Use load information to migrate threads
This commit is contained in:
@@ -57,8 +57,9 @@ const bigtime_t kMinimalWaitTime = kThreadQuantum / 4;
|
|||||||
const bigtime_t kCacheExpire = 100000;
|
const bigtime_t kCacheExpire = 100000;
|
||||||
|
|
||||||
const bigtime_t kHighLoad = 600;
|
const bigtime_t kHighLoad = 600;
|
||||||
|
const int kLoadDifference = 200;
|
||||||
|
|
||||||
static int sDisableSmallTaskPacking;
|
static bigtime_t sDisableSmallTaskPacking;
|
||||||
static int32 sSmallTaskCore;
|
static int32 sSmallTaskCore;
|
||||||
|
|
||||||
static bool sSingleCore;
|
static bool sSingleCore;
|
||||||
@@ -66,6 +67,7 @@ static bool sSingleCore;
|
|||||||
static scheduler_mode sSchedulerMode;
|
static scheduler_mode sSchedulerMode;
|
||||||
|
|
||||||
static int32 (*sChooseCore)(Thread* thread);
|
static int32 (*sChooseCore)(Thread* thread);
|
||||||
|
static bool (*sShouldRebalance)(Thread* thread);
|
||||||
|
|
||||||
|
|
||||||
// Heaps in sCPUPriorityHeaps are used for load balancing on a core the logical
|
// Heaps in sCPUPriorityHeaps are used for load balancing on a core the logical
|
||||||
@@ -560,16 +562,24 @@ update_load_heaps(int32 core)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
is_small_task_packing_enabled(void)
|
||||||
|
{
|
||||||
|
if (sDisableSmallTaskPacking == -1)
|
||||||
|
return false;
|
||||||
|
return sDisableSmallTaskPacking < system_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
disable_small_task_packing(void)
|
disable_small_task_packing(void)
|
||||||
{
|
{
|
||||||
ASSERT(!sSingleCore);
|
ASSERT(!sSingleCore);
|
||||||
|
|
||||||
ASSERT(sDisableSmallTaskPacking == 0);
|
ASSERT(is_small_task_packing_enabled());
|
||||||
ASSERT(sSmallTaskCore == sCPUToCore[smp_get_current_cpu()]);
|
ASSERT(sSmallTaskCore == sCPUToCore[smp_get_current_cpu()]);
|
||||||
|
|
||||||
// ASSERT(sAssignedThreads > 0);
|
sDisableSmallTaskPacking = system_time() + kThreadQuantum * 100;
|
||||||
// sDisableSmallTaskPacking = sAssignedThreads * 64;
|
|
||||||
sSmallTaskCore = -1;
|
sSmallTaskCore = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -726,9 +736,7 @@ choose_core_performance(Thread* thread)
|
|||||||
static inline bool
|
static inline bool
|
||||||
is_task_small(Thread* thread)
|
is_task_small(Thread* thread)
|
||||||
{
|
{
|
||||||
int32 priority = get_effective_priority(thread);
|
return thread->scheduler_data->load <= 200;
|
||||||
int32 penalty = thread->scheduler_data->priority_penalty;
|
|
||||||
return penalty < 2 || priority >= B_DISPLAY_PRIORITY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -739,10 +747,7 @@ choose_core_power_saving(Thread* thread)
|
|||||||
|
|
||||||
int32 priority = get_effective_priority(thread);
|
int32 priority = get_effective_priority(thread);
|
||||||
|
|
||||||
if (sDisableSmallTaskPacking > 0)
|
if (is_small_task_packing_enabled() && is_task_small(thread)
|
||||||
sDisableSmallTaskPacking--;
|
|
||||||
|
|
||||||
if (!sDisableSmallTaskPacking && is_task_small(thread)
|
|
||||||
&& sCoreLoadHeap->PeekMaximum() != NULL) {
|
&& sCoreLoadHeap->PeekMaximum() != NULL) {
|
||||||
// try to pack all threads on one core
|
// try to pack all threads on one core
|
||||||
if (sSmallTaskCore < 0)
|
if (sSmallTaskCore < 0)
|
||||||
@@ -791,9 +796,38 @@ choose_cpu(int32 core)
|
|||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
should_rebalance(Thread* thread)
|
should_rebalance_performance(Thread* thread)
|
||||||
|
{
|
||||||
|
scheduler_thread_data* schedulerThreadData = thread->scheduler_data;
|
||||||
|
ASSERT(schedulerThreadData->previous_core >= 0);
|
||||||
|
|
||||||
|
CoreEntry* coreEntry = &sCoreEntries[schedulerThreadData->previous_core];
|
||||||
|
|
||||||
|
// If the thread produces more than 50% of the load, leave it here. In
|
||||||
|
// such situation it is better to move other threads away.
|
||||||
|
if (schedulerThreadData->load >= coreEntry->fLoad / 2)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If there is high load on this core but this thread does not contribute
|
||||||
|
// significantly consider giving it to someone less busy.
|
||||||
|
if (coreEntry->fLoad > kHighLoad) {
|
||||||
|
CoreEntry* other = sCoreLoadHeap->PeekMinimum();
|
||||||
|
if (other != NULL && coreEntry->fLoad - other->fLoad >= kLoadDifference)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No cpu bound threads - the situation is quite good. Make sure it
|
||||||
|
// won't get much worse...
|
||||||
|
CoreEntry* other = sCoreLoadHeap->PeekMinimum();
|
||||||
|
if (other == NULL)
|
||||||
|
other = sCoreHighLoadHeap->PeekMinimum();
|
||||||
|
return coreEntry->fLoad - other->fLoad >= kLoadDifference * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
should_rebalance_power_saving(Thread* thread)
|
||||||
{
|
{
|
||||||
#if 0
|
|
||||||
ASSERT(!sSingleCore);
|
ASSERT(!sSingleCore);
|
||||||
|
|
||||||
if (thread_is_idle_thread(thread))
|
if (thread_is_idle_thread(thread))
|
||||||
@@ -802,49 +836,47 @@ should_rebalance(Thread* thread)
|
|||||||
scheduler_thread_data* schedulerThreadData = thread->scheduler_data;
|
scheduler_thread_data* schedulerThreadData = thread->scheduler_data;
|
||||||
ASSERT(schedulerThreadData->previous_core >= 0);
|
ASSERT(schedulerThreadData->previous_core >= 0);
|
||||||
|
|
||||||
CoreEntry* coreEntry = &sCoreEntries[schedulerThreadData->previous_core];
|
int32 core = schedulerThreadData->previous_core;
|
||||||
|
CoreEntry* coreEntry = &sCoreEntries[core];
|
||||||
|
|
||||||
// If this is a cpu bound thread and we have significantly more such threads
|
// If the thread produces more than 50% of the load, leave it here. In
|
||||||
// than the average get rid of this one.
|
// such situation it is better to move other threads away.
|
||||||
if (schedulerThreadData->additional_penalty != 0) {
|
// Unless we are trying to pack small tasks here, in such case get rid
|
||||||
int32 averageCPUBound = sCPUBoundThreads / sRunQueueCount;
|
// of CPU hungry thread and continue packing.
|
||||||
return coreEntry->fCPUBoundThreads - averageCPUBound > 1;
|
if (schedulerThreadData->load >= coreEntry->fLoad / 2)
|
||||||
}
|
return is_small_task_packing_enabled() && sSmallTaskCore == core;
|
||||||
|
|
||||||
// If this thread is not cpu bound but we have at least one consider giving
|
|
||||||
// this one to someone less busy.
|
|
||||||
int32 averageThread = sAssignedThreads / sRunQueueCount;
|
|
||||||
if (coreEntry->fCPUBoundThreads > 0) {
|
|
||||||
CoreEntry* other = sCoreThreadHeap->PeekMinimum();
|
|
||||||
if (other != NULL
|
|
||||||
&& CoreThreadHeap::GetKey(other) <= averageThread) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 threadsAboveAverage = coreEntry->fThreads - averageThread;
|
|
||||||
|
|
||||||
// All cores try to give us small tasks, check whether we have enough.
|
// All cores try to give us small tasks, check whether we have enough.
|
||||||
const int kSmallTaskCountThreshold = 5;
|
if (is_small_task_packing_enabled() && sSmallTaskCore == core) {
|
||||||
if (sDisableSmallTaskPacking == 0 && sSmallTaskCore == coreEntry->fCoreID) {
|
if (coreEntry->fLoad > kHighLoad) {
|
||||||
if (threadsAboveAverage > kSmallTaskCountThreshold) {
|
|
||||||
if (!is_task_small(thread))
|
if (!is_task_small(thread))
|
||||||
return true;
|
return true;
|
||||||
} else if (threadsAboveAverage > 2 * kSmallTaskCountThreshold) {
|
} else if (coreEntry->fLoad > (kHighLoad + 1000) / 2)
|
||||||
disable_small_task_packing();
|
disable_small_task_packing();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try our luck at small task packing.
|
// Try small task packing.
|
||||||
if (sDisableSmallTaskPacking == 0 && is_task_small(thread))
|
if (is_small_task_packing_enabled() && is_task_small(thread))
|
||||||
return sSmallTaskCore != coreEntry->fCoreID;
|
return sSmallTaskCore != core;
|
||||||
|
|
||||||
// No cpu bound threads - the situation is quite good. Make sure it
|
// No cpu bound threads - the situation is quite good. Make sure it
|
||||||
// won't get much worse...
|
// won't get much worse...
|
||||||
const int32 kBalanceThreshold = 3;
|
CoreEntry* other = sCoreLoadHeap->PeekMinimum();
|
||||||
return threadsAboveAverage > kBalanceThreshold;
|
if (other == NULL)
|
||||||
#endif
|
other = sCoreHighLoadHeap->PeekMinimum();
|
||||||
return false;
|
return coreEntry->fLoad - other->fLoad >= kLoadDifference;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
should_rebalance(Thread* thread)
|
||||||
|
{
|
||||||
|
ASSERT(!sSingleCore);
|
||||||
|
|
||||||
|
if (thread_is_idle_thread(thread))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return sShouldRebalance(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1526,12 +1558,14 @@ scheduler_set_operation_mode(scheduler_mode mode)
|
|||||||
sDisableSmallTaskPacking = -1;
|
sDisableSmallTaskPacking = -1;
|
||||||
sSmallTaskCore = -1;
|
sSmallTaskCore = -1;
|
||||||
sChooseCore = choose_core_performance;
|
sChooseCore = choose_core_performance;
|
||||||
|
sShouldRebalance = should_rebalance_performance;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCHEDULER_MODE_POWER_SAVING:
|
case SCHEDULER_MODE_POWER_SAVING:
|
||||||
sDisableSmallTaskPacking = 0;
|
sDisableSmallTaskPacking = 0;
|
||||||
sSmallTaskCore = -1;
|
sSmallTaskCore = -1;
|
||||||
sChooseCore = choose_core_power_saving;
|
sChooseCore = choose_core_power_saving;
|
||||||
|
sShouldRebalance = should_rebalance_power_saving;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
Reference in New Issue
Block a user