root/lib/nproc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. num_processors_via_affinity_mask
  2. num_processors_ignoring_omp
  3. parse_omp_threads
  4. num_processors

     1 /* Detect the number of processors.
     2 
     3    Copyright (C) 2009-2023 Free Software Foundation, Inc.
     4 
     5    This file is free software: you can redistribute it and/or modify
     6    it under the terms of the GNU Lesser General Public License as
     7    published by the Free Software Foundation; either version 2.1 of the
     8    License, or (at your option) any later version.
     9 
    10    This file is distributed in the hope that it will be useful,
    11    but WITHOUT ANY WARRANTY; without even the implied warranty of
    12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13    GNU Lesser General Public License for more details.
    14 
    15    You should have received a copy of the GNU Lesser General Public License
    16    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
    17 
    18 /* Written by Glen Lenker and Bruno Haible.  */
    19 
    20 #include <config.h>
    21 #include "nproc.h"
    22 
    23 #include <limits.h>
    24 #include <stdlib.h>
    25 #include <unistd.h>
    26 
    27 #if HAVE_PTHREAD_GETAFFINITY_NP && 0
    28 # include <pthread.h>
    29 # include <sched.h>
    30 #endif
    31 #if HAVE_SCHED_GETAFFINITY_LIKE_GLIBC || HAVE_SCHED_GETAFFINITY_NP
    32 # include <sched.h>
    33 #endif
    34 
    35 #include <sys/types.h>
    36 
    37 #if HAVE_SYS_PSTAT_H
    38 # include <sys/pstat.h>
    39 #endif
    40 
    41 #if HAVE_SYS_SYSMP_H
    42 # include <sys/sysmp.h>
    43 #endif
    44 
    45 #if HAVE_SYS_PARAM_H
    46 # include <sys/param.h>
    47 #endif
    48 
    49 #if HAVE_SYS_SYSCTL_H && !(defined __GLIBC__ && defined __linux__)
    50 # include <sys/sysctl.h>
    51 #endif
    52 
    53 #if defined _WIN32 && ! defined __CYGWIN__
    54 # define WIN32_LEAN_AND_MEAN
    55 # include <windows.h>
    56 #endif
    57 
    58 #include "c-ctype.h"
    59 
    60 #include "minmax.h"
    61 
    62 #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
    63 
    64 /* Return the number of processors available to the current process, based
    65    on a modern system call that returns the "affinity" between the current
    66    process and each CPU.  Return 0 if unknown or if such a system call does
    67    not exist.  */
    68 static unsigned long
    69 num_processors_via_affinity_mask (void)
    70 {
    71   /* glibc >= 2.3.3 with NPTL and NetBSD 5 have pthread_getaffinity_np,
    72      but with different APIs.  Also it requires linking with -lpthread.
    73      Therefore this code is not enabled.
    74      glibc >= 2.3.4 has sched_getaffinity whereas NetBSD 5 has
    75      sched_getaffinity_np.  */
    76 #if HAVE_PTHREAD_GETAFFINITY_NP && defined __GLIBC__ && 0
    77   {
    78     cpu_set_t set;
    79 
    80     if (pthread_getaffinity_np (pthread_self (), sizeof (set), &set) == 0)
    81       {
    82         unsigned long count;
    83 
    84 # ifdef CPU_COUNT
    85         /* glibc >= 2.6 has the CPU_COUNT macro.  */
    86         count = CPU_COUNT (&set);
    87 # else
    88         size_t i;
    89 
    90         count = 0;
    91         for (i = 0; i < CPU_SETSIZE; i++)
    92           if (CPU_ISSET (i, &set))
    93             count++;
    94 # endif
    95         if (count > 0)
    96           return count;
    97       }
    98   }
    99 #elif HAVE_PTHREAD_GETAFFINITY_NP && defined __NetBSD__ && 0
   100   {
   101     cpuset_t *set;
   102 
   103     set = cpuset_create ();
   104     if (set != NULL)
   105       {
   106         unsigned long count = 0;
   107 
   108         if (pthread_getaffinity_np (pthread_self (), cpuset_size (set), set)
   109             == 0)
   110           {
   111             cpuid_t i;
   112 
   113             for (i = 0;; i++)
   114               {
   115                 int ret = cpuset_isset (i, set);
   116                 if (ret < 0)
   117                   break;
   118                 if (ret > 0)
   119                   count++;
   120               }
   121           }
   122         cpuset_destroy (set);
   123         if (count > 0)
   124           return count;
   125       }
   126   }
   127 #elif HAVE_SCHED_GETAFFINITY_LIKE_GLIBC /* glibc >= 2.3.4 */
   128   {
   129     cpu_set_t set;
   130 
   131     if (sched_getaffinity (0, sizeof (set), &set) == 0)
   132       {
   133         unsigned long count;
   134 
   135 # ifdef CPU_COUNT
   136         /* glibc >= 2.6 has the CPU_COUNT macro.  */
   137         count = CPU_COUNT (&set);
   138 # else
   139         size_t i;
   140 
   141         count = 0;
   142         for (i = 0; i < CPU_SETSIZE; i++)
   143           if (CPU_ISSET (i, &set))
   144             count++;
   145 # endif
   146         if (count > 0)
   147           return count;
   148       }
   149   }
   150 #elif HAVE_SCHED_GETAFFINITY_NP /* NetBSD >= 5 */
   151   {
   152     cpuset_t *set;
   153 
   154     set = cpuset_create ();
   155     if (set != NULL)
   156       {
   157         unsigned long count = 0;
   158 
   159         if (sched_getaffinity_np (getpid (), cpuset_size (set), set) == 0)
   160           {
   161             cpuid_t i;
   162 
   163             for (i = 0;; i++)
   164               {
   165                 int ret = cpuset_isset (i, set);
   166                 if (ret < 0)
   167                   break;
   168                 if (ret > 0)
   169                   count++;
   170               }
   171           }
   172         cpuset_destroy (set);
   173         if (count > 0)
   174           return count;
   175       }
   176   }
   177 #endif
   178 
   179 #if defined _WIN32 && ! defined __CYGWIN__
   180   { /* This works on native Windows platforms.  */
   181     DWORD_PTR process_mask;
   182     DWORD_PTR system_mask;
   183 
   184     if (GetProcessAffinityMask (GetCurrentProcess (),
   185                                 &process_mask, &system_mask))
   186       {
   187         DWORD_PTR mask = process_mask;
   188         unsigned long count = 0;
   189 
   190         for (; mask != 0; mask = mask >> 1)
   191           if (mask & 1)
   192             count++;
   193         if (count > 0)
   194           return count;
   195       }
   196   }
   197 #endif
   198 
   199   return 0;
   200 }
   201 
   202 
   203 /* Return the total number of processors.  Here QUERY must be one of
   204    NPROC_ALL, NPROC_CURRENT.  The result is guaranteed to be at least 1.  */
   205 static unsigned long int
   206 num_processors_ignoring_omp (enum nproc_query query)
   207 {
   208   /* On systems with a modern affinity mask system call, we have
   209          sysconf (_SC_NPROCESSORS_CONF)
   210             >= sysconf (_SC_NPROCESSORS_ONLN)
   211                >= num_processors_via_affinity_mask ()
   212      The first number is the number of CPUs configured in the system.
   213      The second number is the number of CPUs available to the scheduler.
   214      The third number is the number of CPUs available to the current process.
   215 
   216      Note! On Linux systems with glibc, the first and second number come from
   217      the /sys and /proc file systems (see
   218      glibc/sysdeps/unix/sysv/linux/getsysstats.c).
   219      In some situations these file systems are not mounted, and the sysconf call
   220      returns 1 or 2 (<https://sourceware.org/bugzilla/show_bug.cgi?id=21542>),
   221      which does not reflect the reality.  */
   222 
   223   if (query == NPROC_CURRENT)
   224     {
   225       /* Try the modern affinity mask system call.  */
   226       {
   227         unsigned long nprocs = num_processors_via_affinity_mask ();
   228 
   229         if (nprocs > 0)
   230           return nprocs;
   231       }
   232 
   233 #if defined _SC_NPROCESSORS_ONLN
   234       { /* This works on glibc, Mac OS X 10.5, FreeBSD, AIX, OSF/1, Solaris,
   235            Cygwin, Haiku.  */
   236         long int nprocs = sysconf (_SC_NPROCESSORS_ONLN);
   237         if (nprocs > 0)
   238           return nprocs;
   239       }
   240 #endif
   241     }
   242   else /* query == NPROC_ALL */
   243     {
   244 #if defined _SC_NPROCESSORS_CONF
   245       { /* This works on glibc, Mac OS X 10.5, FreeBSD, AIX, OSF/1, Solaris,
   246            Cygwin, Haiku.  */
   247         long int nprocs = sysconf (_SC_NPROCESSORS_CONF);
   248 
   249 # if __GLIBC__ >= 2 && defined __linux__
   250         /* On Linux systems with glibc, this information comes from the /sys and
   251            /proc file systems (see glibc/sysdeps/unix/sysv/linux/getsysstats.c).
   252            In some situations these file systems are not mounted, and the
   253            sysconf call returns 1 or 2.  But we wish to guarantee that
   254            num_processors (NPROC_ALL) >= num_processors (NPROC_CURRENT).  */
   255         if (nprocs == 1 || nprocs == 2)
   256           {
   257             unsigned long nprocs_current = num_processors_via_affinity_mask ();
   258 
   259             if (/* nprocs_current > 0 && */ nprocs_current > nprocs)
   260               nprocs = nprocs_current;
   261           }
   262 # endif
   263 
   264         if (nprocs > 0)
   265           return nprocs;
   266       }
   267 #endif
   268     }
   269 
   270 #if HAVE_PSTAT_GETDYNAMIC
   271   { /* This works on HP-UX.  */
   272     struct pst_dynamic psd;
   273     if (pstat_getdynamic (&psd, sizeof psd, 1, 0) >= 0)
   274       {
   275         /* The field psd_proc_cnt contains the number of active processors.
   276            In newer releases of HP-UX 11, the field psd_max_proc_cnt includes
   277            deactivated processors.  */
   278         if (query == NPROC_CURRENT)
   279           {
   280             if (psd.psd_proc_cnt > 0)
   281               return psd.psd_proc_cnt;
   282           }
   283         else
   284           {
   285             if (psd.psd_max_proc_cnt > 0)
   286               return psd.psd_max_proc_cnt;
   287           }
   288       }
   289   }
   290 #endif
   291 
   292 #if HAVE_SYSMP && defined MP_NAPROCS && defined MP_NPROCS
   293   { /* This works on IRIX.  */
   294     /* MP_NPROCS yields the number of installed processors.
   295        MP_NAPROCS yields the number of processors available to unprivileged
   296        processes.  */
   297     int nprocs =
   298       sysmp (query == NPROC_CURRENT && getuid () != 0
   299              ? MP_NAPROCS
   300              : MP_NPROCS);
   301     if (nprocs > 0)
   302       return nprocs;
   303   }
   304 #endif
   305 
   306   /* Finally, as fallback, use the APIs that don't distinguish between
   307      NPROC_CURRENT and NPROC_ALL.  */
   308 
   309 #if HAVE_SYSCTL && !(defined __GLIBC__ && defined __linux__) && defined HW_NCPU
   310   { /* This works on macOS, FreeBSD, NetBSD, OpenBSD.
   311        macOS 10.14 does not allow mib to be const.  */
   312     int nprocs;
   313     size_t len = sizeof (nprocs);
   314     static int mib[][2] = {
   315 # ifdef HW_NCPUONLINE
   316       { CTL_HW, HW_NCPUONLINE },
   317 # endif
   318       { CTL_HW, HW_NCPU }
   319     };
   320     for (int i = 0; i < ARRAY_SIZE (mib); i++)
   321       {
   322         if (sysctl (mib[i], ARRAY_SIZE (mib[i]), &nprocs, &len, NULL, 0) == 0
   323             && len == sizeof (nprocs)
   324             && 0 < nprocs)
   325           return nprocs;
   326       }
   327   }
   328 #endif
   329 
   330 #if defined _WIN32 && ! defined __CYGWIN__
   331   { /* This works on native Windows platforms.  */
   332     SYSTEM_INFO system_info;
   333     GetSystemInfo (&system_info);
   334     if (0 < system_info.dwNumberOfProcessors)
   335       return system_info.dwNumberOfProcessors;
   336   }
   337 #endif
   338 
   339   return 1;
   340 }
   341 
   342 /* Parse OMP environment variables without dependence on OMP.
   343    Return 0 for invalid values.  */
   344 static unsigned long int
   345 parse_omp_threads (char const* threads)
   346 {
   347   unsigned long int ret = 0;
   348 
   349   if (threads == NULL)
   350     return ret;
   351 
   352   /* The OpenMP spec says that the value assigned to the environment variables
   353      "may have leading and trailing white space".  */
   354   while (*threads != '\0' && c_isspace (*threads))
   355     threads++;
   356 
   357   /* Convert it from positive decimal to 'unsigned long'.  */
   358   if (c_isdigit (*threads))
   359     {
   360       char *endptr = NULL;
   361       unsigned long int value = strtoul (threads, &endptr, 10);
   362 
   363       if (endptr != NULL)
   364         {
   365           while (*endptr != '\0' && c_isspace (*endptr))
   366             endptr++;
   367           if (*endptr == '\0')
   368             return value;
   369           /* Also accept the first value in a nesting level,
   370              since we can't determine the nesting level from env vars.  */
   371           else if (*endptr == ',')
   372             return value;
   373         }
   374     }
   375 
   376   return ret;
   377 }
   378 
   379 unsigned long int
   380 num_processors (enum nproc_query query)
   381 {
   382   unsigned long int omp_env_limit = ULONG_MAX;
   383 
   384   if (query == NPROC_CURRENT_OVERRIDABLE)
   385     {
   386       unsigned long int omp_env_threads;
   387       /* Honor the OpenMP environment variables, recognized also by all
   388          programs that are based on OpenMP.  */
   389       omp_env_threads = parse_omp_threads (getenv ("OMP_NUM_THREADS"));
   390       omp_env_limit = parse_omp_threads (getenv ("OMP_THREAD_LIMIT"));
   391       if (! omp_env_limit)
   392         omp_env_limit = ULONG_MAX;
   393 
   394       if (omp_env_threads)
   395         return MIN (omp_env_threads, omp_env_limit);
   396 
   397       query = NPROC_CURRENT;
   398     }
   399   /* Here query is one of NPROC_ALL, NPROC_CURRENT.  */
   400   {
   401     unsigned long nprocs = num_processors_ignoring_omp (query);
   402     return MIN (nprocs, omp_env_limit);
   403   }
   404 }

/* [<][>][^][v][top][bottom][index][help] */