Tpetra parallel linear algebra  Version of the Day
Tpetra_Details_Behavior.cpp
2 #include "TpetraCore_config.h"
3 #include <algorithm> // std::transform
4 #include <atomic> // std::atomic_thread_fence, std::memory_order_release
5 #include <cstdlib> // std::getenv
6 #include <cctype> // std::toupper
7 #include <mutex> // std::call_once, std::once_flag
8 #include <string>
9 #include <map>
10 #include <vector>
11 #include <functional>
12 
13 namespace Tpetra {
14 namespace Details {
15 
16 namespace BehaviorDetails {
17 std::map<std::string, std::map<std::string, bool> > namedVariableMap_;
18 }
19 
20 namespace { // (anonymous)
21 
22  enum EnvironmentVariableState
23  {
24  EnvironmentVariableIsSet_ON,
25  EnvironmentVariableIsSet_OFF,
26  EnvironmentVariableIsSet,
27  EnvironmentVariableIsNotSet
28  };
29 
30  // See example here:
31  //
32  // http://en.cppreference.com/w/cpp/string/byte/toupper
33  std::string stringToUpper (std::string s)
34  {
35  std::transform (s.begin (), s.end (), s.begin (),
36  [] (unsigned char c) { return std::toupper (c); });
37  return s;
38  }
39 
40  void
41  split(const std::string& s,
42  std::function<void(const std::string&)> f,
43  const char sep=',')
44  {
45  typedef std::string::size_type size_type;
46  size_type cur_pos, last_pos=0, length=s.length();
47  while(last_pos < length + 1)
48  {
49  cur_pos = s.find_first_of(sep, last_pos);
50  if(cur_pos == std::string::npos)
51  {
52  cur_pos = length;
53  }
54  if(cur_pos!=last_pos) {
55  auto token = std::string(s.data()+last_pos, (size_type)cur_pos-last_pos);
56  f(token);
57  }
58  last_pos = cur_pos + 1;
59  }
60  return;
61  }
62 
63  EnvironmentVariableState
64  environmentVariableState(const std::string& environmentVariableValue)
65  {
66  std::string v = stringToUpper(environmentVariableValue);
67  if (v == "1" || v == "YES" || v == "TRUE" || v == "ON")
68  // Environment variable is "ON"
69  return EnvironmentVariableIsSet_ON;
70  else if (v == "0" || v == "NO" || v == "FALSE" || v == "OFF")
71  // Environment variable is "OFF"
72  return EnvironmentVariableIsSet_OFF;
73  // Environment has some other non-boolean value
74  return EnvironmentVariableIsSet;
75  }
76 
77  void
78  setEnvironmentVariableMap (const char environmentVariableName[],
79  std::map<std::string,std::map<std::string, bool> >& valsMap,
80  const bool defaultValue)
81  {
82  using std::map;
83  using std::getenv;
84  using std::string;
85  using std::vector;
86 
87  // Set the default value for this variable
88  valsMap[environmentVariableName] = map<string,bool>{{"DEFAULT", defaultValue}};
89 
90  const char* varVal = getenv (environmentVariableName);
91  if (varVal == NULL) {
92  // Environment variable is not set, use the default value for any named
93  // variants
94  return;
95  }
96 
97  // Variable is not empty.
98  const string varStr(varVal);
99  vector<string> names;
100  split(varStr, [&](const string& x){names.push_back(x);});
101  for (auto const& name: names) {
102  auto state = environmentVariableState(name);
103  if (state == EnvironmentVariableIsSet_ON) {
104  // Environment variable was set as ENVAR_NAME=[1,YES,TRUE,ON]
105  // Global value takes precedence
106  valsMap[environmentVariableName]["DEFAULT"] = true;
107  }
108  else if (state == EnvironmentVariableIsSet_OFF) {
109  // Environment variable was set as ENVAR_NAME=[0,NO,FALSE,OFF]
110  // Global value takes precedence
111  valsMap[environmentVariableName]["DEFAULT"] = false;
112  }
113  else {
114  // Environment variable was set as ENVAR_NAME=...:name:...
115  // So we set the mapping true for this named variant
116  valsMap[environmentVariableName][name] = true;
117  }
118  }
119  return;
120  }
121 
122  bool
123  getEnvironmentVariableAsBool (const char environmentVariableName[],
124  const bool defaultValue)
125  {
126  const char* varVal = std::getenv (environmentVariableName);
127 
128  bool retVal = defaultValue;
129  if (varVal != NULL) {
130  auto state = environmentVariableState(std::string(varVal));
131  if (state == EnvironmentVariableIsSet_ON) retVal = true;
132  else if (state == EnvironmentVariableIsSet_OFF) retVal = false;
133  }
134  return retVal;
135  }
136 
137  bool
138  idempotentlyGetEnvironmentVariableAsBool (std::once_flag& once_flag,
139  bool& value,
140  bool& initialized,
141  const char environmentVariableName[],
142  const bool defaultValue)
143  {
144  // The extra "initialized" check avoids the cost of synchronizing
145  // on the std::call_once for every call to this function. We want
146  // it to be cheap to get the Boolean value, so that users aren't
147  // tempted to try to cache it themselves.
148  if (! initialized) {
149  std::call_once (once_flag, [&] () {
150  value = getEnvironmentVariableAsBool (environmentVariableName,
151  defaultValue);
152  // http://preshing.com/20130922/acquire-and-release-fences/
153  //
154  // "A release fence prevents the memory reordering of any
155  // read or write which precedes it in program order with any
156  // write which follows it in program order."
157  //
158  // The point is to prevent the assignment to 'value' from
159  // getting reordered after the assignment to 'initialized'
160  // (the so-called "StoreStore" reordering). That would be
161  // bad in this case, because then other threads might read
162  // 'initialized' as true, yet would fail to pick up the
163  // change to 'value'.
164  //
165  // It's harmless if other threads don't see the write to
166  // 'initialized', but did see the write to 'value'. In that
167  // case, they would just attempt and fail to enter the
168  // std::call_once, and return (the correct value of)
169  // 'value'.
170  std::atomic_thread_fence (std::memory_order_release);
171 
172  initialized = true;
173  });
174  }
175  return value;
176  }
177 
178  bool
179  idempotentlyGetNamedEnvironmentVariableAsBool (const char name[],
180  std::once_flag& once_flag,
181  bool& initialized,
182  const char environmentVariableName[],
183  const bool defaultValue)
184  {
185  using BehaviorDetails::namedVariableMap_;
186  // The extra "initialized" check avoids the cost of synchronizing
187  // on the std::call_once for every call to this function. We want
188  // it to be cheap to fill the namedVariableMap_, so that users aren't
189  // tempted to try to cache it themselves.
190  if (! initialized) {
191  std::call_once (once_flag, [&] () {
192  setEnvironmentVariableMap (environmentVariableName,
193  namedVariableMap_,
194  defaultValue);
195  // http://preshing.com/20130922/acquire-and-release-fences/
196  //
197  // "A release fence prevents the memory reordering of any
198  // read or write which precedes it in program order with any
199  // write which follows it in program order."
200  //
201  // The point is to prevent the assignment to 'namedValueMap' from
202  // getting reordered after the assignment to 'initialized' (the
203  // so-called "StoreStore" reordering). That would be bad in this
204  // case, because then other threads might read 'initialized' as true,
205  // yet would fail to pick up the change to 'namedValueMap'.
206  //
207  // It's harmless if other threads don't see the write to
208  // 'initialized', but did see the write to 'namedValueMap'. In that
209  // case, they would just attempt and fail to enter the std::call_once,
210  // and return (the correct value of) 'namedValueMap'.
211  std::atomic_thread_fence (std::memory_order_release);
212 
213  initialized = true;
214  });
215  }
216  auto thisEnvironmentVariableMap = namedVariableMap_[environmentVariableName];
217  auto thisEnvironmentVariable = thisEnvironmentVariableMap.find(name);
218  if (thisEnvironmentVariable != thisEnvironmentVariableMap.end())
219  return thisEnvironmentVariable->second;
220  return thisEnvironmentVariableMap["DEFAULT"];
221  }
222 
223  constexpr bool debugDefault () {
224 #ifdef HAVE_TPETRA_DEBUG
225  return true;
226 #else
227  return false;
228 #endif // HAVE_TPETRA_DEBUG
229  }
230 
231  constexpr bool verboseDefault () {
232  return false;
233  }
234 
235  constexpr bool assumeMpiIsCudaAwareDefault () {
236 #ifdef TPETRA_ASSUME_CUDA_AWARE_MPI
237  return true;
238 #else
239  return false;
240 #endif // TPETRA_ASSUME_CUDA_AWARE_MPI
241  }
242 
243 } // namespace (anonymous)
244 
246 {
247  constexpr char envVarName[] = "TPETRA_DEBUG";
248  constexpr bool defaultValue = debugDefault ();
249 
250  static std::once_flag flag_;
251  static bool value_ = defaultValue;
252  static bool initialized_ = false;
253  return idempotentlyGetEnvironmentVariableAsBool (flag_,
254  value_,
255  initialized_,
256  envVarName,
257  defaultValue);
258 }
259 
261 {
262  constexpr char envVarName[] = "TPETRA_VERBOSE";
263  constexpr bool defaultValue = verboseDefault ();
264 
265  static std::once_flag flag_;
266  static bool value_ = defaultValue;
267  static bool initialized_ = false;
268  return idempotentlyGetEnvironmentVariableAsBool (flag_,
269  value_,
270  initialized_,
271  envVarName,
272  defaultValue);
273 }
274 
276 {
277  constexpr char envVarName[] = "TPETRA_ASSUME_CUDA_AWARE_MPI";
278  constexpr bool defaultValue = assumeMpiIsCudaAwareDefault ();
279 
280  static std::once_flag flag_;
281  static bool value_ = defaultValue;
282  static bool initialized_ = false;
283  return idempotentlyGetEnvironmentVariableAsBool (flag_,
284  value_,
285  initialized_,
286  envVarName,
287  defaultValue);
288 }
289 
290 bool Behavior::debug (const char name[])
291 {
292  constexpr char envVarName[] = "TPETRA_DEBUG";
293  constexpr bool defaultValue = false;
294 
295  static std::once_flag flag_;
296  static bool initialized_ = false;
297  return idempotentlyGetNamedEnvironmentVariableAsBool (name,
298  flag_,
299  initialized_,
300  envVarName,
301  defaultValue);
302 }
303 
304 bool Behavior::verbose (const char name[])
305 {
306  constexpr char envVarName[] = "TPETRA_VERBOSE";
307  constexpr bool defaultValue = false;
308 
309  static std::once_flag flag_;
310  static bool initialized_ = false;
311  return idempotentlyGetNamedEnvironmentVariableAsBool (name,
312  flag_,
313  initialized_,
314  envVarName,
315  defaultValue);
316 }
317 
318 } // namespace Details
319 } // namespace Tpetra
320 
Tpetra_Details_Behavior.hpp
Declaration of Tpetra::Details::Behavior, a class that describes Tpetra's behavior.
Tpetra::Details::Behavior::assumeMpiIsCudaAware
static bool assumeMpiIsCudaAware()
Whether to assume that MPI is CUDA aware.
Definition: Tpetra_Details_Behavior.cpp:275
Details
Implementation details of Tpetra.
Tpetra::Details::Behavior::debug
static bool debug()
Whether Tpetra is in debug mode.
Definition: Tpetra_Details_Behavior.cpp:245
Tpetra::Details::Behavior::verbose
static bool verbose()
Whether Tpetra is in verbose mode.
Definition: Tpetra_Details_Behavior.cpp:260
Tpetra
Namespace Tpetra contains the class and methods constituting the Tpetra library.