LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
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
T c_str(T... args)
No threading library is available.
Definition: threads.h:20
T getenv(T... args)
T max(T... args)
unsigned int getNumThreads()
Get maximum number of threads we might use.
Definition: threads.cc:120
bool canLoadLibrary(std::string const &libName)
Return whether we can load a library.
Definition: library.cc:37
bool disableImplicitThreading()
Disable threading that has not been set explicitly.
Definition: threads.cc:132
bool const haveOpenBlas
Is OpenBLAS available?
Definition: threads.cc:104
std::string const allowEnvvar
Environment variable to allow implicit threading.
Definition: threads.h:16
bool const haveMkl
Is MKL available?
Definition: threads.cc:105
void setNumThreads(unsigned int numThreads)
Set number of threads to use.
Definition: threads.cc:107
A base class for image defects.