LSSTApplications  19.0.0-14-gb0260a2+72efe9b372,20.0.0+7927753e06,20.0.0+8829bf0056,20.0.0+995114c5d2,20.0.0+b6f4b2abd1,20.0.0+bddc4f4cbe,20.0.0-1-g253301a+8829bf0056,20.0.0-1-g2b7511a+0d71a2d77f,20.0.0-1-g5b95a8c+7461dd0434,20.0.0-12-g321c96ea+23efe4bbff,20.0.0-16-gfab17e72e+fdf35455f6,20.0.0-2-g0070d88+ba3ffc8f0b,20.0.0-2-g4dae9ad+ee58a624b3,20.0.0-2-g61b8584+5d3db074ba,20.0.0-2-gb780d76+d529cf1a41,20.0.0-2-ged6426c+226a441f5f,20.0.0-2-gf072044+8829bf0056,20.0.0-2-gf1f7952+ee58a624b3,20.0.0-20-geae50cf+e37fec0aee,20.0.0-25-g3dcad98+544a109665,20.0.0-25-g5eafb0f+ee58a624b3,20.0.0-27-g64178ef+f1f297b00a,20.0.0-3-g4cc78c6+e0676b0dc8,20.0.0-3-g8f21e14+4fd2c12c9a,20.0.0-3-gbd60e8c+187b78b4b8,20.0.0-3-gbecbe05+48431fa087,20.0.0-38-ge4adf513+a12e1f8e37,20.0.0-4-g97dc21a+544a109665,20.0.0-4-gb4befbc+087873070b,20.0.0-4-gf910f65+5d3db074ba,20.0.0-5-gdfe0fee+199202a608,20.0.0-5-gfbfe500+d529cf1a41,20.0.0-6-g64f541c+d529cf1a41,20.0.0-6-g9a5b7a1+a1cd37312e,20.0.0-68-ga3f3dda+5fca18c6a4,20.0.0-9-g4aef684+e18322736b,w.2020.45
LSSTDataManagementBasePackage
threads.cc
Go to the documentation of this file.
1 #include <cstddef>
2 #include <cstdlib>
3 
4 #include <iostream>
5 
6 #include "lsst/base/library.h"
7 #include "lsst/base/threads.h"
8 
9 namespace lsst {
10 namespace base {
11 
12 namespace {
13 
14 typedef int (Getter)(void);
15 typedef void (Setter)(int);
16 
17 Getter* getOpenBlasThreads = NULL;
18 Setter* setOpenBlasThreads = NULL;
19 Getter* getMklThreads = NULL;
20 Setter* setMklThreads = NULL;
21 
22 bool loadOpenBlas() {
23  if (haveOpenBlas) {
24  return true;
25  }
26 
27  try {
28  getOpenBlasThreads = loadSymbol<Getter>("libopenblas", "goto_get_num_procs");
29  // Believe it or not, the function which returns the number of threads
30  // that OpenBLAS will actually use is called "goto_get_num_procs". The
31  // number returned by THIS function, and not "openblas_get_num_threads"
32  // nor "openblas_get_num_procs", is modified by calls to
33  // "openblas_set_num_threads". Confused? Me too.
34  setOpenBlasThreads = loadSymbol<Setter>("libopenblas", "openblas_set_num_threads");
35  } catch (LibraryException const&) {
36  return false;
37  }
38  return true;
39 }
40 
41 bool loadMkl() {
42  if (haveMkl) {
43  return true;
44  }
45 
46  if (!canLoadLibrary("libmkl_core")) {
47  return false;
48  }
49 
50  // We will set the number of threads through OMP because that's easier.
51  // (There are multiple mkl libraries, and each has a function to set the number of threads.)
52  try {
53  getMklThreads = loadSymbol<Getter>("libiomp5", "omp_get_max_threads");
54  setMklThreads = loadSymbol<Setter>("libiomp5", "omp_set_num_threads");
55  } catch (LibraryException const&) {
56  return false;
57  }
58  return true;
59 }
60 
61 bool disableImplicitThreadingImpl(
62  std::string const& package, // Name of package
63  std::initializer_list<std::string const> envvars, // Environment variables to check
64  Getter* getter, // Function to get number of threads
65  Setter* setter // Function to set number of threads
66  )
67 {
68  for (auto&& ss : envvars) {
69  if (std::getenv(ss.c_str())) {
70  return false; // User is being explicit
71  }
72  }
73  unsigned int numThreads = getter();
74  if (numThreads <= 1) {
75  return false;
76  }
77 
78  std::cerr << "WARNING: You are using " << package << " with multiple threads (" << numThreads <<
79  "), but have not\n" <<
80  "specified the number of threads using one of the " << package <<
81  " environment variables:\n";
82 
83  // Join list of environment variables. An alternative is
84  // boost::algorithm::join(envvars, ", ")
85  // but we are trying to control the proliferation of Boost usage.
86  std::cerr << *envvars.begin();
87  for (auto iter = envvars.begin() + 1; iter != envvars.end(); ++iter) {
88  std::cerr << ", " << *iter;
89  }
90 
91  std::cerr << ".\n" <<
92  "This may indicate that you are unintentionally using multiple threads, which may\n"
93  "cause problems. WE HAVE THEREFORE DISABLED " << package << " THREADING. If you know\n" <<
94  "what you are doing and want threads enabled implicitly, set the environment\n" <<
95  "variable " << allowEnvvar << ".\n";
96  setter(1);
97  return true;
98 }
99 
100 
101 } // anonymous namespace
102 
103 
104 extern bool const haveOpenBlas = loadOpenBlas();
105 extern bool const haveMkl = loadMkl();
106 
107 void setNumThreads(unsigned int numThreads)
108 {
109  if (!haveOpenBlas && !haveMkl && numThreads != 0 && numThreads != 1) {
110  throw NoThreadsException();
111  }
112  if (haveOpenBlas) {
113  setOpenBlasThreads(numThreads);
114  }
115  if (haveMkl) {
116  setMklThreads(numThreads);
117  }
118 }
119 
120 unsigned int getNumThreads()
121 {
122  unsigned int numThreads = 0;
123  if (haveOpenBlas) {
124  numThreads = std::max(numThreads, static_cast<unsigned int>(getOpenBlasThreads()));
125  }
126  if (haveMkl) {
127  numThreads = std::max(numThreads, static_cast<unsigned int>(getMklThreads()));
128  }
129  return numThreads;
130 }
131 
133 {
134  if (std::getenv(allowEnvvar.c_str())) {
135  return false; // The user knows what he's doing; no intervention performed
136  }
137 
138  bool intervened = false; // Did we intervene on behalf of the user?
139  if (haveOpenBlas) {
140  intervened |= disableImplicitThreadingImpl(
141  "OpenBLAS",
142  {"OPENBLAS_NUM_THREADS", "GOTO_NUM_THREADS", "OMP_NUM_THREADS"},
143  getOpenBlasThreads,
144  setOpenBlasThreads
145  );
146  }
147  if (haveMkl) {
148  intervened |= disableImplicitThreadingImpl(
149  "MKL",
150  {"MKL_NUM_THREADS", "MKL_DOMAIN_NUM_THREADS", "OMP_NUM_THREADS"},
151  getMklThreads,
152  setMklThreads
153  );
154  }
155  return intervened;
156 }
157 
158 }} // namespace lsst::base
std::string
STL class.
threads.h
lsst::base::allowEnvvar
std::string const allowEnvvar
Environment variable to allow implicit threading.
Definition: threads.h:16
lsst::base::haveMkl
bool const haveMkl
Is MKL available?
Definition: threads.cc:105
lsst::base::disableImplicitThreading
bool disableImplicitThreading()
Disable threading that has not been set explicitly.
Definition: threads.cc:132
std::cerr
lsst::base::setNumThreads
void setNumThreads(unsigned int numThreads)
Set number of threads to use.
Definition: threads.cc:107
std::string::c_str
T c_str(T... args)
std::getenv
T getenv(T... args)
library.h
lsst
A base class for image defects.
Definition: imageAlgorithm.dox:1
lsst::base::canLoadLibrary
bool canLoadLibrary(std::string const &libName)
Return whether we can load a library.
Definition: library.cc:37
std::max
T max(T... args)
astshim.fitsChanContinued.iter
def iter(self)
Definition: fitsChanContinued.py:88
lsst::base::haveOpenBlas
bool const haveOpenBlas
Is OpenBLAS available?
Definition: threads.cc:104
lsst::base::getNumThreads
unsigned int getNumThreads()
Get maximum number of threads we might use.
Definition: threads.cc:120
lsst::base::NoThreadsException
No threading library is available.
Definition: threads.h:20
std::initializer_list