scheduler_affine: Balance number of threads assigned to CPUs
When the thread cannot be run immediately assign it to the core with lowest number of CPU bound threads and assigned threads.
This commit is contained in:
@@ -52,7 +52,7 @@ const bigtime_t kCacheExpire = 100000;
|
|||||||
|
|
||||||
static scheduler_mode sSchedulerMode;
|
static scheduler_mode sSchedulerMode;
|
||||||
|
|
||||||
static int32 (*sChooseCore)(void);
|
static int32 (*sChooseCore)(int32 priority);
|
||||||
|
|
||||||
|
|
||||||
// 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
|
||||||
@@ -67,16 +67,28 @@ typedef MinMaxHeap<CPUEntry, int32> AffineCPUHeap;
|
|||||||
static CPUEntry* sCPUEntries;
|
static CPUEntry* sCPUEntries;
|
||||||
static AffineCPUHeap* sCPUPriorityHeaps;
|
static AffineCPUHeap* sCPUPriorityHeaps;
|
||||||
|
|
||||||
struct CoreEntry : public HeapLinkImpl<CoreEntry, int32>,
|
struct CoreEntry : public DoublyLinkedListLinkImpl<CoreEntry> {
|
||||||
DoublyLinkedListLinkImpl<CoreEntry> {
|
HeapLink<CoreEntry, int32> fPriorityHeapLink;
|
||||||
|
HeapLink<CoreEntry, int32> fThreadHeapLink;
|
||||||
|
|
||||||
int32 fCoreID;
|
int32 fCoreID;
|
||||||
|
|
||||||
bigtime_t fActiveTime;
|
bigtime_t fActiveTime;
|
||||||
|
|
||||||
|
int32 fCPUBoundThreads;
|
||||||
|
int32 fThreads;
|
||||||
};
|
};
|
||||||
|
|
||||||
static CoreEntry* sCoreEntries;
|
static CoreEntry* sCoreEntries;
|
||||||
typedef Heap<CoreEntry, int32> AffineCoreHeap;
|
typedef Heap<CoreEntry, int32, HeapLesserCompare<int32>,
|
||||||
static AffineCoreHeap* sCorePriorityHeap;
|
HeapMemberGetLink<CoreEntry, int32, &CoreEntry::fPriorityHeapLink> >
|
||||||
|
AffineCorePriorityHeap;
|
||||||
|
static AffineCorePriorityHeap* sCorePriorityHeap;
|
||||||
|
|
||||||
|
typedef Heap<CoreEntry, int32, HeapLesserCompare<int32>,
|
||||||
|
HeapMemberGetLink<CoreEntry, int32, &CoreEntry::fThreadHeapLink> >
|
||||||
|
AffineCoreThreadHeap;
|
||||||
|
static AffineCoreThreadHeap* sCoreThreadHeap;
|
||||||
|
|
||||||
// sPackageUsageHeap is used to decide which core should be woken up from the
|
// sPackageUsageHeap is used to decide which core should be woken up from the
|
||||||
// idle state. When aiming for performance we should use as many packages as
|
// idle state. When aiming for performance we should use as many packages as
|
||||||
@@ -249,7 +261,7 @@ dump_run_queue(int argc, char **argv)
|
|||||||
static void
|
static void
|
||||||
dump_heap(AffineCPUHeap* heap)
|
dump_heap(AffineCPUHeap* heap)
|
||||||
{
|
{
|
||||||
AffineCPUHeap temp;
|
AffineCPUHeap temp(smp_get_num_cpus());
|
||||||
|
|
||||||
kprintf("cpu priority actual priority\n");
|
kprintf("cpu priority actual priority\n");
|
||||||
CPUEntry* entry = heap->PeekMinimum();
|
CPUEntry* entry = heap->PeekMinimum();
|
||||||
@@ -278,13 +290,13 @@ dump_heap(AffineCPUHeap* heap)
|
|||||||
static int
|
static int
|
||||||
dump_cpu_heap(int argc, char** argv)
|
dump_cpu_heap(int argc, char** argv)
|
||||||
{
|
{
|
||||||
AffineCoreHeap temp;
|
AffineCorePriorityHeap temp(sRunQueueCount);
|
||||||
|
|
||||||
kprintf("core priority\n");
|
kprintf("core priority\n");
|
||||||
CoreEntry* entry = sCorePriorityHeap->PeekRoot();
|
CoreEntry* entry = sCorePriorityHeap->PeekRoot();
|
||||||
while (entry) {
|
while (entry) {
|
||||||
int32 core = entry->fCoreID;
|
int32 core = entry->fCoreID;
|
||||||
int32 key = AffineCoreHeap::GetKey(entry);
|
int32 key = AffineCorePriorityHeap::GetKey(entry);
|
||||||
kprintf("%4" B_PRId32 " %8" B_PRId32 "\n", core, key);
|
kprintf("%4" B_PRId32 " %8" B_PRId32 "\n", core, key);
|
||||||
|
|
||||||
sCorePriorityHeap->RemoveRoot();
|
sCorePriorityHeap->RemoveRoot();
|
||||||
@@ -295,12 +307,35 @@ dump_cpu_heap(int argc, char** argv)
|
|||||||
|
|
||||||
entry = temp.PeekRoot();
|
entry = temp.PeekRoot();
|
||||||
while (entry) {
|
while (entry) {
|
||||||
int32 key = AffineCoreHeap::GetKey(entry);
|
int32 key = AffineCorePriorityHeap::GetKey(entry);
|
||||||
temp.RemoveRoot();
|
temp.RemoveRoot();
|
||||||
sCorePriorityHeap->Insert(entry, key);
|
sCorePriorityHeap->Insert(entry, key);
|
||||||
entry = temp.PeekRoot();
|
entry = temp.PeekRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AffineCoreThreadHeap temp2(sRunQueueCount);
|
||||||
|
|
||||||
|
kprintf("\ncore key threads cpu-bound\n");
|
||||||
|
entry = sCoreThreadHeap->PeekRoot();
|
||||||
|
while (entry) {
|
||||||
|
int32 key = AffineCoreThreadHeap::GetKey(entry);
|
||||||
|
kprintf("%4" B_PRId32 " %6" B_PRId32 " %7" B_PRId32 " %9" B_PRId32 "\n",
|
||||||
|
entry->fCoreID, key, entry->fThreads, entry->fCPUBoundThreads);
|
||||||
|
|
||||||
|
sCoreThreadHeap->RemoveRoot();
|
||||||
|
temp2.Insert(entry, key);
|
||||||
|
|
||||||
|
entry = sCoreThreadHeap->PeekRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = temp2.PeekRoot();
|
||||||
|
while (entry) {
|
||||||
|
int32 key = AffineCoreThreadHeap::GetKey(entry);
|
||||||
|
temp2.RemoveRoot();
|
||||||
|
sCoreThreadHeap->Insert(entry, key);
|
||||||
|
entry = temp2.PeekRoot();
|
||||||
|
}
|
||||||
|
|
||||||
for (int32 i = 0; i < sRunQueueCount; i++) {
|
for (int32 i = 0; i < sRunQueueCount; i++) {
|
||||||
kprintf("\nCore %" B_PRId32 " heap:\n", i);
|
kprintf("\nCore %" B_PRId32 " heap:\n", i);
|
||||||
dump_heap(&sCPUPriorityHeaps[i]);
|
dump_heap(&sCPUPriorityHeaps[i]);
|
||||||
@@ -339,8 +374,7 @@ dump_idle_cores(int argc, char** argv)
|
|||||||
} else
|
} else
|
||||||
kprintf("No idle packages.\n");
|
kprintf("No idle packages.\n");
|
||||||
|
|
||||||
AffinePackageHeap temp;
|
AffinePackageHeap temp(smp_get_num_cpus());
|
||||||
temp.GrowHeap(smp_get_num_cpus());
|
|
||||||
kprintf("\nPackages with idle cores:\n");
|
kprintf("\nPackages with idle cores:\n");
|
||||||
|
|
||||||
PackageEntry* entry = sPackageUsageHeap->PeekMinimum();
|
PackageEntry* entry = sPackageUsageHeap->PeekMinimum();
|
||||||
@@ -439,6 +473,22 @@ affine_dump_thread_data(Thread* thread)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
affine_update_thread_heaps(int32 core)
|
||||||
|
{
|
||||||
|
CoreEntry* entry = &sCoreEntries[core];
|
||||||
|
|
||||||
|
ASSERT(entry->fCPUBoundThreads >= 0
|
||||||
|
&& entry->fCPUBoundThreads <= entry->fThreads);
|
||||||
|
ASSERT(entry->fThreads >= 0
|
||||||
|
&& entry->fThreads <= thread_max_threads());
|
||||||
|
|
||||||
|
int32 newKey = entry->fCPUBoundThreads * thread_max_threads();
|
||||||
|
newKey += entry->fThreads;
|
||||||
|
sCoreThreadHeap->ModifyKey(entry, newKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
affine_increase_penalty(Thread* thread)
|
affine_increase_penalty(Thread* thread)
|
||||||
{
|
{
|
||||||
@@ -455,6 +505,13 @@ affine_increase_penalty(Thread* thread)
|
|||||||
ASSERT(thread->priority - oldPenalty >= B_LOWEST_ACTIVE_PRIORITY);
|
ASSERT(thread->priority - oldPenalty >= B_LOWEST_ACTIVE_PRIORITY);
|
||||||
const int kMinimalPriority = affine_get_minimal_priority(thread);
|
const int kMinimalPriority = affine_get_minimal_priority(thread);
|
||||||
if (thread->priority - oldPenalty <= kMinimalPriority) {
|
if (thread->priority - oldPenalty <= kMinimalPriority) {
|
||||||
|
int32 core = schedulerThreadData->previous_core;
|
||||||
|
ASSERT(core >= 0);
|
||||||
|
if (schedulerThreadData->additional_penalty == 0) {
|
||||||
|
sCoreEntries[core].fCPUBoundThreads++;
|
||||||
|
affine_update_thread_heaps(core);
|
||||||
|
}
|
||||||
|
|
||||||
schedulerThreadData->priority_penalty = oldPenalty;
|
schedulerThreadData->priority_penalty = oldPenalty;
|
||||||
schedulerThreadData->additional_penalty++;
|
schedulerThreadData->additional_penalty++;
|
||||||
}
|
}
|
||||||
@@ -468,31 +525,20 @@ affine_cancel_penalty(Thread* thread)
|
|||||||
|
|
||||||
if (schedulerThreadData->priority_penalty != 0)
|
if (schedulerThreadData->priority_penalty != 0)
|
||||||
TRACE("cancelling thread %ld penalty\n", thread->id);
|
TRACE("cancelling thread %ld penalty\n", thread->id);
|
||||||
|
|
||||||
|
if (schedulerThreadData->additional_penalty != 0) {
|
||||||
|
int32 core = schedulerThreadData->previous_core;
|
||||||
|
ASSERT(core >= 0);
|
||||||
|
|
||||||
|
sCoreEntries[core].fCPUBoundThreads--;
|
||||||
|
affine_update_thread_heaps(core);
|
||||||
|
}
|
||||||
|
|
||||||
schedulerThreadData->priority_penalty = 0;
|
schedulerThreadData->priority_penalty = 0;
|
||||||
schedulerThreadData->additional_penalty = 0;
|
schedulerThreadData->additional_penalty = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Returns the most idle CPU based on the active time counters.
|
|
||||||
Note: thread lock must be held when entering this function
|
|
||||||
*/
|
|
||||||
#if 0
|
|
||||||
static int32
|
|
||||||
affine_get_most_idle_cpu()
|
|
||||||
{
|
|
||||||
int32 targetCPU = -1;
|
|
||||||
for (int32 i = 0; i < smp_get_num_cpus(); i++) {
|
|
||||||
if (gCPU[i].disabled)
|
|
||||||
continue;
|
|
||||||
if (targetCPU < 0 || sRunQueueSize[i] < sRunQueueSize[targetCPU])
|
|
||||||
targetCPU = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return targetCPU;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
affine_update_priority_heaps(int32 cpu, int32 priority)
|
affine_update_priority_heaps(int32 cpu, int32 priority)
|
||||||
{
|
{
|
||||||
@@ -502,7 +548,7 @@ affine_update_priority_heaps(int32 cpu, int32 priority)
|
|||||||
|
|
||||||
int32 maxPriority
|
int32 maxPriority
|
||||||
= AffineCPUHeap::GetKey(sCPUPriorityHeaps[core].PeekMaximum());
|
= AffineCPUHeap::GetKey(sCPUPriorityHeaps[core].PeekMaximum());
|
||||||
int32 corePriority = AffineCoreHeap::GetKey(&sCoreEntries[core]);
|
int32 corePriority = AffineCorePriorityHeap::GetKey(&sCoreEntries[core]);
|
||||||
|
|
||||||
if (corePriority != maxPriority) {
|
if (corePriority != maxPriority) {
|
||||||
sCorePriorityHeap->ModifyKey(&sCoreEntries[core], maxPriority);
|
sCorePriorityHeap->ModifyKey(&sCoreEntries[core], maxPriority);
|
||||||
@@ -567,7 +613,7 @@ affine_update_priority_heaps(int32 cpu, int32 priority)
|
|||||||
|
|
||||||
|
|
||||||
static int32
|
static int32
|
||||||
affine_choose_core_performance(void)
|
affine_choose_core_performance(int32 priority)
|
||||||
{
|
{
|
||||||
CoreEntry* entry;
|
CoreEntry* entry;
|
||||||
|
|
||||||
@@ -582,6 +628,8 @@ affine_choose_core_performance(void)
|
|||||||
} else {
|
} else {
|
||||||
// no idle cores, use least occupied core
|
// no idle cores, use least occupied core
|
||||||
entry = sCorePriorityHeap->PeekRoot();
|
entry = sCorePriorityHeap->PeekRoot();
|
||||||
|
if (AffineCorePriorityHeap::GetKey(entry) >= priority)
|
||||||
|
entry = sCoreThreadHeap->PeekRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(entry != NULL);
|
ASSERT(entry != NULL);
|
||||||
@@ -590,7 +638,7 @@ affine_choose_core_performance(void)
|
|||||||
|
|
||||||
|
|
||||||
static int32
|
static int32
|
||||||
affine_choose_core_power_saving(void)
|
affine_choose_core_power_saving(int32 priority)
|
||||||
{
|
{
|
||||||
CoreEntry* entry;
|
CoreEntry* entry;
|
||||||
|
|
||||||
@@ -606,6 +654,8 @@ affine_choose_core_power_saving(void)
|
|||||||
} else {
|
} else {
|
||||||
// no idle cores, use least occupied core
|
// no idle cores, use least occupied core
|
||||||
entry = sCorePriorityHeap->PeekRoot();
|
entry = sCorePriorityHeap->PeekRoot();
|
||||||
|
if (AffineCorePriorityHeap::GetKey(entry) >= priority)
|
||||||
|
entry = sCoreThreadHeap->PeekRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(entry != NULL);
|
ASSERT(entry != NULL);
|
||||||
@@ -614,9 +664,9 @@ affine_choose_core_power_saving(void)
|
|||||||
|
|
||||||
|
|
||||||
static inline int32
|
static inline int32
|
||||||
affine_choose_core(void)
|
affine_choose_core(int32 priority)
|
||||||
{
|
{
|
||||||
return sChooseCore();
|
return sChooseCore(priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -629,6 +679,33 @@ affine_choose_cpu(int32 core)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
affine_assign_thread_to_core(Thread* thread, int32 targetCore)
|
||||||
|
{
|
||||||
|
scheduler_thread_data* schedulerThreadData = thread->scheduler_data;
|
||||||
|
|
||||||
|
int32 oldCore = schedulerThreadData->previous_core;
|
||||||
|
if (oldCore == targetCore)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (oldCore >= 0) {
|
||||||
|
sCoreEntries[oldCore].fThreads--;
|
||||||
|
if (schedulerThreadData->additional_penalty != 0)
|
||||||
|
sCoreEntries[oldCore].fCPUBoundThreads--;
|
||||||
|
|
||||||
|
affine_update_thread_heaps(oldCore);
|
||||||
|
}
|
||||||
|
|
||||||
|
schedulerThreadData->previous_core = targetCore;
|
||||||
|
if (targetCore >= 0) {
|
||||||
|
sCoreEntries[targetCore].fThreads++;
|
||||||
|
if (schedulerThreadData->additional_penalty != 0)
|
||||||
|
sCoreEntries[targetCore].fCPUBoundThreads++;
|
||||||
|
affine_update_thread_heaps(targetCore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
affine_enqueue(Thread* thread, bool newOne)
|
affine_enqueue(Thread* thread, bool newOne)
|
||||||
{
|
{
|
||||||
@@ -658,10 +735,11 @@ affine_enqueue(Thread* thread, bool newOne)
|
|||||||
targetCPU = thread->previous_cpu->cpu_num;
|
targetCPU = thread->previous_cpu->cpu_num;
|
||||||
targetCore = sCPUToCore[targetCPU];
|
targetCore = sCPUToCore[targetCPU];
|
||||||
} else {
|
} else {
|
||||||
targetCore = affine_choose_core();
|
targetCore = affine_choose_core(threadPriority);
|
||||||
targetCPU = affine_choose_cpu(targetCore);
|
targetCPU = affine_choose_cpu(targetCore);
|
||||||
}
|
}
|
||||||
schedulerThreadData->previous_core = targetCore;
|
|
||||||
|
affine_assign_thread_to_core(thread, targetCore);
|
||||||
} else {
|
} else {
|
||||||
targetCore = schedulerThreadData->previous_core;
|
targetCore = schedulerThreadData->previous_core;
|
||||||
targetCPU = affine_choose_cpu(targetCore);
|
targetCPU = affine_choose_cpu(targetCore);
|
||||||
@@ -1068,6 +1146,7 @@ affine_reschedule(void)
|
|||||||
TRACE("reschedule(): suspending thread %ld\n", oldThread->id);
|
TRACE("reschedule(): suspending thread %ld\n", oldThread->id);
|
||||||
break;
|
break;
|
||||||
case THREAD_STATE_FREE_ON_RESCHED:
|
case THREAD_STATE_FREE_ON_RESCHED:
|
||||||
|
affine_assign_thread_to_core(oldThread, -1);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
affine_thread_goes_sleep(oldThread, thisCore);
|
affine_thread_goes_sleep(oldThread, thisCore);
|
||||||
@@ -1302,13 +1381,10 @@ scheduler_affine_init()
|
|||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
ArrayDeleter<PackageEntry> packageEntriesDeleter(sPackageEntries);
|
ArrayDeleter<PackageEntry> packageEntriesDeleter(sPackageEntries);
|
||||||
|
|
||||||
sPackageUsageHeap = new(std::nothrow) AffinePackageHeap;
|
sPackageUsageHeap = new(std::nothrow) AffinePackageHeap(packageCount);
|
||||||
if (sPackageUsageHeap == NULL)
|
if (sPackageUsageHeap == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
ObjectDeleter<AffinePackageHeap> packageHeapDeleter(sPackageUsageHeap);
|
ObjectDeleter<AffinePackageHeap> packageHeapDeleter(sPackageUsageHeap);
|
||||||
result = sPackageUsageHeap->GrowHeap(packageCount);
|
|
||||||
if (result != B_OK)
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
sIdlePackageList = new(std::nothrow) AffineIdlePackageList;
|
sIdlePackageList = new(std::nothrow) AffineIdlePackageList;
|
||||||
if (sIdlePackageList == NULL)
|
if (sIdlePackageList == NULL)
|
||||||
@@ -1334,18 +1410,31 @@ scheduler_affine_init()
|
|||||||
ArrayDeleter<CoreEntry> coreEntriesDeleter(
|
ArrayDeleter<CoreEntry> coreEntriesDeleter(
|
||||||
sCoreEntries);
|
sCoreEntries);
|
||||||
|
|
||||||
sCorePriorityHeap = new AffineCoreHeap;
|
sCorePriorityHeap = new AffineCorePriorityHeap;
|
||||||
if (sCorePriorityHeap == NULL)
|
if (sCorePriorityHeap == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
ObjectDeleter<AffineCoreHeap> corePriorityHeapDeleter(sCorePriorityHeap);
|
ObjectDeleter<AffineCorePriorityHeap> corePriorityHeapDeleter(
|
||||||
|
sCorePriorityHeap);
|
||||||
|
|
||||||
|
sCoreThreadHeap = new AffineCoreThreadHeap;
|
||||||
|
if (sCoreThreadHeap == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
ObjectDeleter<AffineCoreThreadHeap> coreThreadHeapDeleter(sCoreThreadHeap);
|
||||||
|
|
||||||
for (int32 i = 0; i < coreCount; i++) {
|
for (int32 i = 0; i < coreCount; i++) {
|
||||||
sCoreEntries[i].fCoreID = i;
|
sCoreEntries[i].fCoreID = i;
|
||||||
sCoreEntries[i].fActiveTime = 0;
|
sCoreEntries[i].fActiveTime = 0;
|
||||||
|
sCoreEntries[i].fThreads = 0;
|
||||||
|
sCoreEntries[i].fCPUBoundThreads = 0;
|
||||||
|
|
||||||
status_t result = sCorePriorityHeap->Insert(&sCoreEntries[i],
|
status_t result = sCorePriorityHeap->Insert(&sCoreEntries[i],
|
||||||
B_IDLE_PRIORITY);
|
B_IDLE_PRIORITY);
|
||||||
if (result != B_OK)
|
if (result != B_OK)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
result = sCoreThreadHeap->Insert(&sCoreEntries[i], 0);
|
||||||
|
if (result != B_OK)
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
sCPUPriorityHeaps = new AffineCPUHeap[coreCount];
|
sCPUPriorityHeaps = new AffineCPUHeap[coreCount];
|
||||||
@@ -1408,6 +1497,7 @@ scheduler_affine_init()
|
|||||||
|
|
||||||
runQueuesDeleter.Detach();
|
runQueuesDeleter.Detach();
|
||||||
pinnedRunQueuesDeleter.Detach();
|
pinnedRunQueuesDeleter.Detach();
|
||||||
|
coreThreadHeapDeleter.Detach();
|
||||||
corePriorityHeapDeleter.Detach();
|
corePriorityHeapDeleter.Detach();
|
||||||
cpuPriorityHeapDeleter.Detach();
|
cpuPriorityHeapDeleter.Detach();
|
||||||
coreEntriesDeleter.Detach();
|
coreEntriesDeleter.Detach();
|
||||||
|
|||||||
Reference in New Issue
Block a user