Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Don't needlessly refresh proc count #27543

Merged
merged 1 commit into from
Oct 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/System.Private.CoreLib/shared/System/Environment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ namespace System
{
public static partial class Environment
{
public static int ProcessorCount { get; } = GetProcessorCount();
benaadams marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Gets whether the current machine has only a single processor.
/// </summary>
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -726,7 +726,7 @@ private void ExecuteCallbackHandlers(bool throwOnFirstException)
/// <returns>A power of 2 representing the number of partitions to use.</returns>
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 :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public bool Wait(int timeoutMs)
counts = countsBeforeUpdate;
}

int processorCount = PlatformHelper.ProcessorCount;
int processorCount = Environment.ProcessorCount;
int spinIndex = processorCount > 1 ? 0 : SpinSleep0Threshold;
while (spinIndex < _spinCount)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ private void Initialize(bool initialState, int spinCount)
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;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ private struct CacheLineSeparated

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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ internal class ReaderWriterCount
/// </summary>
public class ReaderWriterLockSlim : IDisposable
{
private static readonly int ProcessorCount = Environment.ProcessorCount;

// Specifying if the lock can be reacquired recursively.
private readonly bool _fIsReentrant;

Expand Down Expand Up @@ -1233,7 +1231,7 @@ private static void SpinWait(int spinCount)
const int LockSpinCycles = 20;

// Exponential back-off
if ((spinCount < 5) && (ProcessorCount > 1))
if ((spinCount < 5) && (Environment.ProcessorCount > 1))
{
Thread.SpinWait(LockSpinCycles * spinCount);
}
Expand Down Expand Up @@ -1563,7 +1561,7 @@ private void EnterSpin(EnterSpinLockReason reason)
Interlocked.Add(ref _enterDeprioritizationState, deprioritizationStateChange);
}

int processorCount = ProcessorCount;
int processorCount = Environment.ProcessorCount;
for (int spinIndex = 0; ; spinIndex++)
{
if (spinIndex < LockSpinCount && processorCount > 1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken)

// *** Step 2, Spinning and Yielding
var spinner = new SpinWait();
if (turn > PlatformHelper.ProcessorCount)
if (turn > Environment.ProcessorCount)
{
spinner.Count = SpinWait.YieldThreshold;
}
Expand Down
44 changes: 3 additions & 41 deletions src/System.Private.CoreLib/shared/System/Threading/SpinWait.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public struct SpinWait
/// depends on the likelihood of the spin being successful and how long the wait would be but those are not accounted
/// for here.
/// </remarks>
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;
Expand Down Expand Up @@ -114,7 +114,7 @@ internal set
/// On a single-CPU machine, <see cref="SpinOnce()"/> always yields the processor. On machines with
/// multiple CPUs, <see cref="SpinOnce()"/> may yield after an unspecified number of calls.
/// </remarks>
public bool NextSpinWillYield => _count >= YieldThreshold || PlatformHelper.IsSingleProcessor;
public bool NextSpinWillYield => _count >= YieldThreshold || Environment.IsSingleProcessor;

/// <summary>
/// Performs a single spin.
Expand Down Expand Up @@ -175,7 +175,7 @@ private void SpinOnceCore(int sleep1Threshold)
_count >= YieldThreshold &&
((_count >= sleep1Threshold && sleep1Threshold >= 0) || (_count - YieldThreshold) % 2 == 0)
) ||
PlatformHelper.IsSingleProcessor)
Environment.IsSingleProcessor)
{
//
// We must yield.
Expand Down Expand Up @@ -346,42 +346,4 @@ public static bool SpinUntil(Func<bool> condition, int millisecondsTimeout)
}
#endregion
}

/// <summary>
/// A helper class to get the number of processors, it updates the numbers of processors every sampling interval.
/// </summary>
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.

/// <summary>
/// Gets the number of available processors
/// </summary>
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;
}
}

/// <summary>
/// Gets whether the current machine has only a single processor.
/// </summary>
/// <remarks>This typically does not change on a machine, so it's checked only once.</remarks>
internal static readonly bool IsSingleProcessor = ProcessorCount == 1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -436,7 +434,7 @@ internal void EnsureThreadRequested()
// 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)
Expand Down
5 changes: 0 additions & 5 deletions src/System.Private.CoreLib/src/System/Environment.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,6 @@ public static string[] GetCommandLineArgs()
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();

Expand Down
59 changes: 30 additions & 29 deletions tests/src/JIT/Directed/pinvoke/pinvoke-examples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// along with the impact of EH.

using System;
using System.Threading;
using System.Runtime.CompilerServices;


Expand All @@ -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()
Expand All @@ -36,7 +37,7 @@ static bool FromTryCatch()
try
{
// All pinvokes should be inline, except on x64
result = (Environment.ProcessorCount == AsNormalInline());
result = (Thread.Yield() == AsNormalInline());
}
catch (Exception)
{
Expand All @@ -53,8 +54,8 @@ static bool FromTryFinally()
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
{
Expand All @@ -72,12 +73,12 @@ static bool FromTryFinally2()
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;
}

Expand All @@ -93,14 +94,14 @@ static bool FromTryFinally3()
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)
{
Expand All @@ -117,33 +118,33 @@ static bool FromTryFinally3()
static bool FromInline()
{
// These two pinvokes should be inline
bool result = (Environment.ProcessorCount == AsForceInline());
bool result = (Thread.Yield() == AsForceInline());
return result;
}

[MethodImpl(MethodImplOptions.NoInlining)]
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;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static bool FromNoInline()
{
// The only pinvoke should be inline
bool result = (Environment.ProcessorCount == AsNoInline());
bool result = (Thread.Yield() == AsNoInline());
return result;
}

[MethodImpl(MethodImplOptions.NoInlining)]
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;
}

Expand All @@ -161,10 +162,10 @@ static bool FromFilter()
// 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;
}
Expand All @@ -174,29 +175,29 @@ static bool FromFilter()

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)
{
// 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;
Expand Down