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 
Namespace Tpetra contains the class and methods constituting the Tpetra library.
static bool debug()
Whether Tpetra is in debug mode.
Implementation details of Tpetra.
static bool assumeMpiIsCudaAware()
Whether to assume that MPI is CUDA aware.
static bool verbose()
Whether Tpetra is in verbose mode.
Declaration of Tpetra::Details::Behavior, a class that describes Tpetra&#39;s behavior.