From 36f6815ae5f1b7d4e8ea576702a1b2908308061b Mon Sep 17 00:00:00 2001 From: Anton Lapounov Date: Tue, 6 Sep 2022 13:40:55 -0700 Subject: [PATCH] Support multiple processor groups for NativeAOT --- src/coreclr/gc/gc.cpp | 7 +-- src/coreclr/gc/gcconfig.cpp | 17 ++++--- src/coreclr/gc/gcconfig.h | 2 + src/coreclr/gc/gcload.cpp | 4 +- src/coreclr/gc/windows/gcenv.windows.cpp | 34 ++++++------- src/coreclr/nativeaot/Runtime/gcrhenv.cpp | 7 --- .../nativeaot/Runtime/unix/PalRedhawkUnix.cpp | 4 ++ .../Runtime/windows/PalRedhawkMinWin.cpp | 48 +++++++++++-------- 8 files changed, 65 insertions(+), 58 deletions(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 207a62355dc738..79a70288a60dd0 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -44804,8 +44804,9 @@ HRESULT GCHeap::Initialize() uint32_t nhp = 1; uint32_t nhp_from_config = 0; -#ifdef MULTIPLE_HEAPS - +#ifndef MULTIPLE_HEAPS + GCConfig::SetServerGC(false); +#else //!MULTIPLE_HEAPS GCConfig::SetServerGC(true); AffinitySet config_affinity_set; GCConfigStringHolder cpu_index_ranges_holder(GCConfig::GetGCHeapAffinitizeRanges()); @@ -44860,7 +44861,7 @@ HRESULT GCHeap::Initialize() nhp = min(nhp, num_affinitized_processors); } } -#endif //MULTIPLE_HEAPS +#endif //!MULTIPLE_HEAPS size_t seg_size = 0; size_t large_seg_size = 0; diff --git a/src/coreclr/gc/gcconfig.cpp b/src/coreclr/gc/gcconfig.cpp index abf4b28e4f5d76..9957e43dc5c5db 100644 --- a/src/coreclr/gc/gcconfig.cpp +++ b/src/coreclr/gc/gcconfig.cpp @@ -7,13 +7,18 @@ #define BOOL_CONFIG(name, unused_private_key, unused_public_key, default, unused_doc) \ bool GCConfig::Get##name() { return s_##name; } \ + bool GCConfig::Get##name(bool defaultValue) \ + { \ + return s_##name##Provided ? s_##name : defaultValue; \ + } \ void GCConfig::Set##name(bool value) { s_Updated##name = value; } \ bool GCConfig::s_##name = default; \ + bool GCConfig::s_##name##Provided = false; \ bool GCConfig::s_Updated##name = default; #define INT_CONFIG(name, unused_private_key, unused_public_key, default, unused_doc) \ int64_t GCConfig::Get##name() { return s_##name; } \ - void GCConfig::Set##name(int64_t value) { s_Updated##name = value; } \ + void GCConfig::Set##name(int64_t value) { s_Updated##name = value; } \ int64_t GCConfig::s_##name = default; \ int64_t GCConfig::s_Updated##name = default; @@ -36,7 +41,7 @@ GC_CONFIGURATION_KEYS void GCConfig::EnumerateConfigurationValues(void* context, ConfigurationValueFunc configurationValueFunc) { -#define INT_CONFIG(name, unused_private_key, public_key, default, unused_doc) \ +#define INT_CONFIG(name, unused_private_key, public_key, unused_default, unused_doc) \ configurationValueFunc(context, (void*)(#name), (void*)(public_key), GCConfigurationType::Int64, static_cast(s_Updated##name)); #define STRING_CONFIG(name, private_key, public_key, unused_doc) \ @@ -47,7 +52,7 @@ void GCConfig::EnumerateConfigurationValues(void* context, ConfigurationValueFun configurationValueFunc(context, (void*)(#name), (void*)(public_key), GCConfigurationType::StringUtf8, reinterpret_cast(resultStr)); \ } -#define BOOL_CONFIG(name, unused_private_key, public_key, default, unused_doc) \ +#define BOOL_CONFIG(name, unused_private_key, public_key, unused_default, unused_doc) \ configurationValueFunc(context, (void*)(#name), (void*)(public_key), GCConfigurationType::Boolean, static_cast(s_Updated##name)); GC_CONFIGURATION_KEYS @@ -59,10 +64,10 @@ GC_CONFIGURATION_KEYS void GCConfig::Initialize() { -#define BOOL_CONFIG(name, private_key, public_key, default, unused_doc) \ - GCToEEInterface::GetBooleanConfigValue(private_key, public_key, &s_##name); +#define BOOL_CONFIG(name, private_key, public_key, unused_default, unused_doc) \ + s_##name##Provided = GCToEEInterface::GetBooleanConfigValue(private_key, public_key, &s_##name); -#define INT_CONFIG(name, private_key, public_key, default, unused_doc) \ +#define INT_CONFIG(name, private_key, public_key, unused_default, unused_doc) \ GCToEEInterface::GetIntConfigValue(private_key, public_key, &s_##name); #define STRING_CONFIG(unused_name, unused_private_key, unused_public_key, unused_doc) diff --git a/src/coreclr/gc/gcconfig.h b/src/coreclr/gc/gcconfig.h index f1111a99eae5f6..78cf1b41234a06 100644 --- a/src/coreclr/gc/gcconfig.h +++ b/src/coreclr/gc/gcconfig.h @@ -142,8 +142,10 @@ class GCConfig { #define BOOL_CONFIG(name, unused_private_key, unused_public_key, unused_default, unused_doc) \ public: static bool Get##name(); \ + public: static bool Get##name(bool defaultValue); \ public: static void Set##name(bool value); \ private: static bool s_##name; \ + private: static bool s_##name##Provided; \ private: static bool s_Updated##name; #define INT_CONFIG(name, unused_private_key, unused_public_key, unused_default, unused_doc) \ diff --git a/src/coreclr/gc/gcload.cpp b/src/coreclr/gc/gcload.cpp index 5a886d36ad3d9e..d12c09d603b809 100644 --- a/src/coreclr/gc/gcload.cpp +++ b/src/coreclr/gc/gcload.cpp @@ -74,11 +74,11 @@ GC_Initialize( assert(clrToGC == nullptr); #endif +#ifndef FEATURE_NATIVEAOT // GCConfig and GCToOSInterface are initialized in PalInit // Initialize GCConfig before anything else - initialization of our // various components may want to query the current configuration. GCConfig::Initialize(); -#ifndef FEATURE_NATIVEAOT // GCToOSInterface is initialized directly if (!GCToOSInterface::Initialize()) { return E_FAIL; @@ -92,7 +92,7 @@ GC_Initialize( } #ifdef FEATURE_SVR_GC - if (GCConfig::GetServerGC()) + if (GCConfig::GetServerGC() && GCToEEInterface::GetCurrentProcessCpuCount() > 1) { #ifdef WRITE_BARRIER_CHECK g_GCShadow = 0; diff --git a/src/coreclr/gc/windows/gcenv.windows.cpp b/src/coreclr/gc/windows/gcenv.windows.cpp index d8675e5e1060bb..76ac01d61cc28a 100644 --- a/src/coreclr/gc/windows/gcenv.windows.cpp +++ b/src/coreclr/gc/windows/gcenv.windows.cpp @@ -61,7 +61,6 @@ struct CPU_Group_Info }; static bool g_fEnableGCCPUGroups; -static bool g_fHadSingleProcessorAtStartup; static DWORD g_nGroups; static DWORD g_nProcessors; static CPU_Group_Info *g_CPUGroupInfoArray; @@ -220,26 +219,26 @@ void InitCPUGroupInfo() g_fEnableGCCPUGroups = false; #if (defined(TARGET_AMD64) || defined(TARGET_ARM64)) - if (!GCConfig::GetGCCpuGroup()) + USHORT groupCount = 0; + + // On Windows 11+ and Windows Server 2022+, a process is no longer restricted to a single processor group by default. + // If more than one processor group is available to the process (a non-affinitized process on Windows 11+), + // default to using multiple processor groups; otherwise, default to using a single processor group. This default + // behavior may be overridden by the configuration value below. + if (GetProcessGroupAffinity(GetCurrentProcess(), &groupCount, NULL) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) + groupCount = 1; + + bool enableGCCPUGroups = GCConfig::GetGCCpuGroup(/* defaultValue */ groupCount > 1); + + if (!enableGCCPUGroups) return; if (!InitCPUGroupInfoArray()) return; - // only enable CPU groups if more than one group exists + // Enable processor groups only if more than one group exists g_fEnableGCCPUGroups = g_nGroups > 1; #endif // TARGET_AMD64 || TARGET_ARM64 - - // Determine if the process is affinitized to a single processor (or if the system has a single processor) - DWORD_PTR processAffinityMask, systemAffinityMask; - if (::GetProcessAffinityMask(::GetCurrentProcess(), &processAffinityMask, &systemAffinityMask)) - { - if (processAffinityMask != 0 && // only one CPU group is involved - (processAffinityMask & (processAffinityMask - 1)) == 0) // only one bit is set - { - g_fHadSingleProcessorAtStartup = true; - } - } } void GetProcessMemoryLoad(LPMEMORYSTATUSEX pMSEX) @@ -475,17 +474,12 @@ size_t GetLogicalProcessorCacheSizeFromOS() return cache_size; } -bool CanEnableGCCPUGroups() -{ - return g_fEnableGCCPUGroups; -} - // Get the CPU group for the specified processor void GetGroupForProcessor(uint16_t processor_number, uint16_t* group_number, uint16_t* group_processor_number) { assert(g_fEnableGCCPUGroups); -#if !defined(FEATURE_NATIVEAOT) && (defined(TARGET_AMD64) || defined(TARGET_ARM64)) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) WORD bTemp = 0; WORD bDiff = processor_number - bTemp; diff --git a/src/coreclr/nativeaot/Runtime/gcrhenv.cpp b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp index 68b7a96c36fa1e..c3c8e15545d74c 100644 --- a/src/coreclr/nativeaot/Runtime/gcrhenv.cpp +++ b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp @@ -1313,13 +1313,6 @@ MethodTable* GCToEEInterface::GetFreeObjectMethodTable() bool GCToEEInterface::GetBooleanConfigValue(const char* privateKey, const char* publicKey, bool* value) { - // these configuration values are given to us via startup flags. - if (strcmp(privateKey, "gcServer") == 0) - { - *value = g_heap_type == GC_HEAP_SVR; - return true; - } - if (strcmp(privateKey, "gcConservative") == 0) { *value = true; diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp index 7ef38f6d464a06..f8be025c062aef 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp @@ -13,6 +13,8 @@ #include "UnixHandle.h" #include #include "gcenv.h" +#include "gcenv.ee.h" +#include "gcconfig.h" #include "holder.h" #include "UnixSignals.h" #include "UnixContext.h" @@ -417,6 +419,8 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalInit() ConfigureSignals(); + GCConfig::Initialize(); + if (!GCToOSInterface::Initialize()) { return false; diff --git a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp index 32c072d05d06a2..39f8ffd00a0cbb 100644 --- a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp @@ -30,6 +30,8 @@ uint32_t PalEventWrite(REGHANDLE arg1, const EVENT_DESCRIPTOR * arg2, uint32_t a } #include "gcenv.h" +#include "gcenv.ee.h" +#include "gcconfig.h" #define REDHAWK_PALEXPORT extern "C" @@ -71,29 +73,36 @@ void InitializeCurrentProcessCpuCount() } else { - DWORD_PTR pmask, smask; - - if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask)) + if (GCToOSInterface::CanEnableGCCPUGroups()) { - count = 1; + count = GCToOSInterface::GetTotalProcessorCount(); } else { - count = 0; + DWORD_PTR pmask, smask; - while (pmask) + if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask)) { - pmask &= (pmask - 1); - count++; + count = 1; + } + else + { + count = 0; + + while (pmask) + { + pmask &= (pmask - 1); + count++; + } + + // GetProcessAffinityMask can return pmask=0 and smask=0 on systems with more + // than 64 processors, which would leave us with a count of 0. Since the GC + // expects there to be at least one processor to run on (and thus at least one + // heap), we'll return 64 here if count is 0, since there are likely a ton of + // processors available in that case. + if (count == 0) + count = 64; } - - // GetProcessAffinityMask can return pmask=0 and smask=0 on systems with more - // than 64 processors, which would leave us with a count of 0. Since the GC - // expects there to be at least one processor to run on (and thus at least one - // heap), we'll return 64 here if count is 0, since there are likely a ton of - // processors available in that case. - if (count == 0) - count = 64; } JOBOBJECT_CPU_RATE_CONTROL_INFORMATION cpuRateControl; @@ -119,10 +128,7 @@ void InitializeCurrentProcessCpuCount() if (0 < maxRate && maxRate < MAXIMUM_CPU_RATE) { - SYSTEM_INFO systemInfo; - GetSystemInfo(&systemInfo); - - DWORD cpuLimit = (maxRate * systemInfo.dwNumberOfProcessors + MAXIMUM_CPU_RATE - 1) / MAXIMUM_CPU_RATE; + DWORD cpuLimit = (maxRate * GCToOSInterface::GetTotalProcessorCount() + MAXIMUM_CPU_RATE - 1) / MAXIMUM_CPU_RATE; if (cpuLimit < count) count = cpuLimit; } @@ -145,6 +151,8 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalInit() return false; } + GCConfig::Initialize(); + if (!GCToOSInterface::Initialize()) { return false;