зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1629064 - pt 4. Add a pref for the maximum number of concurrent GCs r=smaug
Differential Revision: https://phabricator.services.mozilla.com/D105957
This commit is contained in:
Родитель
ca5b034b04
Коммит
883b7bec01
|
@ -5,6 +5,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "mozilla/StaticPrefs_page_load.h"
|
#include "mozilla/StaticPrefs_page_load.h"
|
||||||
|
#include "mozilla/StaticPrefs_javascript.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
#include "mozilla/ipc/IdleSchedulerParent.h"
|
#include "mozilla/ipc/IdleSchedulerParent.h"
|
||||||
#include "nsSystemInfo.h"
|
#include "nsSystemInfo.h"
|
||||||
|
@ -19,52 +20,93 @@ base::SharedMemory* IdleSchedulerParent::sActiveChildCounter = nullptr;
|
||||||
std::bitset<NS_IDLE_SCHEDULER_COUNTER_ARRAY_LENGHT>
|
std::bitset<NS_IDLE_SCHEDULER_COUNTER_ARRAY_LENGHT>
|
||||||
IdleSchedulerParent::sInUseChildCounters;
|
IdleSchedulerParent::sInUseChildCounters;
|
||||||
LinkedList<IdleSchedulerParent> IdleSchedulerParent::sIdleAndGCRequests;
|
LinkedList<IdleSchedulerParent> IdleSchedulerParent::sIdleAndGCRequests;
|
||||||
Atomic<int32_t> IdleSchedulerParent::sMaxConcurrentIdleTasksInChildProcesses(
|
int32_t IdleSchedulerParent::sMaxConcurrentIdleTasksInChildProcesses = 1;
|
||||||
-1);
|
|
||||||
uint32_t IdleSchedulerParent::sMaxConcurrentGCs = 1;
|
uint32_t IdleSchedulerParent::sMaxConcurrentGCs = 1;
|
||||||
uint32_t IdleSchedulerParent::sActiveGCs = 0;
|
uint32_t IdleSchedulerParent::sActiveGCs = 0;
|
||||||
uint32_t IdleSchedulerParent::sChildProcessesRunningPrioritizedOperation = 0;
|
uint32_t IdleSchedulerParent::sChildProcessesRunningPrioritizedOperation = 0;
|
||||||
uint32_t IdleSchedulerParent::sChildProcessesAlive = 0;
|
uint32_t IdleSchedulerParent::sChildProcessesAlive = 0;
|
||||||
nsITimer* IdleSchedulerParent::sStarvationPreventer = nullptr;
|
nsITimer* IdleSchedulerParent::sStarvationPreventer = nullptr;
|
||||||
|
|
||||||
|
uint32_t IdleSchedulerParent::sNumCPUs = 0;
|
||||||
|
uint32_t IdleSchedulerParent::sPrefConcurrentGCsMax = 0;
|
||||||
|
uint32_t IdleSchedulerParent::sPrefConcurrentGCsCPUDivisor = 0;
|
||||||
|
|
||||||
IdleSchedulerParent::IdleSchedulerParent() {
|
IdleSchedulerParent::IdleSchedulerParent() {
|
||||||
sChildProcessesAlive++;
|
sChildProcessesAlive++;
|
||||||
|
|
||||||
if (sMaxConcurrentIdleTasksInChildProcesses == -1) {
|
uint32_t max_gcs_pref =
|
||||||
|
StaticPrefs::javascript_options_concurrent_multiprocess_gcs_max();
|
||||||
|
uint32_t cpu_divisor_pref =
|
||||||
|
StaticPrefs::javascript_options_concurrent_multiprocess_gcs_cpu_divisor();
|
||||||
|
if (!max_gcs_pref) {
|
||||||
|
max_gcs_pref = UINT32_MAX;
|
||||||
|
}
|
||||||
|
if (!cpu_divisor_pref) {
|
||||||
|
cpu_divisor_pref = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sNumCPUs) {
|
||||||
|
// While waiting for the real logical core count behave as if there was
|
||||||
|
// just one core.
|
||||||
|
sNumCPUs = 1;
|
||||||
|
|
||||||
// nsISystemInfo can be initialized only on the main thread.
|
// nsISystemInfo can be initialized only on the main thread.
|
||||||
// While waiting for the real logical core count behave as if there was just
|
|
||||||
// one core.
|
|
||||||
sMaxConcurrentIdleTasksInChildProcesses = 1;
|
|
||||||
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
||||||
nsCOMPtr<nsIRunnable> runnable =
|
nsCOMPtr<nsIRunnable> runnable =
|
||||||
NS_NewRunnableFunction("cpucount getter", [thread]() {
|
NS_NewRunnableFunction("cpucount getter", [thread]() {
|
||||||
// Always pretend that there is at least one core for child processes.
|
|
||||||
// If there are multiple logical cores, reserve one for the parent
|
|
||||||
// process and for the non-main threads.
|
|
||||||
ProcessInfo processInfo = {};
|
ProcessInfo processInfo = {};
|
||||||
if (NS_SUCCEEDED(CollectProcessInfo(processInfo)) &&
|
if (NS_SUCCEEDED(CollectProcessInfo(processInfo))) {
|
||||||
processInfo.cpuCount > 1) {
|
uint32_t num_cpus = processInfo.cpuCount;
|
||||||
// On one and two processor (or hardware thread) systems this will
|
// We have a new cpu count, Update the number of idle tasks.
|
||||||
// allow one concurrent idle task.
|
nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction(
|
||||||
sMaxConcurrentIdleTasksInChildProcesses =
|
"IdleSchedulerParent::CalculateNumIdleTasks", [num_cpus]() {
|
||||||
std::max(processInfo.cpuCount - 1, 1);
|
// We're setting this within this lambda because it's run on
|
||||||
sMaxConcurrentGCs = std::max(processInfo.cpuCount / 4, 1);
|
// the correct thread and avoids a race.
|
||||||
// We have a new cpu count, reschedule idle scheduler.
|
sNumCPUs = num_cpus;
|
||||||
nsCOMPtr<nsIRunnable> runnable =
|
|
||||||
NS_NewRunnableFunction("IdleSchedulerParent::Schedule", []() {
|
// This reads the sPrefConcurrentGCsMax and
|
||||||
if (sActiveChildCounter && sActiveChildCounter->memory()) {
|
// sPrefConcurrentGCsCPUDivisor values set below, it will run
|
||||||
static_cast<Atomic<int32_t>*>(sActiveChildCounter->memory())
|
// after the code that sets those.
|
||||||
[NS_IDLE_SCHEDULER_INDEX_OF_CPU_COUNTER] =
|
CalculateNumIdleTasks();
|
||||||
static_cast<int32_t>(
|
|
||||||
sMaxConcurrentIdleTasksInChildProcesses);
|
|
||||||
}
|
|
||||||
IdleSchedulerParent::Schedule(nullptr);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
thread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
thread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
NS_DispatchBackgroundTask(runnable.forget(), NS_DISPATCH_EVENT_MAY_BLOCK);
|
NS_DispatchBackgroundTask(runnable.forget(), NS_DISPATCH_EVENT_MAY_BLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sPrefConcurrentGCsMax != max_gcs_pref ||
|
||||||
|
sPrefConcurrentGCsCPUDivisor != cpu_divisor_pref) {
|
||||||
|
// We execute this if these preferences have changed. We also want to make
|
||||||
|
// sure it executes for the first IdleSchedulerParent, which it does because
|
||||||
|
// sPrefConcurrentGCsMax and sPrefConcurrentGCsCPUDivisor are initially
|
||||||
|
// zero.
|
||||||
|
sPrefConcurrentGCsMax = max_gcs_pref;
|
||||||
|
sPrefConcurrentGCsCPUDivisor = cpu_divisor_pref;
|
||||||
|
|
||||||
|
CalculateNumIdleTasks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IdleSchedulerParent::CalculateNumIdleTasks() {
|
||||||
|
MOZ_ASSERT(sNumCPUs);
|
||||||
|
MOZ_ASSERT(sPrefConcurrentGCsMax);
|
||||||
|
MOZ_ASSERT(sPrefConcurrentGCsCPUDivisor);
|
||||||
|
|
||||||
|
// On one and two processor (or hardware thread) systems this will
|
||||||
|
// allow one concurrent idle task.
|
||||||
|
sMaxConcurrentIdleTasksInChildProcesses = int32_t(std::max(sNumCPUs, 1u));
|
||||||
|
sMaxConcurrentGCs =
|
||||||
|
std::min(std::max(sNumCPUs / sPrefConcurrentGCsCPUDivisor, 1u),
|
||||||
|
sPrefConcurrentGCsMax);
|
||||||
|
|
||||||
|
if (sActiveChildCounter && sActiveChildCounter->memory()) {
|
||||||
|
static_cast<Atomic<int32_t>*>(
|
||||||
|
sActiveChildCounter->memory())[NS_IDLE_SCHEDULER_INDEX_OF_CPU_COUNTER] =
|
||||||
|
static_cast<int32_t>(sMaxConcurrentIdleTasksInChildProcesses);
|
||||||
|
}
|
||||||
|
IdleSchedulerParent::Schedule(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
IdleSchedulerParent::~IdleSchedulerParent() {
|
IdleSchedulerParent::~IdleSchedulerParent() {
|
||||||
|
|
|
@ -47,6 +47,8 @@ class IdleSchedulerParent final
|
||||||
IdleSchedulerParent();
|
IdleSchedulerParent();
|
||||||
~IdleSchedulerParent();
|
~IdleSchedulerParent();
|
||||||
|
|
||||||
|
static void CalculateNumIdleTasks();
|
||||||
|
|
||||||
static int32_t ActiveCount();
|
static int32_t ActiveCount();
|
||||||
static void Schedule(IdleSchedulerParent* aRequester);
|
static void Schedule(IdleSchedulerParent* aRequester);
|
||||||
static bool HasSpareCycles(int32_t aActiveCount);
|
static bool HasSpareCycles(int32_t aActiveCount);
|
||||||
|
@ -104,7 +106,7 @@ class IdleSchedulerParent final
|
||||||
// mRequestingGC and mDoingGC fields for the GC state.
|
// mRequestingGC and mDoingGC fields for the GC state.
|
||||||
static LinkedList<IdleSchedulerParent> sIdleAndGCRequests;
|
static LinkedList<IdleSchedulerParent> sIdleAndGCRequests;
|
||||||
|
|
||||||
static Atomic<int32_t> sMaxConcurrentIdleTasksInChildProcesses;
|
static int32_t sMaxConcurrentIdleTasksInChildProcesses;
|
||||||
static uint32_t sMaxConcurrentGCs;
|
static uint32_t sMaxConcurrentGCs;
|
||||||
static uint32_t sActiveGCs;
|
static uint32_t sActiveGCs;
|
||||||
|
|
||||||
|
@ -116,6 +118,10 @@ class IdleSchedulerParent final
|
||||||
static uint32_t sChildProcessesAlive;
|
static uint32_t sChildProcessesAlive;
|
||||||
|
|
||||||
static nsITimer* sStarvationPreventer;
|
static nsITimer* sStarvationPreventer;
|
||||||
|
|
||||||
|
static uint32_t sNumCPUs;
|
||||||
|
static uint32_t sPrefConcurrentGCsMax;
|
||||||
|
static uint32_t sPrefConcurrentGCsCPUDivisor;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|
|
@ -5756,6 +5756,19 @@
|
||||||
value: @IS_NOT_ANDROID@
|
value: @IS_NOT_ANDROID@
|
||||||
mirror: always
|
mirror: always
|
||||||
|
|
||||||
|
# We allow at most MIN(max, MAX(NUM_CPUS / cpu_divisor, 1)) concurrent GCs
|
||||||
|
# between processes
|
||||||
|
- name: javascript.options.concurrent_multiprocess_gcs.cpu_divisor
|
||||||
|
type: RelaxedAtomicUint32
|
||||||
|
value: 4
|
||||||
|
mirror: always
|
||||||
|
|
||||||
|
# See 'cpu_divisor' above, 0 means UINT_32_MAX.
|
||||||
|
- name: javascript.options.concurrent_multiprocess_gcs.max
|
||||||
|
type: RelaxedAtomicUint32
|
||||||
|
value: 0
|
||||||
|
mirror: always
|
||||||
|
|
||||||
- name: javascript.options.mem.log
|
- name: javascript.options.mem.log
|
||||||
type: bool
|
type: bool
|
||||||
value: false
|
value: false
|
||||||
|
|
Загрузка…
Ссылка в новой задаче