Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/bin/sage-num-threads.py
8815 views
1
#!/usr/bin/env python
2
#
3
# Determine the number of threads to be used by Sage.
4
#
5
# Outputs three space-separated numbers:
6
# 1) The number of threads to use for Sage, based on MAKE, MAKEFLAGS
7
# and SAGE_NUM_THREADS
8
# 2) The number of threads to use when parallel execution is explicitly
9
# asked for (e.g. sage -tp)
10
# 3) The number of CPU cores in the system, as determined by
11
# multiprocessing.cpu_count()
12
#
13
# AUTHOR: Jeroen Demeyer (2011-12-08): Trac ticket #12016
14
#
15
16
import os
17
import multiprocessing
18
import re
19
import math
20
21
def number_of_cores():
22
"""
23
Try to determine the number of CPU cores in this system.
24
If successful return that number. Otherwise return 1.
25
"""
26
# If the environment variable SAGE_NUM_CORES exists, use that value.
27
# This is useful for testing purposes.
28
try:
29
n = int(os.environ["SAGE_NUM_CORES"])
30
if n > 0:
31
return n
32
except (ValueError, KeyError):
33
pass
34
35
try:
36
n = multiprocessing.cpu_count()
37
if n > 0:
38
return n
39
except NotImplementedError:
40
pass
41
42
try: # Solaris fix
43
from subprocess import Popen, PIPE
44
p = Popen(['sysctl','-n','hw.ncpu'], stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
45
n = int(p.stdout.read().strip())
46
if n > 0:
47
return n
48
except (ValueError, OSError):
49
pass
50
51
return 1
52
53
def parse_jobs_from_MAKE(MAKE, unlimited=999999):
54
"""
55
Parse an environment variable like :envvar:`MAKE` for the number of
56
jobs specified. This looks at arguments ``-j``, ``--jobs``, ``-l``,
57
``--load-average``.
58
59
INPUT:
60
61
- ``MAKE`` -- The value of :envvar:`MAKE` or :envvar:`MAKEFLAGS`.
62
63
- ``unlimited`` -- The value to return when ``MAKE`` contains ``-j``
64
without argument and no ``-l`` option. Normally this is interpreted
65
as "unlimited".
66
67
OUTPUT:
68
69
The number of jobs specified by that variable. Raise ``KeyError``
70
if no number of jobs is specified in ``MAKE``.
71
"""
72
# First, find value of -j
73
# Since this is doing a greedy match on the left and non-greedy on the right,
74
# we find the last -j or --jobs
75
(j,num) = re.subn(r'^(.* )?(-j *|--jobs(=(?=[0-9])| +))([0-9]*)( .*?)?$', r'\4', MAKE, count=1)
76
if num < 1:
77
# No replacement done, i.e. no -j option found
78
raise KeyError, "No number of jobs specified"
79
elif j == "":
80
# j is empty: unlimited number of jobs! :-)
81
j = unlimited
82
else:
83
j = int(j)
84
if j <= 0:
85
raise ValueError, "Non-positive value specified for -j"
86
87
# Next, find the value of -l
88
# If it is specified, use this as an upper bound on j
89
(l,num) = re.subn(r'^(.* )?(-l *|--(load-average|max-load)(=(?=[0-9])| +))([0-9.]*)( .*?)?$', r'\5', MAKE, count=1)
90
if num < 1:
91
# No replacement done, i.e. no -l option found
92
pass
93
elif l == "":
94
# No load limit specified
95
pass
96
else:
97
l = int(math.ceil(float(l)))
98
# A load limit will never prevent starting at least one job
99
if l <= 1:
100
l = 1
101
j = min(j,l)
102
103
return j
104
105
def num_threads():
106
"""
107
Determine the number of threads from the environment variables
108
(in decreasing priority) :envvar:`SAGE_NUM_THREADS`, :envvar:`MAKE`,
109
:envvar:`MAKEFLAGS` and :envvar:`MFLAGS`.
110
111
If :envvar:`SAGE_NUM_THREADS` is 0 and neither :envvar:`MAKE` nor
112
:envvar:`MAKEFLAGS` specifies a number of jobs, the use a default
113
of ``min(8, number_of_cores)``.
114
115
OUTPUT:
116
117
a tuple (num_threads, num_threads_parallel, num_cores)
118
"""
119
num_cores = number_of_cores()
120
121
num_threads = None
122
# Handle MFLAGS only for backwards compatibility
123
try:
124
num_threads = parse_jobs_from_MAKE(os.environ["MFLAGS"], unlimited=2)
125
except (ValueError, KeyError):
126
pass
127
128
try:
129
# Prepend hyphen to MAKEFLAGS if it does not start with one
130
MAKEFLAGS=os.environ["MAKEFLAGS"]
131
if MAKEFLAGS[0] != '-':
132
MAKEFLAGS = '-' + MAKEFLAGS
133
# In MAKEFLAGS, "-j" does not mean unlimited. It probably
134
# means an inherited number of jobs, let us use 2 for safety.
135
num_threads = parse_jobs_from_MAKE(MAKEFLAGS, unlimited=2)
136
except (ValueError, KeyError, IndexError):
137
pass
138
139
try:
140
num_threads = parse_jobs_from_MAKE(os.environ["MAKE"])
141
except (ValueError, KeyError):
142
pass
143
144
# Number of threads to use when parallel execution is explicitly
145
# asked for
146
num_threads_parallel = num_threads
147
if num_threads_parallel is None:
148
num_threads_parallel = max(min(8, num_cores), 2)
149
150
try:
151
sage_num_threads = int(os.environ["SAGE_NUM_THREADS"])
152
if sage_num_threads == 0:
153
# If SAGE_NUM_THREADS is 0, use the default only
154
# if none of the above variables specified anything.
155
if num_threads is None:
156
num_threads = min(8, num_cores)
157
elif sage_num_threads > 0:
158
# SAGE_NUM_THREADS overrides everything
159
num_threads = sage_num_threads
160
num_threads_parallel = sage_num_threads
161
except (ValueError, KeyError):
162
pass
163
164
# If we still don't know, use 1 thread
165
if num_threads is None:
166
num_threads = 1
167
168
# Finally, use SAGE_NUM_THREADS_PARALLEL if set. A user isn't
169
# likely to set this, but it ensures that sage-env is idempotent
170
# if called more than once.
171
try:
172
sage_num_threads = int(os.environ["SAGE_NUM_THREADS_PARALLEL"])
173
if sage_num_threads > 0:
174
num_threads_parallel = sage_num_threads
175
except (ValueError, KeyError):
176
pass
177
178
return (num_threads, num_threads_parallel, num_cores)
179
180
print "%i %i %i"%num_threads()
181
182