diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSourceHelper.Unix.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSourceHelper.Unix.cs index f5c4475bf1dc78..da952d12b4c8a8 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSourceHelper.Unix.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSourceHelper.Unix.cs @@ -8,11 +8,9 @@ namespace System.Diagnostics.Tracing { internal static class RuntimeEventSourceHelper { - private static Interop.Sys.ProcessCpuInformation cpuInfo; + private static Interop.Sys.ProcessCpuInformation s_cpuInfo; - internal static int GetCpuUsage() - { - return Interop.Sys.GetCpuUtilization(ref cpuInfo); - } + internal static int GetCpuUsage() => + Interop.Sys.GetCpuUtilization(ref s_cpuInfo) / Environment.ProcessorCount; } } diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSourceHelper.Windows.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSourceHelper.Windows.cs index 2688c56d3c3f90..ad198646debbf4 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSourceHelper.Windows.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSourceHelper.Windows.cs @@ -6,10 +6,10 @@ namespace System.Diagnostics.Tracing { internal static class RuntimeEventSourceHelper { - private static long prevProcUserTime = 0; - private static long prevProcKernelTime = 0; - private static long prevSystemUserTime = 0; - private static long prevSystemKernelTime = 0; + private static long s_prevProcUserTime = 0; + private static long s_prevProcKernelTime = 0; + private static long s_prevSystemUserTime = 0; + private static long s_prevSystemKernelTime = 0; internal static int GetCpuUsage() { @@ -27,21 +27,21 @@ internal static int GetCpuUsage() return 0; } - if (prevSystemUserTime == 0 && prevSystemKernelTime == 0) // These may be 0 when we report CPU usage for the first time, in which case we should just return 0. + if (s_prevSystemUserTime == 0 && s_prevSystemKernelTime == 0) // These may be 0 when we report CPU usage for the first time, in which case we should just return 0. { cpuUsage = 0; } else { - long totalProcTime = (procUserTime - prevProcUserTime) + (procKernelTime - prevProcKernelTime); - long totalSystemTime = (systemUserTime - prevSystemUserTime) + (systemKernelTime - prevSystemKernelTime); + long totalProcTime = (procUserTime - s_prevProcUserTime) + (procKernelTime - s_prevProcKernelTime); + long totalSystemTime = (systemUserTime - s_prevSystemUserTime) + (systemKernelTime - s_prevSystemKernelTime); cpuUsage = (int)(totalProcTime * 100 / totalSystemTime); } - prevProcUserTime = procUserTime; - prevProcKernelTime = procKernelTime; - prevSystemUserTime = systemUserTime; - prevSystemKernelTime = systemKernelTime; + s_prevProcUserTime = procUserTime; + s_prevProcKernelTime = procKernelTime; + s_prevSystemUserTime = systemUserTime; + s_prevSystemKernelTime = systemKernelTime; return cpuUsage; } diff --git a/src/libraries/Native/Unix/System.Native/pal_time.c b/src/libraries/Native/Unix/System.Native/pal_time.c index 8f04a5a27d6b08..ce22f91ff2e6df 100644 --- a/src/libraries/Native/Unix/System.Native/pal_time.c +++ b/src/libraries/Native/Unix/System.Native/pal_time.c @@ -93,25 +93,8 @@ uint64_t SystemNative_GetTimestamp() #endif } -#if defined(_ARM_) || defined(_ARM64_) -#define SYSCONF_GET_NUMPROCS _SC_NPROCESSORS_CONF -#else -#define SYSCONF_GET_NUMPROCS _SC_NPROCESSORS_ONLN -#endif - int32_t SystemNative_GetCpuUtilization(ProcessCpuInformation* previousCpuInfo) { - static long numProcessors = 0; - - if (numProcessors <= 0) - { - numProcessors = sysconf(SYSCONF_GET_NUMPROCS); - if (numProcessors <= 0) - { - return 0; - } - } - uint64_t kernelTime = 0; uint64_t userTime = 0; @@ -143,11 +126,7 @@ int32_t SystemNative_GetCpuUtilization(ProcessCpuInformation* previousCpuInfo) uint64_t cpuTotalTime = 0; if (currentTime > lastRecordedCurrentTime) { - // cpuTotalTime is based on clock time. Since multiple threads can run in parallel, - // we need to scale cpuTotalTime cover the same amount of total CPU time. - // rusage time is already scaled across multiple processors. cpuTotalTime = (currentTime - lastRecordedCurrentTime); - cpuTotalTime *= (uint64_t)numProcessors; } uint64_t cpuBusyTime = 0; @@ -162,8 +141,6 @@ int32_t SystemNative_GetCpuUtilization(ProcessCpuInformation* previousCpuInfo) cpuUtilization = (int32_t)(cpuBusyTime * 100 / cpuTotalTime); } - assert(cpuUtilization >= 0 && cpuUtilization <= 100); - previousCpuInfo->lastRecordedCurrentTime = currentTime; previousCpuInfo->lastRecordedUserTime = userTime; previousCpuInfo->lastRecordedKernelTime = kernelTime; diff --git a/src/libraries/Native/Unix/System.Native/pal_time.h b/src/libraries/Native/Unix/System.Native/pal_time.h index de4b749cec45a0..0a9c98058777fa 100644 --- a/src/libraries/Native/Unix/System.Native/pal_time.h +++ b/src/libraries/Native/Unix/System.Native/pal_time.h @@ -43,6 +43,8 @@ DLLEXPORT uint64_t SystemNative_GetTimestamp(void); * for the CLR thread pool to regulate the number of worker threads. * Since there is no consistent API on Unix to get the CPU utilization * from a user process, getrusage and gettimeofday are used to - * compute the current process's CPU utilization instead. + * compute the current process's CPU utilization instead. The CPU utilization + * returned is sum of utilization accross all processors, e.g. this function will + * return 200 when two cores are running at 100%. */ DLLEXPORT int32_t SystemNative_GetCpuUtilization(ProcessCpuInformation* previousCpuInfo); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Unix.cs index 582fc74a778c62..6ee21ce13a19c6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Unix.cs @@ -6,11 +6,12 @@ namespace System.Threading { internal partial class PortableThreadPool { - private class CpuUtilizationReader + private struct CpuUtilizationReader { private Interop.Sys.ProcessCpuInformation _cpuInfo; - public int CurrentUtilization => Interop.Sys.GetCpuUtilization(ref _cpuInfo); // Updates cpuInfo as a side effect for the next call + public int CurrentUtilization => + Interop.Sys.GetCpuUtilization(ref _cpuInfo) / Environment.ProcessorCount; } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Windows.cs index d04cd2ada5938d..ee789979552792 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Windows.cs @@ -9,16 +9,11 @@ namespace System.Threading { internal partial class PortableThreadPool { - private class CpuUtilizationReader + private struct CpuUtilizationReader { - private struct ProcessCpuInformation - { - public long idleTime; - public long kernelTime; - public long userTime; - } - - private ProcessCpuInformation _processCpuInfo = new ProcessCpuInformation(); + public long _idleTime; + public long _kernelTime; + public long _userTime; public int CurrentUtilization { @@ -26,18 +21,15 @@ public int CurrentUtilization { if (!Interop.Kernel32.GetSystemTimes(out var idleTime, out var kernelTime, out var userTime)) { - int error = Marshal.GetLastWin32Error(); - var exception = new OutOfMemoryException(); - exception.HResult = error; - throw exception; + return 0; } - long cpuTotalTime = ((long)userTime - _processCpuInfo.userTime) + ((long)kernelTime - _processCpuInfo.kernelTime); - long cpuBusyTime = cpuTotalTime - ((long)idleTime - _processCpuInfo.idleTime); + long cpuTotalTime = ((long)userTime - _userTime) + ((long)kernelTime - _kernelTime); + long cpuBusyTime = cpuTotalTime - ((long)idleTime - _idleTime); - _processCpuInfo.kernelTime = (long)kernelTime; - _processCpuInfo.userTime = (long)userTime; - _processCpuInfo.idleTime = (long)idleTime; + _kernelTime = (long)kernelTime; + _userTime = (long)userTime; + _idleTime = (long)idleTime; if (cpuTotalTime > 0 && cpuBusyTime > 0) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs index b5bbc8201cafc6..a6124b4e1a3b44 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs @@ -20,7 +20,7 @@ private static class GateThread private static LowLevelLock s_createdLock = new LowLevelLock(); - private static readonly CpuUtilizationReader s_cpu = new CpuUtilizationReader(); + private static CpuUtilizationReader s_cpu = new CpuUtilizationReader(); private const int MaxRuns = 2; // TODO: CoreCLR: Worker Tracking in CoreCLR? (Config name: ThreadPool_EnableWorkerTracking)