From 8019897e73f90ce09fe8c8927467e8a30392c30d Mon Sep 17 00:00:00 2001 From: Andrew Hsieh Date: Wed, 6 Mar 2013 18:37:02 +0800 Subject: GCC 4.6/4.4.3: OpenMP: Better CPU count detection for Linux See related b3cd55a4e23e443cc4561424e22c27fe7f05b5c1 Change-Id: Ie43b94f27841b1560f2ce4b2ba163b8584f39ebc --- gcc-4.6/libgomp/config/linux/proc.c | 189 +++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 3 deletions(-) (limited to 'gcc-4.6') diff --git a/gcc-4.6/libgomp/config/linux/proc.c b/gcc-4.6/libgomp/config/linux/proc.c index 01f51dfa1..82e58f4f4 100644 --- a/gcc-4.6/libgomp/config/linux/proc.c +++ b/gcc-4.6/libgomp/config/linux/proc.c @@ -31,8 +31,11 @@ #endif #include "libgomp.h" #include -#include #include +#include +#include +#include +#include #ifdef HAVE_GETLOADAVG # ifdef HAVE_SYS_LOADAVG_H # include @@ -64,6 +67,186 @@ cpuset_popcount (cpu_set_t *cpusetp) } #endif +/* Read the content of a file. + * Return the length of the data, or -1 on error. Does *not* + * zero-terminate the content. Will not read more + * than 'buffsize' bytes. + */ +static int +read_file(const char* pathname, char* buffer, size_t buffsize) +{ + int fd, len; + + fd = open(pathname, O_RDONLY); + if (fd < 0) + return -1; + + do { + len = read(fd, buffer, buffsize); + } while (len < 0 && errno == EINTR); + + close(fd); + + return len; +} + + +/* Parse a decimal integer starting from 'input', but not going further + * than 'limit'. Return the value into '*result'. + * + * NOTE: Does not skip over leading spaces, or deal with sign characters. + * NOTE: Ignores overflows. + * + * The function returns NULL in case of error (bad format), or the new + * position after the decimal number in case of success (which will always + * be <= 'limit'). + */ +static const char* +parse_decimal(const char* input, const char* limit, int* result) +{ + const char* p = input; + int val = 0; + while (p < limit) { + int d = (*p - '0'); + if ((unsigned)d >= 10U) + break; + val = val*10 + d; + p++; + } + if (p == input) + return NULL; + + *result = val; + return p; +} + + +/* This data type is used to represent a CPU list / mask, as read + * from sysfs on Linux. See http://www.kernel.org/doc/Documentation/cputopology.txt + */ +typedef struct { + int mask; +} cpuList; + +/* Returns Actual CPUs (total installed CPUs) */ +static int +cpuList_count (cpuList* list) { + return list->mask ; +} + +/* Parse a textual list of cpus and store the result inside a cpuList object. + * Input format is the following: + * - comma-separated list of items (no spaces) + * - each item is either a single decimal number (cpu index), or a range made + * of two numbers separated by a single dash (-). Ranges are inclusive. + * Examples: + * 0 + * 2,4-127,128-143 + * 0-1 + */ +static void +cpuList_parse (cpuList* list, const char* line, int line_len) +{ + const char* p = line; + const char* end = p + line_len; + const char* q; + + /* NOTE: the input line coming from sysfs typically contains a + * trailing newline, so take care of it in the code below + */ + while (p < end && *p != '\n') + { + int val, start_value, end_value; + + /* Find the end of current item, and put it into 'q' */ + q = memchr(p, ',', end-p); + if (q == NULL) { + q = end; + } + + /* Get first value */ + p = parse_decimal(p, q, &start_value); + if (p == NULL) + goto BAD_FORMAT; + + end_value = start_value; + + /* If we're not at the end of the item, expect a dash and + * and integer; extract end value. + */ + if (p < q && *p == '-') { + p = parse_decimal(p+1, q, &end_value); + if (p == NULL) + goto BAD_FORMAT; + } + + /* Set CPU list */ + for (val = start_value; val <= end_value; val++) { + list->mask++; + } + + /* Jump to next item */ + p = q; + if (p < end) + p++; + } + +BAD_FORMAT: + ; +} + + +/* Read a CPU list from one sysfs file + * The below is CPU related sys interface by CPU topologoy. + */ +static void +cpuList_read_from (cpuList* list, const char* filename) +{ + char file[64]; + int filelen; + + list->mask = 0; + + filelen = read_file(filename, file, sizeof file); + if (filelen < 0) { + fprintf(stderr,"Could not read %s: %s\n", filename, strerror(errno)); + return; + } + + cpuList_parse(list, file, filelen); +} + + +/* Probe the number of actual CPUs on devices (e.g. Android devices) using + * CPU-Hotplug and CPU-DVFS to optimize battery life. + * Return the number of CPUs present on a given device. + * See http://www.kernel.org/doc/Documentation/cputopology.txt + */ +static int +sc_nprocessors_actu () +{ + char buffer[256]; + int buffer_len; + int cpuCount = 1; + cpuList cpus_present[1]; + char file_name[64] = "/sys/devices/system/cpu/present"; + + buffer_len = read_file(file_name, buffer, sizeof buffer); + + if (buffer_len < 0) /* should not happen */ { + fprintf(stderr,"Could not find %s: %s\n", file_name, strerror(errno)); + return; + } + + /* Count the CPU cores, the value may be 0 for single-core CPUs */ + cpuList_read_from(cpus_present, file_name); + cpuCount = cpuList_count(cpus_present); + if (cpuCount == 0) { + cpuCount = 1; + } + return cpuCount; +} + /* At startup, determine the default number of threads. It would seem this should be related to the number of cpus online. */ @@ -83,7 +266,7 @@ gomp_init_num_threads (void) } #endif #ifdef _SC_NPROCESSORS_ONLN - gomp_global_icv.nthreads_var = sysconf (_SC_NPROCESSORS_ONLN); + gomp_global_icv.nthreads_var = sc_nprocessors_actu (); #endif } @@ -115,7 +298,7 @@ get_num_procs (void) } #endif #ifdef _SC_NPROCESSORS_ONLN - return sysconf (_SC_NPROCESSORS_ONLN); + return sc_nprocessors_actu (); #else return gomp_icv (false)->nthreads_var; #endif -- cgit v1.2.3