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