Commit | Line | Data |
---|---|---|
0f00f2c3 LC |
1 | /* Detect the number of processors. |
2 | ||
f0007cad | 3 | Copyright (C) 2009-2012 Free Software Foundation, Inc. |
0f00f2c3 LC |
4 | |
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU Lesser General Public License as published by | |
7 | the Free Software Foundation; either version 2, or (at your option) | |
8 | any later version. | |
9 | ||
10 | This program 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, write to the Free Software Foundation, | |
17 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ | |
18 | ||
19 | /* Written by Glen Lenker and Bruno Haible. */ | |
20 | ||
21 | #include <config.h> | |
22 | #include "nproc.h" | |
23 | ||
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 | |
50 | # include <sys/sysctl.h> | |
51 | #endif | |
52 | ||
53 | #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ | |
54 | # define WIN32_LEAN_AND_MEAN | |
55 | # include <windows.h> | |
56 | #endif | |
57 | ||
58 | #include "c-ctype.h" | |
59 | ||
60 | #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) | |
61 | ||
62 | /* Return the number of processors available to the current process, based | |
63 | on a modern system call that returns the "affinity" between the current | |
64 | process and each CPU. Return 0 if unknown or if such a system call does | |
65 | not exist. */ | |
66 | static unsigned long | |
67 | num_processors_via_affinity_mask (void) | |
68 | { | |
69 | /* glibc >= 2.3.3 with NPTL and NetBSD 5 have pthread_getaffinity_np, | |
70 | but with different APIs. Also it requires linking with -lpthread. | |
71 | Therefore this code is not enabled. | |
72 | glibc >= 2.3.4 has sched_getaffinity whereas NetBSD 5 has | |
73 | sched_getaffinity_np. */ | |
74 | #if HAVE_PTHREAD_GETAFFINITY_NP && defined __GLIBC__ && 0 | |
75 | { | |
76 | cpu_set_t set; | |
77 | ||
78 | if (pthread_getaffinity_np (pthread_self (), sizeof (set), &set) == 0) | |
79 | { | |
80 | unsigned long count; | |
81 | ||
82 | # ifdef CPU_COUNT | |
83 | /* glibc >= 2.6 has the CPU_COUNT macro. */ | |
84 | count = CPU_COUNT (&set); | |
85 | # else | |
86 | size_t i; | |
87 | ||
88 | count = 0; | |
89 | for (i = 0; i < CPU_SETSIZE; i++) | |
90 | if (CPU_ISSET (i, &set)) | |
91 | count++; | |
92 | # endif | |
93 | if (count > 0) | |
94 | return count; | |
95 | } | |
96 | } | |
97 | #elif HAVE_PTHREAD_GETAFFINITY_NP && defined __NetBSD__ && 0 | |
98 | { | |
99 | cpuset_t *set; | |
100 | ||
101 | set = cpuset_create (); | |
102 | if (set != NULL) | |
103 | { | |
104 | unsigned long count = 0; | |
105 | ||
106 | if (pthread_getaffinity_np (pthread_self (), cpuset_size (set), set) | |
107 | == 0) | |
108 | { | |
109 | cpuid_t i; | |
110 | ||
111 | for (i = 0;; i++) | |
112 | { | |
113 | int ret = cpuset_isset (i, set); | |
114 | if (ret < 0) | |
115 | break; | |
116 | if (ret > 0) | |
117 | count++; | |
118 | } | |
119 | } | |
120 | cpuset_destroy (set); | |
121 | if (count > 0) | |
122 | return count; | |
123 | } | |
124 | } | |
125 | #elif HAVE_SCHED_GETAFFINITY_LIKE_GLIBC /* glibc >= 2.3.4 */ | |
126 | { | |
127 | cpu_set_t set; | |
128 | ||
129 | if (sched_getaffinity (0, sizeof (set), &set) == 0) | |
130 | { | |
131 | unsigned long count; | |
132 | ||
133 | # ifdef CPU_COUNT | |
134 | /* glibc >= 2.6 has the CPU_COUNT macro. */ | |
135 | count = CPU_COUNT (&set); | |
136 | # else | |
137 | size_t i; | |
138 | ||
139 | count = 0; | |
140 | for (i = 0; i < CPU_SETSIZE; i++) | |
141 | if (CPU_ISSET (i, &set)) | |
142 | count++; | |
143 | # endif | |
144 | if (count > 0) | |
145 | return count; | |
146 | } | |
147 | } | |
148 | #elif HAVE_SCHED_GETAFFINITY_NP /* NetBSD >= 5 */ | |
149 | { | |
150 | cpuset_t *set; | |
151 | ||
152 | set = cpuset_create (); | |
153 | if (set != NULL) | |
154 | { | |
155 | unsigned long count = 0; | |
156 | ||
157 | if (sched_getaffinity_np (getpid (), cpuset_size (set), set) == 0) | |
158 | { | |
159 | cpuid_t i; | |
160 | ||
161 | for (i = 0;; i++) | |
162 | { | |
163 | int ret = cpuset_isset (i, set); | |
164 | if (ret < 0) | |
165 | break; | |
166 | if (ret > 0) | |
167 | count++; | |
168 | } | |
169 | } | |
170 | cpuset_destroy (set); | |
171 | if (count > 0) | |
172 | return count; | |
173 | } | |
174 | } | |
175 | #endif | |
176 | ||
177 | #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ | |
178 | { /* This works on native Windows platforms. */ | |
179 | DWORD_PTR process_mask; | |
180 | DWORD_PTR system_mask; | |
181 | ||
182 | if (GetProcessAffinityMask (GetCurrentProcess (), | |
183 | &process_mask, &system_mask)) | |
184 | { | |
185 | DWORD_PTR mask = process_mask; | |
186 | unsigned long count = 0; | |
187 | ||
188 | for (; mask != 0; mask = mask >> 1) | |
189 | if (mask & 1) | |
190 | count++; | |
191 | if (count > 0) | |
192 | return count; | |
193 | } | |
194 | } | |
195 | #endif | |
196 | ||
197 | return 0; | |
198 | } | |
199 | ||
200 | unsigned long int | |
201 | num_processors (enum nproc_query query) | |
202 | { | |
203 | if (query == NPROC_CURRENT_OVERRIDABLE) | |
204 | { | |
205 | /* Test the environment variable OMP_NUM_THREADS, recognized also by all | |
206 | programs that are based on OpenMP. The OpenMP spec says that the | |
207 | value assigned to the environment variable "may have leading and | |
208 | trailing white space". */ | |
209 | const char *envvalue = getenv ("OMP_NUM_THREADS"); | |
210 | ||
211 | if (envvalue != NULL) | |
212 | { | |
213 | while (*envvalue != '\0' && c_isspace (*envvalue)) | |
214 | envvalue++; | |
215 | /* Convert it from decimal to 'unsigned long'. */ | |
216 | if (c_isdigit (*envvalue)) | |
217 | { | |
218 | char *endptr = NULL; | |
219 | unsigned long int value = strtoul (envvalue, &endptr, 10); | |
220 | ||
221 | if (endptr != NULL) | |
222 | { | |
223 | while (*endptr != '\0' && c_isspace (*endptr)) | |
224 | endptr++; | |
225 | if (*endptr == '\0') | |
226 | return (value > 0 ? value : 1); | |
227 | } | |
228 | } | |
229 | } | |
230 | ||
231 | query = NPROC_CURRENT; | |
232 | } | |
233 | /* Here query is one of NPROC_ALL, NPROC_CURRENT. */ | |
234 | ||
235 | /* On systems with a modern affinity mask system call, we have | |
236 | sysconf (_SC_NPROCESSORS_CONF) | |
237 | >= sysconf (_SC_NPROCESSORS_ONLN) | |
238 | >= num_processors_via_affinity_mask () | |
239 | The first number is the number of CPUs configured in the system. | |
240 | The second number is the number of CPUs available to the scheduler. | |
241 | The third number is the number of CPUs available to the current process. | |
242 | ||
243 | Note! On Linux systems with glibc, the first and second number come from | |
244 | the /sys and /proc file systems (see | |
245 | glibc/sysdeps/unix/sysv/linux/getsysstats.c). | |
246 | In some situations these file systems are not mounted, and the sysconf | |
247 | call returns 1, which does not reflect the reality. */ | |
248 | ||
249 | if (query == NPROC_CURRENT) | |
250 | { | |
251 | /* Try the modern affinity mask system call. */ | |
252 | { | |
253 | unsigned long nprocs = num_processors_via_affinity_mask (); | |
254 | ||
255 | if (nprocs > 0) | |
256 | return nprocs; | |
257 | } | |
258 | ||
259 | #if defined _SC_NPROCESSORS_ONLN | |
260 | { /* This works on glibc, MacOS X 10.5, FreeBSD, AIX, OSF/1, Solaris, | |
261 | Cygwin, Haiku. */ | |
262 | long int nprocs = sysconf (_SC_NPROCESSORS_ONLN); | |
263 | if (nprocs > 0) | |
264 | return nprocs; | |
265 | } | |
266 | #endif | |
267 | } | |
268 | else /* query == NPROC_ALL */ | |
269 | { | |
270 | #if defined _SC_NPROCESSORS_CONF | |
271 | { /* This works on glibc, MacOS X 10.5, FreeBSD, AIX, OSF/1, Solaris, | |
272 | Cygwin, Haiku. */ | |
273 | long int nprocs = sysconf (_SC_NPROCESSORS_CONF); | |
274 | ||
275 | # if __GLIBC__ >= 2 && defined __linux__ | |
276 | /* On Linux systems with glibc, this information comes from the /sys and | |
277 | /proc file systems (see glibc/sysdeps/unix/sysv/linux/getsysstats.c). | |
278 | In some situations these file systems are not mounted, and the | |
279 | sysconf call returns 1. But we wish to guarantee that | |
280 | num_processors (NPROC_ALL) >= num_processors (NPROC_CURRENT). */ | |
281 | if (nprocs == 1) | |
282 | { | |
283 | unsigned long nprocs_current = num_processors_via_affinity_mask (); | |
284 | ||
285 | if (nprocs_current > 0) | |
286 | nprocs = nprocs_current; | |
287 | } | |
288 | # endif | |
289 | ||
290 | if (nprocs > 0) | |
291 | return nprocs; | |
292 | } | |
293 | #endif | |
294 | } | |
295 | ||
296 | #if HAVE_PSTAT_GETDYNAMIC | |
297 | { /* This works on HP-UX. */ | |
298 | struct pst_dynamic psd; | |
299 | if (pstat_getdynamic (&psd, sizeof psd, 1, 0) >= 0) | |
300 | { | |
301 | /* The field psd_proc_cnt contains the number of active processors. | |
302 | In newer releases of HP-UX 11, the field psd_max_proc_cnt includes | |
303 | deactivated processors. */ | |
304 | if (query == NPROC_CURRENT) | |
305 | { | |
306 | if (psd.psd_proc_cnt > 0) | |
307 | return psd.psd_proc_cnt; | |
308 | } | |
309 | else | |
310 | { | |
311 | if (psd.psd_max_proc_cnt > 0) | |
312 | return psd.psd_max_proc_cnt; | |
313 | } | |
314 | } | |
315 | } | |
316 | #endif | |
317 | ||
318 | #if HAVE_SYSMP && defined MP_NAPROCS && defined MP_NPROCS | |
319 | { /* This works on IRIX. */ | |
320 | /* MP_NPROCS yields the number of installed processors. | |
321 | MP_NAPROCS yields the number of processors available to unprivileged | |
322 | processes. */ | |
323 | int nprocs = | |
324 | sysmp (query == NPROC_CURRENT && getpid () != 0 | |
325 | ? MP_NAPROCS | |
326 | : MP_NPROCS); | |
327 | if (nprocs > 0) | |
328 | return nprocs; | |
329 | } | |
330 | #endif | |
331 | ||
332 | /* Finally, as fallback, use the APIs that don't distinguish between | |
333 | NPROC_CURRENT and NPROC_ALL. */ | |
334 | ||
335 | #if HAVE_SYSCTL && defined HW_NCPU | |
336 | { /* This works on MacOS X, FreeBSD, NetBSD, OpenBSD. */ | |
337 | int nprocs; | |
338 | size_t len = sizeof (nprocs); | |
339 | static int mib[2] = { CTL_HW, HW_NCPU }; | |
340 | ||
341 | if (sysctl (mib, ARRAY_SIZE (mib), &nprocs, &len, NULL, 0) == 0 | |
342 | && len == sizeof (nprocs) | |
343 | && 0 < nprocs) | |
344 | return nprocs; | |
345 | } | |
346 | #endif | |
347 | ||
348 | #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ | |
349 | { /* This works on native Windows platforms. */ | |
350 | SYSTEM_INFO system_info; | |
351 | GetSystemInfo (&system_info); | |
352 | if (0 < system_info.dwNumberOfProcessors) | |
353 | return system_info.dwNumberOfProcessors; | |
354 | } | |
355 | #endif | |
356 | ||
357 | return 1; | |
358 | } |