Skip to content
Open
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
12 changes: 11 additions & 1 deletion src/coreclr/gc/unix/gcenv.unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,18 @@ bool GCToOSInterface::Initialize()

g_pageSizeUnixInl = uint32_t((pageSize > 0) ? pageSize : 0x1000);

// Calculate and cache the number of processors on this machine
#ifdef TARGET_ANDROID
// Android tries really hard to save power by powering off CPUs on SMP phones which
// means the normal way to query cpu count can underestimate the number of available CPUs.
int cpuCount = minipal_get_cpu_present_count();
if (cpuCount == -1)
{
cpuCount = sysconf(SYSCONF_GET_NUMPROCS);
}
#else
int cpuCount = sysconf(SYSCONF_GET_NUMPROCS);
#endif

if (cpuCount == -1)
{
return false;
Expand Down
15 changes: 14 additions & 1 deletion src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -447,16 +447,29 @@ void InitializeCurrentProcessCpuCount()
uint32_t count = 0;

// If the configuration value has been set, it takes precedence. Otherwise, take into account
// process affinity and CPU quota limit.
// process affinity and CPU quota limit, except for Android (explained below).

const unsigned int MAX_PROCESSOR_COUNT = 0xffff;
uint64_t configValue;
int cpuPresentCount;

if (g_pRhConfig->ReadConfigValue("PROCESSOR_COUNT", &configValue, true /* decimal */) &&
0 < configValue && configValue <= MAX_PROCESSOR_COUNT)
{
count = configValue;
}
#ifdef HOST_ANDROID
// Android tries really hard to save power by powering off CPUs on SMP phones which
// means the normal way to query cpu count can underestimate the number of available CPUs.
else if ((cpuPresentCount = minipal_get_cpu_present_count()) > 0)
{
count = cpuPresentCount;

uint32_t cpuLimit;
if (GetCpuLimit(&cpuLimit) && cpuLimit < count)
count = cpuLimit;
}
Comment thread
eduardo-vp marked this conversation as resolved.
#endif
else
{
#if HAVE_SCHED_GETAFFINITY
Expand Down
11 changes: 10 additions & 1 deletion src/coreclr/pal/src/misc/sysinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,16 @@ PAL_GetLogicalCpuCountFromOS()
{
static int nrcpus = -1;

if (nrcpus == -1)
// Android tries really hard to save power by powering off CPUs on SMP phones which
// means the normal way to query cpu count can underestimate the number of available CPUs.
#if defined(HOST_ANDROID)
if (nrcpus <= 0)
{
nrcpus = minipal_get_cpu_present_count();
}
#endif

if (nrcpus <= 0)
{
#if HAVE_SCHED_GETAFFINITY

Expand Down
22 changes: 7 additions & 15 deletions src/mono/mono/utils/mono-proclib.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "config.h"
#include <mono/utils/mono-proclib.h>
#include <minipal/cpucount.h>

#include <stdlib.h>
#include <stdio.h>
Expand Down Expand Up @@ -78,23 +79,14 @@ mono_cpu_count (void)
#else
#ifdef HOST_ANDROID
/* Android tries really hard to save power by powering off CPUs on SMP phones which
* means the normal way to query cpu count returns a wrong value with userspace API.
* means the normal way to query cpu count can underestimate the number of available CPUs.
* Instead we use /sys entries to query the actual hardware CPU count.
*/
int count = 0;
char buffer[8] = {'\0'};
int present = open ("/sys/devices/system/cpu/present", O_RDONLY);
/* Format of the /sys entry is a cpulist of indexes which in the case
* of present is always of the form "0-(n-1)" when there is more than
* 1 core, n being the number of CPU cores in the system. Otherwise
* the value is simply 0
*/
if (present != -1 && read (present, (char*)buffer, sizeof (buffer)) > 3)
count = strtol (((char*)buffer) + 2, NULL, 10);
if (present != -1)
close (present);
if (count > 0)
return count + 1;
{
int count = minipal_get_cpu_present_count ();
if (count > 0)
return count;
}
#endif

#if defined(HOST_ARM) || defined (HOST_ARM64)
Expand Down
113 changes: 83 additions & 30 deletions src/native/minipal/cpucount.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,97 @@
#include <stdio.h>
#include <unistd.h>

int minipal_get_cpu_max_possible_count(void)
{
#if defined(__linux__)
FILE* f = fopen("/sys/devices/system/cpu/possible", "r");
if (f != NULL)
// Parses a kernel cpulist file (comma-separated ranges like "0-3,5-7").
// Sets *outCount to total CPU count and *outMaxIndex to highest index on success.
static int parse_cpulist_file(const char* path, int* outCount, int* outMaxIndex)
{
FILE* f = fopen(path, "r");
if (f == NULL)
{
int maxCpu = -1;
for (;;)
return 0;
}

int count = 0;
int maxIndex = -1;
int parseSuccess = 0;
for (;;)
{
int lo, hi;
int matched = fscanf(f, "%d-%d", &lo, &hi);
if (matched == 1)
{
hi = lo;
}
else if (matched != 2)
{
// Unexpected format, discard
count = 0;
break;
}

if (hi < lo)
{
// Invalid range, discard
count = 0;
break;
}

count += hi - lo + 1;
if (hi > maxIndex)
{
maxIndex = hi;
}

int ch = fgetc(f);
if (ch == ',')
{
continue;
}
else if (ch == '\n' || ch == EOF)
{
int lo, hi;
int matched = fscanf(f, "%d-%d", &lo, &hi);
if (matched == 1)
{
hi = lo;
}
else if (matched != 2)
{
break;
}

if (maxCpu < hi)
{
maxCpu = hi;
}

int ch = fgetc(f);
if (ch == EOF || ch != ',')
{
break;
}
parseSuccess = 1;
break;
}
fclose(f);
if (maxCpu != -1)
else
{
return maxCpu + 1;
// Unexpected character, discard
count = 0;
break;
}
}

fclose(f);

if (parseSuccess && count > 0)
{
if (outCount) *outCount = count;
if (outMaxIndex) *outMaxIndex = maxIndex;
}
return parseSuccess && count > 0;
}
#endif

int minipal_get_cpu_max_possible_count(void)
{
#if defined(__linux__)
int maxIndex;
if (parse_cpulist_file("/sys/devices/system/cpu/possible", NULL, &maxIndex))
{
return maxIndex + 1;
}
#endif
return (int)sysconf(_SC_NPROCESSORS_CONF);
}

int minipal_get_cpu_present_count(void)
{
#if defined(__linux__)
int count;
if (parse_cpulist_file("/sys/devices/system/cpu/present", &count, NULL))
{
return count;
}
#endif
return -1;
}
7 changes: 7 additions & 0 deletions src/native/minipal/cpucount.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ extern "C"
// Falls back to sysconf(_SC_NPROCESSORS_CONF) if the sysfs file is unavailable.
int minipal_get_cpu_max_possible_count(void);

// Returns the number of CPUs physically present in the system, regardless of
// whether they are currently online or offline or -1 if unavailable. This is the
// system core count and does not account for per-process affinity / cgroup limits.
//
// On Linux, this reads /sys/devices/system/cpu/present.
int minipal_get_cpu_present_count(void);

#ifdef __cplusplus
}
#endif // __cplusplus
Expand Down
Loading