From: Ben Adams Date: Thu, 31 Oct 2019 09:47:57 +0000 (+0000) Subject: Don't needlessly refresh proc count (dotnet/coreclr#27543) X-Git-Tag: submit/tizen/20210909.063632~11030^2~175 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4282f41265c0118247cd7cce5f2382f0f2039a4f;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Don't needlessly refresh proc count (dotnet/coreclr#27543) Commit migrated from https://github.com/dotnet/coreclr/commit/946e57b61a4db9ccb574fc3a754f783208080947 --- diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Environment.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Environment.CoreCLR.cs index 5ff86e0..7dc2f38 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Environment.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Environment.CoreCLR.cs @@ -80,11 +80,6 @@ namespace System GetCommandLineArgsNative(); } - // Unconditionally return false since .NET Core does not support object finalization during shutdown. - public static bool HasShutdownStarted => false; - - public static int ProcessorCount => GetProcessorCount(); - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] private static extern int GetProcessorCount(); diff --git a/src/coreclr/tests/src/JIT/Directed/pinvoke/pinvoke-examples.cs b/src/coreclr/tests/src/JIT/Directed/pinvoke/pinvoke-examples.cs index 26080d8..d25a24e 100644 --- a/src/coreclr/tests/src/JIT/Directed/pinvoke/pinvoke-examples.cs +++ b/src/coreclr/tests/src/JIT/Directed/pinvoke/pinvoke-examples.cs @@ -6,6 +6,7 @@ // along with the impact of EH. using System; +using System.Threading; using System.Runtime.CompilerServices; @@ -14,20 +15,20 @@ namespace PInvokeTest internal class Test { [MethodImpl(MethodImplOptions.AggressiveInlining)] - static int AsForceInline() + static bool AsForceInline() { - return Environment.ProcessorCount; + return Thread.Yield(); } - static int AsNormalInline() + static bool AsNormalInline() { - return Environment.ProcessorCount; + return Thread.Yield(); } [MethodImpl(MethodImplOptions.NoInlining)] - static int AsNoInline() + static bool AsNoInline() { - return Environment.ProcessorCount; + return Thread.Yield(); } static bool FromTryCatch() @@ -36,7 +37,7 @@ namespace PInvokeTest try { // All pinvokes should be inline, except on x64 - result = (Environment.ProcessorCount == AsNormalInline()); + result = (Thread.Yield() == AsNormalInline()); } catch (Exception) { @@ -53,8 +54,8 @@ namespace PInvokeTest try { // All pinvokes should be inline, except on x64 - result1 = (Environment.ProcessorCount == AsNormalInline()); - result2 = (Environment.ProcessorCount == AsNormalInline()); + result1 = (Thread.Yield() == AsNormalInline()); + result2 = (Thread.Yield() == AsNormalInline()); } finally { @@ -72,12 +73,12 @@ namespace PInvokeTest try { // These two pinvokes should be inline, except on x64 - result1 = (Environment.ProcessorCount == AsNormalInline()); + result1 = (Thread.Yield() == AsNormalInline()); } finally { // These two pinvokes should *not* be inline (finally) - result2 = (Environment.ProcessorCount == AsNormalInline()); + result2 = (Thread.Yield() == AsNormalInline()); result = result1 && result2; } @@ -93,14 +94,14 @@ namespace PInvokeTest try { // These two pinvokes should be inline, except on x64 - result1 = (Environment.ProcessorCount == AsNormalInline()); + result1 = (Thread.Yield() == AsNormalInline()); } finally { try { // These two pinvokes should *not* be inline (finally) - result2 = (Environment.ProcessorCount == AsNormalInline()); + result2 = (Thread.Yield() == AsNormalInline()); } catch (Exception) { @@ -117,7 +118,7 @@ namespace PInvokeTest static bool FromInline() { // These two pinvokes should be inline - bool result = (Environment.ProcessorCount == AsForceInline()); + bool result = (Thread.Yield() == AsForceInline()); return result; } @@ -125,8 +126,8 @@ namespace PInvokeTest static bool FromInline2() { // These four pinvokes should be inline - bool result1 = (Environment.ProcessorCount == AsNormalInline()); - bool result2 = (Environment.ProcessorCount == AsForceInline()); + bool result1 = (Thread.Yield() == AsNormalInline()); + bool result2 = (Thread.Yield() == AsForceInline()); return result1 && result2; } @@ -134,7 +135,7 @@ namespace PInvokeTest static bool FromNoInline() { // The only pinvoke should be inline - bool result = (Environment.ProcessorCount == AsNoInline()); + bool result = (Thread.Yield() == AsNoInline()); return result; } @@ -142,8 +143,8 @@ namespace PInvokeTest static bool FromNoInline2() { // Three pinvokes should be inline - bool result1 = (Environment.ProcessorCount == AsNormalInline()); - bool result2 = (Environment.ProcessorCount == AsNoInline()); + bool result1 = (Thread.Yield() == AsNormalInline()); + bool result2 = (Thread.Yield() == AsNoInline()); return result1 && result2; } @@ -161,10 +162,10 @@ namespace PInvokeTest // it just calls get_ProcessorCount. // // For the second call, the force inline works, and the - // subsequent inline of get_ProcessorCount exposes a call - // to the pinvoke GetProcessorCount. This pinvoke will + // subsequent inline of Thread.Yield exposes a call + // to the pinvoke YieldInternal. This pinvoke will // not be inline. - catch (Exception) when (Environment.ProcessorCount == AsForceInline()) + catch (Exception) when (Thread.Yield() == AsForceInline()) { result = true; } @@ -174,14 +175,14 @@ namespace PInvokeTest static bool FromColdCode() { - int pc = 0; + bool yield = false; bool result1 = false; bool result2 = false; try { // This pinvoke should not be inline (cold) - pc = Environment.ProcessorCount; + yield = Thread.Yield(); throw new Exception("expected"); } catch (Exception) @@ -189,14 +190,14 @@ namespace PInvokeTest // These two pinvokes should not be inline (catch) // // For the first call the jit won't inline the - // wrapper, so it just calls get_ProcessorCount. + // wrapper, so it just calls Thread.Yield. // // For the second call, the force inline works, and - // the subsequent inline of get_ProcessorCount exposes - // a call to the pinvoke GetProcessorCount. This + // the subsequent inline of Thread.Yield exposes + // a call to the pinvoke YieldInternal. This // pinvoke will not be inline. - result1 = (pc == Environment.ProcessorCount); - result2 = (pc == AsForceInline()); + result1 = (yield == Thread.Yield()); + result2 = (yield == AsForceInline()); } return result1 && result2; diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.cs index ea04f10..1454f1d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.cs @@ -11,6 +11,16 @@ namespace System { public static partial class Environment { + public static int ProcessorCount { get; } = GetProcessorCount(); + + /// + /// Gets whether the current machine has only a single processor. + /// + internal static bool IsSingleProcessor => ProcessorCount == 1; + + // Unconditionally return false since .NET Core does not support object finalization during shutdown. + public static bool HasShutdownStarted => false; + public static string? GetEnvironmentVariable(string variable) { if (variable == null) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs index 1ed5c02..e90d775 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs @@ -726,7 +726,7 @@ namespace System.Threading /// A power of 2 representing the number of partitions to use. private static int GetPartitionCount() { - int procs = PlatformHelper.ProcessorCount; + int procs = Environment.ProcessorCount; int count = procs > 8 ? 16 : // capped at 16 to limit memory usage on larger machines procs > 4 ? 8 : diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.cs index a02dbd8..4e81254 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.cs @@ -88,7 +88,7 @@ namespace System.Threading counts = countsBeforeUpdate; } - int processorCount = PlatformHelper.ProcessorCount; + int processorCount = Environment.ProcessorCount; int spinIndex = processorCount > 1 ? 0 : SpinSleep0Threshold; while (spinIndex < _spinCount) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ManualResetEventSlim.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ManualResetEventSlim.cs index e804d38..f15e4ef 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ManualResetEventSlim.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ManualResetEventSlim.cs @@ -202,7 +202,7 @@ namespace System.Threading Debug.Assert(DEFAULT_SPIN_SP >= 0, "Internal error - DEFAULT_SPIN_SP is outside the legal range."); Debug.Assert(DEFAULT_SPIN_SP <= SpinCountState_MaxValue, "Internal error - DEFAULT_SPIN_SP is outside the legal range."); - SpinCount = PlatformHelper.IsSingleProcessor ? DEFAULT_SPIN_SP : spinCount; + SpinCount = Environment.IsSingleProcessor ? DEFAULT_SPIN_SP : spinCount; } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs index 3ea1c15..bad6e1f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs @@ -68,7 +68,7 @@ namespace System.Threading private PortableThreadPool() { - _minThreads = s_forcedMinWorkerThreads > 0 ? s_forcedMinWorkerThreads : (short)ThreadPoolGlobals.processorCount; + _minThreads = s_forcedMinWorkerThreads > 0 ? s_forcedMinWorkerThreads : (short)Environment.ProcessorCount; if (_minThreads > MaxPossibleThreadCount) { _minThreads = MaxPossibleThreadCount; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ReaderWriterLockSlim.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ReaderWriterLockSlim.cs index 6267c67..928f4c6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ReaderWriterLockSlim.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ReaderWriterLockSlim.cs @@ -53,8 +53,6 @@ namespace System.Threading /// public class ReaderWriterLockSlim : IDisposable { - private static readonly int ProcessorCount = Environment.ProcessorCount; - // Specifying if the lock can be reacquired recursively. private readonly bool _fIsReentrant; @@ -1233,7 +1231,7 @@ namespace System.Threading const int LockSpinCycles = 20; // Exponential back-off - if ((spinCount < 5) && (ProcessorCount > 1)) + if ((spinCount < 5) && (Environment.ProcessorCount > 1)) { Thread.SpinWait(LockSpinCycles * spinCount); } @@ -1563,7 +1561,7 @@ namespace System.Threading Interlocked.Add(ref _enterDeprioritizationState, deprioritizationStateChange); } - int processorCount = ProcessorCount; + int processorCount = Environment.ProcessorCount; for (int spinIndex = 0; ; spinIndex++) { if (spinIndex < LockSpinCount && processorCount > 1) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/SpinLock.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/SpinLock.cs index d2717e7..0a222dc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/SpinLock.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/SpinLock.cs @@ -349,7 +349,7 @@ namespace System.Threading // *** Step 2, Spinning and Yielding var spinner = new SpinWait(); - if (turn > PlatformHelper.ProcessorCount) + if (turn > Environment.ProcessorCount) { spinner.Count = SpinWait.YieldThreshold; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/SpinWait.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/SpinWait.cs index 56ee7c4..1d1b68d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/SpinWait.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/SpinWait.cs @@ -86,7 +86,7 @@ namespace System.Threading /// depends on the likelihood of the spin being successful and how long the wait would be but those are not accounted /// for here. /// - internal static readonly int SpinCountforSpinBeforeWait = PlatformHelper.IsSingleProcessor ? 1 : 35; + internal static readonly int SpinCountforSpinBeforeWait = Environment.IsSingleProcessor ? 1 : 35; // The number of times we've spun already. private int _count; @@ -114,7 +114,7 @@ namespace System.Threading /// On a single-CPU machine, always yields the processor. On machines with /// multiple CPUs, may yield after an unspecified number of calls. /// - public bool NextSpinWillYield => _count >= YieldThreshold || PlatformHelper.IsSingleProcessor; + public bool NextSpinWillYield => _count >= YieldThreshold || Environment.IsSingleProcessor; /// /// Performs a single spin. @@ -175,7 +175,7 @@ namespace System.Threading _count >= YieldThreshold && ((_count >= sleep1Threshold && sleep1Threshold >= 0) || (_count - YieldThreshold) % 2 == 0) ) || - PlatformHelper.IsSingleProcessor) + Environment.IsSingleProcessor) { // // We must yield. @@ -346,42 +346,4 @@ namespace System.Threading } #endregion } - - /// - /// A helper class to get the number of processors, it updates the numbers of processors every sampling interval. - /// - internal static class PlatformHelper - { - private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 30000; // How often to refresh the count, in milliseconds. - private static volatile int s_processorCount; // The last count seen. - private static volatile int s_lastProcessorCountRefreshTicks; // The last time we refreshed. - - /// - /// Gets the number of available processors - /// - internal static int ProcessorCount - { - get - { - int now = Environment.TickCount; - int procCount = s_processorCount; - if (procCount == 0 || (now - s_lastProcessorCountRefreshTicks) >= PROCESSOR_COUNT_REFRESH_INTERVAL_MS) - { - s_processorCount = procCount = Environment.ProcessorCount; - s_lastProcessorCountRefreshTicks = now; - } - - Debug.Assert(procCount > 0, - "Processor count should be greater than 0."); - - return procCount; - } - } - - /// - /// Gets whether the current machine has only a single processor. - /// - /// This typically does not change on a machine, so it's checked only once. - internal static readonly bool IsSingleProcessor = ProcessorCount == 1; - } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs index f59785a..68cdc6ce 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -25,8 +25,6 @@ namespace System.Threading { internal static class ThreadPoolGlobals { - public static readonly int processorCount = Environment.ProcessorCount; - public static volatile bool threadPoolInitialized; public static bool enableWorkerTracking; @@ -436,7 +434,7 @@ namespace System.Threading // by the VM by the time we reach this point. // int count = numOutstandingThreadRequests; - while (count < ThreadPoolGlobals.processorCount) + while (count < Environment.ProcessorCount) { int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count + 1, count); if (prev == count)