Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
stacked_timer.cpp
Go to the documentation of this file.
1 // @HEADER
2 // @HEADER
3 
10 #include "Teuchos_DefaultComm.hpp"
11 #include <sstream>
12 #include <thread> // std::this_thread::sleep_for;
13 #include <tuple>
14 #include <regex>
15 #include <iterator>
16 #include <limits>
17 
18 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOSCORE)
19 #include "Kokkos_Core.hpp"
20 #endif
21 
22 TEUCHOS_UNIT_TEST(PerformanceMonitorBase, UnsortedMergeUnion) {
23 
25 
26  Teuchos::Array<std::string> a,b, tmp_a, tmp_b;
27 
28  a.push_back("foo");
29  a.push_back("bar");
30  a.push_back("car");
31 
32  b.push_back("car");
33  b.push_back("bar");
34  b.push_back("cat");
35 
36 
37  tmp_a=a;
38  tmp_b=b;
40  TEST_EQUALITY(tmp_b.size(),4);
41  TEST_EQUALITY(tmp_b[0], "car");
42  TEST_EQUALITY(tmp_b[1], "bar");
43  TEST_EQUALITY(tmp_b[2], "cat");
44  TEST_EQUALITY(tmp_b[3], "foo");
45 }
46 
47 TEUCHOS_UNIT_TEST(PerformanceMonitorBase, UnsortedMergeIntersection) {
48 
50 
51  Teuchos::Array<std::string> a,b, tmp_a, tmp_b;
52 
53  a.push_back("foo");
54  a.push_back("bar");
55  a.push_back("car");
56 
57  b.push_back("car");
58  b.push_back("bar");
59  b.push_back("cat");
60 
61 
62  tmp_a=a;
63  tmp_b=b;
65  TEST_EQUALITY(tmp_b.size(),2);
66  TEST_EQUALITY(tmp_b[0], "car");
67  TEST_EQUALITY(tmp_b[1], "bar");
68 }
69 
70 TEUCHOS_UNIT_TEST(StackedTimer, Basic)
71 {
73  const int myRank = Teuchos::rank(*comm);
74 
75  Teuchos::StackedTimer timer("My New Timer");
76  timer.enableVerbose(true);
77  timer.setVerboseOstream(Teuchos::rcpFromRef(std::cout));
78  timer.start("Total Time");
79  {
80  for (int i=0; i < 10; ++i) {
81 
82  timer.start("Assembly");
83  std::this_thread::sleep_for(std::chrono::milliseconds{100});
84  timer.stop("Assembly");
85 
86  timer.start("Solve");
87  {
88  timer.start("Prec");
89  std::this_thread::sleep_for(std::chrono::milliseconds{50});
90  timer.stop("Prec");
91 
92  // Test different timers on different mpi processes
93  if (myRank == 0 ) {
94  const std::string label = "Rank 0 ONLY";
95  timer.start(label);
96  std::this_thread::sleep_for(std::chrono::milliseconds{50});
97  TEST_ASSERT((timer.findTimer("My New Timer@Total Time@Solve@Rank 0 ONLY")).running);
98  timer.stop(label);
99  TEST_ASSERT(!(timer.findTimer("My New Timer@Total Time@Solve@Rank 0 ONLY")).running);
100  } else {
101  timer.start("Not Rank 0");
102  std::this_thread::sleep_for(std::chrono::milliseconds{50});
103  TEST_ASSERT((timer.findTimer("My New Timer@Total Time@Solve@Not Rank 0")).running);
104  timer.stop("Not Rank 0");
105  TEST_ASSERT(!(timer.findTimer("My New Timer@Total Time@Solve@Not Rank 0")).running);
106  }
107  }
108  timer.stop("Solve");
109 
110  }
111  }
112  timer.stop("Total Time");
113  timer.stopBaseTimer();
114 
115  TEST_EQUALITY((timer.findTimer("My New Timer@Total Time")).count, 1);
116  TEST_EQUALITY((timer.findTimer("My New Timer@Total Time@Assembly")).count, 10);
117  TEST_EQUALITY((timer.findTimer("My New Timer@Total Time@Solve")).count, 10);
118  TEST_EQUALITY((timer.findTimer("My New Timer@Total Time@Solve@Prec")).count, 10);
119 
120  // Test for exception for bad timer name
121  TEST_THROW(timer.findTimer("Testing misspelled timer name!"),std::runtime_error);
122 
123  // Pre-aggregation
124  if (myRank == 0) {
125  TEST_EQUALITY((timer.findTimer("My New Timer@Total Time@Solve@Rank 0 ONLY")).count, 10);
126  }
127  else {
128  TEST_EQUALITY((timer.findTimer("My New Timer@Total Time@Solve@Not Rank 0")).count, 10);
129  }
130 
132  options.output_histogram=true;
133  options.num_histogram=3;
134  options.print_warnings=false;
135 
136  // Get the report
137  std::stringstream sout1;
138  timer.report(sout1, comm, options);
139 
140  // Make sure can call report() multiple times, i.e. aggregation
141  // resets correctly for each call report()
142  std::stringstream sout2;
143  timer.report(sout2, comm, options);
144  TEST_EQUALITY(sout1.str(),sout2.str());
145 
146  // Gold file results (timer name,expected runtime,number of calls)
147  std::vector<std::tuple<std::string,double,unsigned long>> lineChecks;
148  lineChecks.push_back(std::make_tuple("My New Timer:",2.0,1));
149  lineChecks.push_back(std::make_tuple("Total Time:",2.0,1));
150  lineChecks.push_back(std::make_tuple("Assembly:",1.0,10));
151  lineChecks.push_back(std::make_tuple("Solve:",1.0,10));
152  lineChecks.push_back(std::make_tuple("Prec:",0.5,10));
153 
154  // Check the report() output. Read the first few lines and parse the
155  // expected timer label, the runtime and the counts.
156  //
157  // * NOTE: The report only combines values to a single MPI process, so
158  // only check on that process.
159  // * NOTE: regex not supported in gcc until 4.9. Can drop this check
160  // when Trilinos drops support for gcc 4.8.
161 #if !defined(__GNUC__) \
162  || ( defined(__GNUC__) && (__GNUC__ > 4) ) \
163  || ( defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC__MINOR__ > 8) )
164 
165  if (myRank == 0) {
166  const double timerTolerance = 0.25; // +- 0.25 seconds
167  std::istringstream is(sout1.str());
168  for (const auto& check : lineChecks) {
169 
170  std::string line;
171  std::getline(is,line);
172  std::smatch regexSMatch;
173  std::regex timerName(std::get<0>(check));
174  std::regex_search(line,regexSMatch,timerName);
175  TEST_ASSERT(!regexSMatch.empty());
176 
177  // Split string to get time and count
178  std::regex delimiter(":\\s|\\s\\[|\\]\\s");
179  std::sregex_token_iterator tok(line.begin(), line.end(),delimiter,-1);
180 
181  const std::string timeAsString = (++tok)->str();
182  const double time = std::stod(timeAsString);
183  TEST_FLOATING_EQUALITY(time,std::get<1>(check),timerTolerance);
184 
185  const std::string countAsString = (++tok)->str();
186  const unsigned long count = std::stoul(countAsString);
187  TEST_EQUALITY(count,std::get<2>(check));
188  }
189  }
190 #endif
191 
192  // Print to screen
193  out << "\n### Printing default report ###" << std::endl;
195  timer.report(out, comm, defaultOptions);
196 
197  // Test some options
198  out << "\n### Printing aligned_column with timers names on left ###" << std::endl;
199  options.output_fraction = true;
200  options.output_total_updates = true;
201  options.output_minmax = true;
202  options.output_histogram = true;
203  options.num_histogram = 3;
204  options.align_columns = true;
205  timer.report(out, comm, options);
206 
207  // Toggle names before values
209  out << "\n### Printing aligned_column with timers names on right ###" << std::endl;
210  options.print_names_before_values = false;
211  timer.report(out, comm, options);
212 }
213 
214 TEUCHOS_UNIT_TEST(StackedTimer, UnitTestSupport)
215 {
217  const int myRank = Teuchos::rank(*comm);
218 
219  const auto timeMonitorDefaultStackedTimer = Teuchos::TimeMonitor::getStackedTimer();
220  const auto timer = Teuchos::rcp(new Teuchos::StackedTimer("Total Time", false));
221  timer->startBaseTimer();
222  for (int i=0; i < 10; ++i) {
223  timer-> start("Subtask");
224  timer->incrementUpdates();
225  timer->incrementUpdates(2);
226  timer-> stop("Subtask");
227  }
228  timer->stopBaseTimer();
229 
230  // If users want to set timer values for unit testing, force them to
231  // const_cast the timer by returning a const Timer object.
232  auto top_timer = const_cast<Teuchos::BaseTimer*>(timer->findBaseTimer("Total Time"));
233  auto sub_timer = const_cast<Teuchos::BaseTimer*>(timer->findBaseTimer("Total Time@Subtask"));
234  TEST_ASSERT(top_timer != nullptr);
235  TEST_ASSERT(sub_timer != nullptr);
236 
237  // Test for exception for bad timer name
238  TEST_THROW(timer->findBaseTimer("Testing misspelled timer name!"),std::runtime_error);
239 
240  {
241  TEST_EQUALITY(top_timer->numCalls(),1);
242  TEST_EQUALITY(top_timer->numUpdates(),0);
243  TEST_EQUALITY(sub_timer->numCalls(),10);
244  TEST_EQUALITY(sub_timer->numUpdates(),30);
245  }
246 
247  // Test the serial version of report
248  if (myRank == 0)
249  timer->report(out);
250 
251  // Override timers for unit testing
252  top_timer->setAccumulatedTime(5000.0);
253  top_timer->overrideNumCallsForUnitTesting(2);
254  top_timer->overrideNumUpdatesForUnitTesting(3);
255  sub_timer->setAccumulatedTime(4000.0);
256  sub_timer->overrideNumCallsForUnitTesting(4);
257  sub_timer->overrideNumUpdatesForUnitTesting(5);
258  {
259  const double timerTolerance = 100.0 * std::numeric_limits<double>::epsilon();
260  TEST_FLOATING_EQUALITY(5000.0,top_timer->accumulatedTime(),timerTolerance);
261  TEST_EQUALITY(top_timer->numCalls(),2);
262  TEST_EQUALITY(top_timer->numUpdates(),3);
263  TEST_FLOATING_EQUALITY(4000.0,sub_timer->accumulatedTime(),timerTolerance);
264  TEST_EQUALITY(sub_timer->numCalls(),4);
265  TEST_EQUALITY(sub_timer->numUpdates(),5);
266  }
267 
268  if (myRank == 0)
269  timer->report(out);
270 }
271 
272 TEUCHOS_UNIT_TEST(StackedTimer, TimeMonitorInteroperability)
273 {
275 
276  const auto diffTimer = Teuchos::TimeMonitor::getNewTimer("Diffusion Term");
277  const auto rxnTimer = Teuchos::TimeMonitor::getNewTimer("Reaction Term");
278  const auto precTimer = Teuchos::TimeMonitor::getNewTimer("Prec");
279  const auto gmresTimer = Teuchos::TimeMonitor::getNewTimer("GMRES");
280 
281  // Test the set and get stacked timer methods on TimeMonitor
282  const auto timeMonitorDefaultStackedTimer = Teuchos::TimeMonitor::getStackedTimer();
283  const auto timer = Teuchos::rcp(new Teuchos::StackedTimer("TM:Interoperability"));
284  TEST_ASSERT(nonnull(timeMonitorDefaultStackedTimer));
285  TEST_ASSERT(nonnull(timer));
286  TEST_ASSERT(timeMonitorDefaultStackedTimer != timer);
289 
290  timer->start("Total Time");
291  {
292  for (int i=0; i < 10; ++i) {
293 
294  timer->start("Assembly");
295  {
296  {
297  Teuchos::TimeMonitor tm(*diffTimer);
298  std::this_thread::sleep_for(std::chrono::milliseconds{25});
299  }
300  {
301  Teuchos::TimeMonitor tm(*rxnTimer);
302  std::this_thread::sleep_for(std::chrono::milliseconds{75});
303  }
304  // Remainder
305  std::this_thread::sleep_for(std::chrono::milliseconds{100});
306  }
307  timer->stop("Assembly");
308  timer->start("Solve");
309  {
310  {
311  Teuchos::TimeMonitor tm(*precTimer);
312  std::this_thread::sleep_for(std::chrono::milliseconds{50});
313  }
314  {
315  Teuchos::TimeMonitor tm(*gmresTimer);
316  std::this_thread::sleep_for(std::chrono::milliseconds{50});
317  }
318  // Remainder
319  std::this_thread::sleep_for(std::chrono::milliseconds{100});
320  }
321  timer->stop("Solve");
322  std::this_thread::sleep_for(std::chrono::milliseconds{100});
323  }
324  }
325  timer->stop("Total Time");
326  timer->stopBaseTimer();
327 
328  assert(size(*comm)>0);
329 
330  TEST_EQUALITY((timer->findTimer("TM:Interoperability@Total Time")).count, 1);
331  TEST_EQUALITY((timer->findTimer("TM:Interoperability@Total Time@Assembly")).count, 10);
332 
333  // Make sure the TimeMonitor added the timers
334 #ifdef HAVE_TEUCHOS_ADD_TIME_MONITOR_TO_STACKED_TIMER
335  TEST_EQUALITY((timer->findTimer("TM:Interoperability@Total Time@Solve@Prec")).count, 10);
336  TEST_EQUALITY((timer->findTimer("TM:Interoperability@Total Time@Solve@GMRES")).count, 10);
337 #endif
338 
340  out << "\n### Printing default report ###" << std::endl;
341  options.output_histogram=true;
342  options.num_histogram=3;
343  options.output_fraction=true;
344  timer->report(out, comm, options);
345 
346  out << "\n### Printing aligned_column with timers names on left ###" << std::endl;
347  options.align_columns = true;
348  timer->report(out, comm, options);
349 
350  out << "\n### Printing aligned_column with timers names on right ###" << std::endl;
351  // options.print_names_before_values=false requires that
352  // options.align_output=true. The code will automatically fix this
353  // and print a warning if warnings are enabled. Testing this here by
354  // specifying the incorrect logic.
355  options.align_columns = false;
356  options.print_names_before_values = false;
357  timer->report(out, comm, options);
358 
359  //Testing limited number of levels in printing
360  out << "\n### Printing with max_levels=2 ###" << std::endl;
361  options.max_levels=2;
362  options.align_columns = true;
363  options.print_names_before_values = true;
364  timer->report(out, comm, options);
365 }
366 
367 // Overlapping timers are not allowed in a StackedTimer, but are in
368 // TimeMonitor. Since StackedTimer is automatically used in
369 // TimeMonitor by default, we have seen this error - a throw from the
370 // stacked timer. In every instance so far, the intention was not to
371 // actually overlap but a constructor/destructor ordering issue
372 // (usually involving RCPs). To prevent tests from failing,
373 // StackedTimer now automatically shuts itself off if it detects
374 // overlaped timers in a TimeMonitor instance, reports a warning on
375 // how to fix and allows the code to continue runnning. Where this has
376 // occurred in Trilinos is when a TimeMonitor object is stored in an
377 // RCP and then the RCP is reassigned to a new timer. The intention
378 // was to stop one and start another. But the destruction of one and
379 // the creation of the new one occurs in the wrong order. This test
380 // demonstrates the issue.
381 TEUCHOS_UNIT_TEST(StackedTimer, OverlappingTimersException)
382 {
383  Teuchos::StackedTimer timer("My Timer");
384  timer.start("Outer");
385  timer.start("Inner");
386  // Should stop inner before outer
387  TEST_THROW(timer.stop("Outer"),std::runtime_error);
388  timer.stop("Inner");
389  timer.stop("Outer");
390  timer.stopBaseTimer();
391 }
392 
393 
394 #ifdef HAVE_TEUCHOS_ADD_TIME_MONITOR_TO_STACKED_TIMER
395 TEUCHOS_UNIT_TEST(StackedTimer, OverlappingTimersViaRCP)
396 {
397  const auto precTimer = Teuchos::TimeMonitor::getNewTimer("Prec");
398  const auto gmresTimer = Teuchos::TimeMonitor::getNewTimer("GMRES");
399 
401  timer = Teuchos::rcp(new Teuchos::TimeMonitor(*gmresTimer));
402 
404 }
405 #endif
406 
407 // Use our own main to initialize kokkos before calling
408 // runUnitTestsFromMain(). The kokkos space_time_stack profiler seg
409 // faults due to inconsistent push/pop of timers in the teuchos unit
410 // test startup code. By calling initialize here we can use the
411 // space_time_stack profiler with this unit test.
412 int main( int argc, char* argv[] )
413 {
414  // Note that the dtor for GlobalMPISession will call
415  // Kokkos::finalize_all().
416  Teuchos::GlobalMPISession mpiSession(&argc, &argv);
417 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOSCORE)
418  Kokkos::initialize(argc,argv);
419 #endif
420  {
421  Teuchos::FancyOStream out(Teuchos::rcpFromRef(std::cout));
422  out.setOutputToRootOnly(0);
423  }
425 
426  auto return_val = Teuchos::UnitTestRepository::runUnitTestsFromMain(argc, argv);
427 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOSCORE)
428  if (Kokkos::is_initialized())
429  Kokkos::finalize_all();
430 #endif
431  return return_val;
432 }
void setVerboseOstream(const Teuchos::RCP< std::ostream > &os)
static Teuchos::RCP< Teuchos::StackedTimer > getStackedTimer()
The StackedTimer used by the TimeMonitor.
#define TEST_ASSERT(v1)
Assert the given statement is true.
void stop(const std::string &name, const bool pop_kokkos_profiling_region=true)
void enableVerbose(const bool enable_verbose)
#define TEST_EQUALITY(v1, v2)
Assert the equality of v1 and v2.
#define TEST_THROW(code, ExceptType)
Assert that the statement &#39;code&#39; throws the exception &#39;ExceptType&#39; (otherwise the test fails)...
#define TEUCHOS_UNIT_TEST(TEST_GROUP, TEST_NAME)
Macro for defining a (non-templated) unit test.
static Teuchos::RCP< const Comm< OrdinalType > > getComm()
Return the default global communicator.
Initialize, finalize, and query the global MPI session.
bool is_null(const ArrayRCP< T > &p)
Returns true if p.get()==NULL.
static RCP< Time > getNewTimer(const std::string &name)
Return a new timer with the given name (class method).
Unit testing support.
static int runUnitTestsFromMain(int argc, char *argv[])
Run the unit tests from main() passing in (argc, argv).
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Deprecated.
void start(const std::string name, const bool push_kokkos_profiling_region=true)
std::ostream subclass that performs the magic of indenting data sent to an std::ostream object among ...
the basic timer used elsewhere, uses MPI_Wtime for time
Unit testing support.
static void setStackedTimer(const Teuchos::RCP< Teuchos::StackedTimer > &t)
Sets the StackedTimer into which the TimeMonitor will insert timings.
static void setGloballyReduceTestResult(const bool globallyReduceUnitTestResult)
Set if the unit tests should reduce pass/fail across processes.
basic_FancyOStream & setOutputToRootOnly(const int rootRank)
Set the stream to print only on the (MPI) process with the given rank.
int size(const Comm< Ordinal > &comm)
Get the number of processes in the communicator.
void unsortedMergePair(const Array< std::string > &localNames, Array< std::string > &globalNames, const ECounterSetOp setOp)
#define TEST_FLOATING_EQUALITY(v1, v2, tol)
Assert the relative floating-point equality of rel_error(v1,v2) &lt;= tol.
int main(int argc, char *argv[])
void push_back(const value_type &x)
A MPI utilities class, providing methods for initializing, finalizing, and querying the global MPI se...
void report(std::ostream &os)
size_type size() const
Scope guard for Teuchos::Time, with MPI collective timer reporting.
Smart reference counting pointer class for automatic garbage collection.
BaseTimer::TimeInfo findTimer(const std::string &name)
This class allows one to push and pop timers on and off a stack.
Common capabilities for collecting and reporting performance data collectively across MPI processes...
Struct for controlling output options like histograms.
Scope guard for Time, that can compute MPI collective timer statistics.
bool nonnull(const ArrayRCP< T > &p)
Returns true if p.get()!=NULL.