LSST Applications  21.0.0-147-g0e635eb1+1acddb5be5,22.0.0+052faf71bd,22.0.0+1ea9a8b2b2,22.0.0+6312710a6c,22.0.0+729191ecac,22.0.0+7589c3a021,22.0.0+9f079a9461,22.0.1-1-g7d6de66+b8044ec9de,22.0.1-1-g87000a6+536b1ee016,22.0.1-1-g8e32f31+6312710a6c,22.0.1-10-gd060f87+016f7cdc03,22.0.1-12-g9c3108e+df145f6f68,22.0.1-16-g314fa6d+c825727ab8,22.0.1-19-g93a5c75+d23f2fb6d8,22.0.1-19-gb93eaa13+aab3ef7709,22.0.1-2-g8ef0a89+b8044ec9de,22.0.1-2-g92698f7+9f079a9461,22.0.1-2-ga9b0f51+052faf71bd,22.0.1-2-gac51dbf+052faf71bd,22.0.1-2-gb66926d+6312710a6c,22.0.1-2-gcb770ba+09e3807989,22.0.1-20-g32debb5+b8044ec9de,22.0.1-23-gc2439a9a+fb0756638e,22.0.1-3-g496fd5d+09117f784f,22.0.1-3-g59f966b+1e6ba2c031,22.0.1-3-g849a1b8+f8b568069f,22.0.1-3-gaaec9c0+c5c846a8b1,22.0.1-32-g5ddfab5d3+60ce4897b0,22.0.1-4-g037fbe1+64e601228d,22.0.1-4-g8623105+b8044ec9de,22.0.1-5-g096abc9+d18c45d440,22.0.1-5-g15c806e+57f5c03693,22.0.1-7-gba73697+57f5c03693,master-g6e05de7fdc+c1283a92b8,master-g72cdda8301+729191ecac,w.2021.39
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.