From 134bba5b17d6cea5797bfd4ca209b6e3a39e7cdf Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Thu, 24 Jan 2019 16:22:18 +0100 Subject: [PATCH 1/2] Add SystemNative_GetCpuUtilization. --- src/Native/Unix/System.Native/pal_time.c | 73 ++++++++++++++++++++++++ src/Native/Unix/System.Native/pal_time.h | 17 ++++++ 2 files changed, 90 insertions(+) diff --git a/src/Native/Unix/System.Native/pal_time.c b/src/Native/Unix/System.Native/pal_time.c index 94cc61a3f2d8..3f516e6532a4 100644 --- a/src/Native/Unix/System.Native/pal_time.c +++ b/src/Native/Unix/System.Native/pal_time.c @@ -12,6 +12,7 @@ #include #include #include +#include #if HAVE_MACH_ABSOLUTE_TIME #include #endif @@ -152,3 +153,75 @@ int32_t SystemNative_GetTimebaseInfo(uint32_t* numer, uint32_t* denom) } return 1; } + +static long numProcessors = 0; +int32_t SystemNative_GetCpuUtilization(ProcessCpuInformation* previousCpuInfo) +{ + if (numProcessors <= 0) + { + numProcessors = sysconf(_SC_NPROCESSORS_CONF); + if (numProcessors <= 0) + { + return 0; + } + } + + uint64_t kernelTime = 0; + uint64_t userTime = 0; + + struct rusage resUsage; + if (getrusage(RUSAGE_SELF, &resUsage) == -1) + { + assert(false); + return 0; + } + else + { + kernelTime = ((uint64_t)(resUsage.ru_stime.tv_sec) * SecondsToNanoSeconds) + (uint64_t)(resUsage.ru_stime.tv_usec); + userTime = ((uint64_t)(resUsage.ru_utime.tv_sec) * SecondsToNanoSeconds) + (uint64_t)(resUsage.ru_utime.tv_usec); + } + + uint64_t timestamp; + uint64_t resolution; + + if (!SystemNative_GetTimestamp(×tamp) || !SystemNative_GetTimestampResolution(&resolution)) + { + return 0; + } + + uint64_t currentTime = timestamp * SecondsToNanoSeconds / resolution; + + uint64_t lastRecordedCurrentTime = previousCpuInfo->lastRecordedCurrentTime; + uint64_t lastRecordedKernelTime = previousCpuInfo->lastRecordedKernelTime; + uint64_t lastRecordedUserTime = previousCpuInfo->lastRecordedUserTime; + + 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; + if (userTime >= lastRecordedUserTime && kernelTime >= lastRecordedKernelTime) + { + cpuBusyTime = (userTime - lastRecordedUserTime) + (kernelTime - lastRecordedKernelTime); + } + + int32_t cpuUtilization = 0; + if (cpuTotalTime > 0 && cpuBusyTime > 0) + { + cpuUtilization = (int32_t)(cpuBusyTime / cpuTotalTime); + } + + assert(cpuUtilization >= 0 && cpuUtilization <= 100); + + previousCpuInfo->lastRecordedCurrentTime = currentTime; + previousCpuInfo->lastRecordedUserTime = userTime; + previousCpuInfo->lastRecordedKernelTime = kernelTime; + + return cpuUtilization; +} diff --git a/src/Native/Unix/System.Native/pal_time.h b/src/Native/Unix/System.Native/pal_time.h index a0deba8ff266..ebeee545cdf1 100644 --- a/src/Native/Unix/System.Native/pal_time.h +++ b/src/Native/Unix/System.Native/pal_time.h @@ -13,6 +13,14 @@ typedef struct TimeSpec int64_t tv_nsec; // nanoseconds } TimeSpec; +typedef struct ProcessCpuInformation +{ + uint64_t lastRecordedCurrentTime; + uint64_t lastRecordedKernelTime; + uint64_t lastRecordedUserTime; +} ProcessCpuInformation; + + /** * Sets the last access and last modified time of a file * @@ -37,3 +45,12 @@ DLLEXPORT int32_t SystemNative_GetTimestamp(uint64_t* timestamp); DLLEXPORT int32_t SystemNative_GetAbsoluteTime(uint64_t* timestamp); DLLEXPORT int32_t SystemNative_GetTimebaseInfo(uint32_t* numer, uint32_t* denom); + +/** + * The main purpose of this function is to compute the overall CPU utilization + * 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. + */ +DLLEXPORT int32_t SystemNative_GetCpuUtilization(ProcessCpuInformation* previousCpuInfo); From 649b56b2006c3e6d1309f1a166ad95886536264b Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Thu, 24 Jan 2019 22:09:51 +0100 Subject: [PATCH 2/2] Update sysconf call to match CoreCLR. --- src/Native/Unix/System.Native/pal_time.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Native/Unix/System.Native/pal_time.c b/src/Native/Unix/System.Native/pal_time.c index 3f516e6532a4..33f168f3de26 100644 --- a/src/Native/Unix/System.Native/pal_time.c +++ b/src/Native/Unix/System.Native/pal_time.c @@ -154,12 +154,19 @@ int32_t SystemNative_GetTimebaseInfo(uint32_t* numer, uint32_t* denom) return 1; } -static long numProcessors = 0; +#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(_SC_NPROCESSORS_CONF); + numProcessors = sysconf(SYSCONF_GET_NUMPROCS); if (numProcessors <= 0) { return 0;