scheduler: Use load information to migrate threads

This commit is contained in:
Pawel Dziepak
2013-10-28 02:44:46 +01:00
parent 5e2701a2b5
commit 6d96f462dc
+79 -45
View File
@@ -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: