Intel® OpenMP* Runtime Library
 All Classes Functions Variables Typedefs Enumerations Enumerator Modules Pages
kmp_stats.cpp
1 
5 /* <copyright>
6  Copyright (c) 1997-2014 Intel Corporation. All Rights Reserved.
7 
8  Redistribution and use in source and binary forms, with or without
9  modification, are permitted provided that the following conditions
10  are met:
11 
12  * Redistributions of source code must retain the above copyright
13  notice, this list of conditions and the following disclaimer.
14  * Redistributions in binary form must reproduce the above copyright
15  notice, this list of conditions and the following disclaimer in the
16  documentation and/or other materials provided with the distribution.
17  * Neither the name of Intel Corporation nor the names of its
18  contributors may be used to endorse or promote products derived
19  from this software without specific prior written permission.
20 
21  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 
33 </copyright> */
34 
35 #if KMP_STATS_ENABLED
36 
37 #include "kmp.h"
38 #include "kmp_str.h"
39 #include "kmp_lock.h"
40 #include "kmp_stats.h"
41 
42 #include <algorithm>
43 #include <sstream>
44 #include <iomanip>
45 #include <stdlib.h> // for atexit
46 
47 #define STRINGIZE2(x) #x
48 #define STRINGIZE(x) STRINGIZE2(x)
49 
50 #define expandName(name,flags,ignore) {STRINGIZE(name),flags},
51 statInfo timeStat::timerInfo[] = {
52  KMP_FOREACH_TIMER(expandName,0)
53  {0,0}
54 };
55 const statInfo counter::counterInfo[] = {
56  KMP_FOREACH_COUNTER(expandName,0)
57  {0,0}
58 };
59 #undef expandName
60 
61 #define expandName(ignore1,ignore2,ignore3) {0.0,0.0,0.0},
62 kmp_stats_output_module::rgb_color kmp_stats_output_module::timerColorInfo[] = {
63  KMP_FOREACH_TIMER(expandName,0)
64  {0.0,0.0,0.0}
65 };
66 #undef expandName
67 
68 const kmp_stats_output_module::rgb_color kmp_stats_output_module::globalColorArray[] = {
69  {1.0, 0.0, 0.0}, // red
70  {1.0, 0.6, 0.0}, // orange
71  {1.0, 1.0, 0.0}, // yellow
72  {0.0, 1.0, 0.0}, // green
73  {0.0, 0.0, 1.0}, // blue
74  {0.6, 0.2, 0.8}, // purple
75  {1.0, 0.0, 1.0}, // magenta
76  {0.0, 0.4, 0.2}, // dark green
77  {1.0, 1.0, 0.6}, // light yellow
78  {0.6, 0.4, 0.6}, // dirty purple
79  {0.0, 1.0, 1.0}, // cyan
80  {1.0, 0.4, 0.8}, // pink
81  {0.5, 0.5, 0.5}, // grey
82  {0.8, 0.7, 0.5}, // brown
83  {0.6, 0.6, 1.0}, // light blue
84  {1.0, 0.7, 0.5}, // peach
85  {0.8, 0.5, 1.0}, // lavender
86  {0.6, 0.0, 0.0}, // dark red
87  {0.7, 0.6, 0.0}, // gold
88  {0.0, 0.0, 0.0} // black
89 };
90 
91 // Ensure that the atexit handler only runs once.
92 static uint32_t statsPrinted = 0;
93 
94 // output interface
95 static kmp_stats_output_module __kmp_stats_global_output;
96 
97 /* ****************************************************** */
98 /* ************* statistic member functions ************* */
99 
100 void statistic::addSample(double sample)
101 {
102  double delta = sample - meanVal;
103 
104  sampleCount = sampleCount + 1;
105  meanVal = meanVal + delta/sampleCount;
106  m2 = m2 + delta*(sample - meanVal);
107 
108  minVal = std::min(minVal, sample);
109  maxVal = std::max(maxVal, sample);
110 }
111 
112 statistic & statistic::operator+= (const statistic & other)
113 {
114  if (sampleCount == 0)
115  {
116  *this = other;
117  return *this;
118  }
119 
120  uint64_t newSampleCount = sampleCount + other.sampleCount;
121  double dnsc = double(newSampleCount);
122  double dsc = double(sampleCount);
123  double dscBydnsc = dsc/dnsc;
124  double dosc = double(other.sampleCount);
125  double delta = other.meanVal - meanVal;
126 
127  // Try to order these calculations to avoid overflows.
128  // If this were Fortran, then the compiler would not be able to re-order over brackets.
129  // In C++ it may be legal to do that (we certainly hope it doesn't, and CC+ Programming Language 2nd edition
130  // suggests it shouldn't, since it says that exploitation of associativity can only be made if the operation
131  // really is associative (which floating addition isn't...)).
132  meanVal = meanVal*dscBydnsc + other.meanVal*(1-dscBydnsc);
133  m2 = m2 + other.m2 + dscBydnsc*dosc*delta*delta;
134  minVal = std::min (minVal, other.minVal);
135  maxVal = std::max (maxVal, other.maxVal);
136  sampleCount = newSampleCount;
137 
138 
139  return *this;
140 }
141 
142 void statistic::scale(double factor)
143 {
144  minVal = minVal*factor;
145  maxVal = maxVal*factor;
146  meanVal= meanVal*factor;
147  m2 = m2*factor*factor;
148  return;
149 }
150 
151 std::string statistic::format(char unit, bool total) const
152 {
153  std::string result = formatSI(sampleCount,9,' ');
154 
155  result = result + std::string(", ") + formatSI(minVal, 9, unit);
156  result = result + std::string(", ") + formatSI(meanVal, 9, unit);
157  result = result + std::string(", ") + formatSI(maxVal, 9, unit);
158  if (total)
159  result = result + std::string(", ") + formatSI(meanVal*sampleCount, 9, unit);
160  result = result + std::string(", ") + formatSI(getSD(), 9, unit);
161 
162  return result;
163 }
164 
165 /* ********************************************************** */
166 /* ************* explicitTimer member functions ************* */
167 
168 void explicitTimer::start(timer_e timerEnumValue) {
169  startTime = tsc_tick_count::now();
170  if(timeStat::logEvent(timerEnumValue)) {
171  __kmp_stats_thread_ptr->incrementNestValue();
172  }
173  return;
174 }
175 
176 void explicitTimer::stop(timer_e timerEnumValue) {
177  if (startTime.getValue() == 0)
178  return;
179 
180  tsc_tick_count finishTime = tsc_tick_count::now();
181 
182  //stat->addSample ((tsc_tick_count::now() - startTime).ticks());
183  stat->addSample ((finishTime - startTime).ticks());
184 
185  if(timeStat::logEvent(timerEnumValue)) {
186  __kmp_stats_thread_ptr->push_event(startTime.getValue() - __kmp_stats_start_time.getValue(), finishTime.getValue() - __kmp_stats_start_time.getValue(), __kmp_stats_thread_ptr->getNestValue(), timerEnumValue);
187  __kmp_stats_thread_ptr->decrementNestValue();
188  }
189 
190  /* We accept the risk that we drop a sample because it really did start at t==0. */
191  startTime = 0;
192  return;
193 }
194 
195 /* ******************************************************************* */
196 /* ************* kmp_stats_event_vector member functions ************* */
197 
198 void kmp_stats_event_vector::deallocate() {
199  __kmp_free(events);
200  internal_size = 0;
201  allocated_size = 0;
202  events = NULL;
203 }
204 
205 // This function is for qsort() which requires the compare function to return
206 // either a negative number if event1 < event2, a positive number if event1 > event2
207 // or zero if event1 == event2.
208 // This sorts by start time (lowest to highest).
209 int compare_two_events(const void* event1, const void* event2) {
210  kmp_stats_event* ev1 = (kmp_stats_event*)event1;
211  kmp_stats_event* ev2 = (kmp_stats_event*)event2;
212 
213  if(ev1->getStart() < ev2->getStart()) return -1;
214  else if(ev1->getStart() > ev2->getStart()) return 1;
215  else return 0;
216 }
217 
218 void kmp_stats_event_vector::sort() {
219  qsort(events, internal_size, sizeof(kmp_stats_event), compare_two_events);
220 }
221 
222 /* *********************************************************** */
223 /* ************* kmp_stats_list member functions ************* */
224 
225 // returns a pointer to newly created stats node
226 kmp_stats_list* kmp_stats_list::push_back(int gtid) {
227  kmp_stats_list* newnode = (kmp_stats_list*)__kmp_allocate(sizeof(kmp_stats_list));
228  // placement new, only requires space and pointer and initializes (so __kmp_allocate instead of C++ new[] is used)
229  new (newnode) kmp_stats_list();
230  newnode->setGtid(gtid);
231  newnode->prev = this->prev;
232  newnode->next = this;
233  newnode->prev->next = newnode;
234  newnode->next->prev = newnode;
235  return newnode;
236 }
237 void kmp_stats_list::deallocate() {
238  kmp_stats_list* ptr = this->next;
239  kmp_stats_list* delptr = this->next;
240  while(ptr != this) {
241  delptr = ptr;
242  ptr=ptr->next;
243  // placement new means we have to explicitly call destructor.
244  delptr->_event_vector.deallocate();
245  delptr->~kmp_stats_list();
246  __kmp_free(delptr);
247  }
248 }
249 kmp_stats_list::iterator kmp_stats_list::begin() {
250  kmp_stats_list::iterator it;
251  it.ptr = this->next;
252  return it;
253 }
254 kmp_stats_list::iterator kmp_stats_list::end() {
255  kmp_stats_list::iterator it;
256  it.ptr = this;
257  return it;
258 }
259 int kmp_stats_list::size() {
260  int retval;
261  kmp_stats_list::iterator it;
262  for(retval=0, it=begin(); it!=end(); it++, retval++) {}
263  return retval;
264 }
265 
266 /* ********************************************************************* */
267 /* ************* kmp_stats_list::iterator member functions ************* */
268 
269 kmp_stats_list::iterator::iterator() : ptr(NULL) {}
270 kmp_stats_list::iterator::~iterator() {}
271 kmp_stats_list::iterator kmp_stats_list::iterator::operator++() {
272  this->ptr = this->ptr->next;
273  return *this;
274 }
275 kmp_stats_list::iterator kmp_stats_list::iterator::operator++(int dummy) {
276  this->ptr = this->ptr->next;
277  return *this;
278 }
279 kmp_stats_list::iterator kmp_stats_list::iterator::operator--() {
280  this->ptr = this->ptr->prev;
281  return *this;
282 }
283 kmp_stats_list::iterator kmp_stats_list::iterator::operator--(int dummy) {
284  this->ptr = this->ptr->prev;
285  return *this;
286 }
287 bool kmp_stats_list::iterator::operator!=(const kmp_stats_list::iterator & rhs) {
288  return this->ptr!=rhs.ptr;
289 }
290 bool kmp_stats_list::iterator::operator==(const kmp_stats_list::iterator & rhs) {
291  return this->ptr==rhs.ptr;
292 }
293 kmp_stats_list* kmp_stats_list::iterator::operator*() const {
294  return this->ptr;
295 }
296 
297 /* *************************************************************** */
298 /* ************* kmp_stats_output_module functions ************** */
299 
300 const char* kmp_stats_output_module::outputFileName = NULL;
301 const char* kmp_stats_output_module::eventsFileName = NULL;
302 const char* kmp_stats_output_module::plotFileName = NULL;
303 int kmp_stats_output_module::printPerThreadFlag = 0;
304 int kmp_stats_output_module::printPerThreadEventsFlag = 0;
305 
306 // init() is called very near the beginning of execution time in the constructor of __kmp_stats_global_output
307 void kmp_stats_output_module::init()
308 {
309  char * statsFileName = getenv("KMP_STATS_FILE");
310  eventsFileName = getenv("KMP_STATS_EVENTS_FILE");
311  plotFileName = getenv("KMP_STATS_PLOT_FILE");
312  char * threadStats = getenv("KMP_STATS_THREADS");
313  char * threadEvents = getenv("KMP_STATS_EVENTS");
314 
315  // set the stats output filenames based on environment variables and defaults
316  outputFileName = statsFileName;
317  eventsFileName = eventsFileName ? eventsFileName : "events.dat";
318  plotFileName = plotFileName ? plotFileName : "events.plt";
319 
320  // set the flags based on environment variables matching: true, on, 1, .true. , .t. , yes
321  printPerThreadFlag = __kmp_str_match_true(threadStats);
322  printPerThreadEventsFlag = __kmp_str_match_true(threadEvents);
323 
324  if(printPerThreadEventsFlag) {
325  // assigns a color to each timer for printing
326  setupEventColors();
327  } else {
328  // will clear flag so that no event will be logged
329  timeStat::clearEventFlags();
330  }
331 
332  return;
333 }
334 
335 void kmp_stats_output_module::setupEventColors() {
336  int i;
337  int globalColorIndex = 0;
338  int numGlobalColors = sizeof(globalColorArray) / sizeof(rgb_color);
339  for(i=0;i<TIMER_LAST;i++) {
340  if(timeStat::logEvent((timer_e)i)) {
341  timerColorInfo[i] = globalColorArray[globalColorIndex];
342  globalColorIndex = (globalColorIndex+1)%numGlobalColors;
343  }
344  }
345  return;
346 }
347 
348 void kmp_stats_output_module::printStats(FILE *statsOut, statistic const * theStats, bool areTimers)
349 {
350  if (areTimers)
351  {
352  // Check if we have useful timers, since we don't print zero value timers we need to avoid
353  // printing a header and then no data.
354  bool haveTimers = false;
355  for (int s = 0; s<TIMER_LAST; s++)
356  {
357  if (theStats[s].getCount() != 0)
358  {
359  haveTimers = true;
360  break;
361  }
362  }
363  if (!haveTimers)
364  return;
365  }
366 
367  // Print
368  const char * title = areTimers ? "Timer, SampleCount," : "Counter, ThreadCount,";
369  fprintf (statsOut, "%s Min, Mean, Max, Total, SD\n", title);
370  if (areTimers) {
371  for (int s = 0; s<TIMER_LAST; s++) {
372  statistic const * stat = &theStats[s];
373  if (stat->getCount() != 0) {
374  char tag = timeStat::noUnits(timer_e(s)) ? ' ' : 'T';
375  fprintf (statsOut, "%-25s, %s\n", timeStat::name(timer_e(s)), stat->format(tag, true).c_str());
376  }
377  }
378  } else { // Counters
379  for (int s = 0; s<COUNTER_LAST; s++) {
380  statistic const * stat = &theStats[s];
381  fprintf (statsOut, "%-25s, %s\n", counter::name(counter_e(s)), stat->format(' ', true).c_str());
382  }
383  }
384 }
385 
386 void kmp_stats_output_module::printCounters(FILE * statsOut, counter const * theCounters)
387 {
388  // We print all the counters even if they are zero.
389  // That makes it easier to slice them into a spreadsheet if you need to.
390  fprintf (statsOut, "\nCounter, Count\n");
391  for (int c = 0; c<COUNTER_LAST; c++) {
392  counter const * stat = &theCounters[c];
393  fprintf (statsOut, "%-25s, %s\n", counter::name(counter_e(c)), formatSI(stat->getValue(), 9, ' ').c_str());
394  }
395 }
396 
397 void kmp_stats_output_module::printEvents(FILE* eventsOut, kmp_stats_event_vector* theEvents, int gtid) {
398  // sort by start time before printing
399  theEvents->sort();
400  for (int i = 0; i < theEvents->size(); i++) {
401  kmp_stats_event ev = theEvents->at(i);
402  rgb_color color = getEventColor(ev.getTimerName());
403  fprintf(eventsOut, "%d %lu %lu %1.1f rgb(%1.1f,%1.1f,%1.1f) %s\n",
404  gtid,
405  ev.getStart(),
406  ev.getStop(),
407  1.2 - (ev.getNestLevel() * 0.2),
408  color.r, color.g, color.b,
409  timeStat::name(ev.getTimerName())
410  );
411  }
412  return;
413 }
414 
415 void kmp_stats_output_module::windupExplicitTimers()
416 {
417  // Wind up any explicit timers. We assume that it's fair at this point to just walk all the explcit timers in all threads
418  // and say "it's over".
419  // If the timer wasn't running, this won't record anything anyway.
420  kmp_stats_list::iterator it;
421  for(it = __kmp_stats_list.begin(); it != __kmp_stats_list.end(); it++) {
422  for (int timer=0; timer<EXPLICIT_TIMER_LAST; timer++) {
423  (*it)->getExplicitTimer(explicit_timer_e(timer))->stop((timer_e)timer);
424  }
425  }
426 }
427 
428 void kmp_stats_output_module::printPloticusFile() {
429  int i;
430  int size = __kmp_stats_list.size();
431  FILE* plotOut = fopen(plotFileName, "w+");
432 
433  fprintf(plotOut, "#proc page\n"
434  " pagesize: 15 10\n"
435  " scale: 1.0\n\n");
436 
437  fprintf(plotOut, "#proc getdata\n"
438  " file: %s\n\n",
439  eventsFileName);
440 
441  fprintf(plotOut, "#proc areadef\n"
442  " title: OpenMP Sampling Timeline\n"
443  " titledetails: align=center size=16\n"
444  " rectangle: 1 1 13 9\n"
445  " xautorange: datafield=2,3\n"
446  " yautorange: -1 %d\n\n",
447  size);
448 
449  fprintf(plotOut, "#proc xaxis\n"
450  " stubs: inc\n"
451  " stubdetails: size=12\n"
452  " label: Time (ticks)\n"
453  " labeldetails: size=14\n\n");
454 
455  fprintf(plotOut, "#proc yaxis\n"
456  " stubs: inc 1\n"
457  " stubrange: 0 %d\n"
458  " stubdetails: size=12\n"
459  " label: Thread #\n"
460  " labeldetails: size=14\n\n",
461  size-1);
462 
463  fprintf(plotOut, "#proc bars\n"
464  " exactcolorfield: 5\n"
465  " axis: x\n"
466  " locfield: 1\n"
467  " segmentfields: 2 3\n"
468  " barwidthfield: 4\n\n");
469 
470  // create legend entries corresponding to the timer color
471  for(i=0;i<TIMER_LAST;i++) {
472  if(timeStat::logEvent((timer_e)i)) {
473  rgb_color c = getEventColor((timer_e)i);
474  fprintf(plotOut, "#proc legendentry\n"
475  " sampletype: color\n"
476  " label: %s\n"
477  " details: rgb(%1.1f,%1.1f,%1.1f)\n\n",
478  timeStat::name((timer_e)i),
479  c.r, c.g, c.b);
480 
481  }
482  }
483 
484  fprintf(plotOut, "#proc legend\n"
485  " format: down\n"
486  " location: max max\n\n");
487  fclose(plotOut);
488  return;
489 }
490 
491 void kmp_stats_output_module::outputStats(const char* heading)
492 {
493  statistic allStats[TIMER_LAST];
494  statistic allCounters[COUNTER_LAST];
495 
496  // stop all the explicit timers for all threads
497  windupExplicitTimers();
498 
499  FILE * eventsOut;
500  FILE * statsOut = outputFileName ? fopen (outputFileName, "a+") : stderr;
501 
502  if (eventPrintingEnabled()) {
503  eventsOut = fopen(eventsFileName, "w+");
504  }
505 
506  if (!statsOut)
507  statsOut = stderr;
508 
509  fprintf(statsOut, "%s\n",heading);
510  // Accumulate across threads.
511  kmp_stats_list::iterator it;
512  for (it = __kmp_stats_list.begin(); it != __kmp_stats_list.end(); it++) {
513  int t = (*it)->getGtid();
514  // Output per thread stats if requested.
515  if (perThreadPrintingEnabled()) {
516  fprintf (statsOut, "Thread %d\n", t);
517  printStats(statsOut, (*it)->getTimers(), true);
518  printCounters(statsOut, (*it)->getCounters());
519  fprintf(statsOut,"\n");
520  }
521  // Output per thread events if requested.
522  if (eventPrintingEnabled()) {
523  kmp_stats_event_vector events = (*it)->getEventVector();
524  printEvents(eventsOut, &events, t);
525  }
526 
527  for (int s = 0; s<TIMER_LAST; s++) {
528  // See if we should ignore this timer when aggregating
529  if ((timeStat::masterOnly(timer_e(s)) && (t != 0)) || // Timer is only valid on the master and this thread is a worker
530  (timeStat::workerOnly(timer_e(s)) && (t == 0)) || // Timer is only valid on a worker and this thread is the master
531  timeStat::synthesized(timer_e(s)) // It's a synthesized stat, so there's no raw data for it.
532  )
533  {
534  continue;
535  }
536 
537  statistic * threadStat = (*it)->getTimer(timer_e(s));
538  allStats[s] += *threadStat;
539  }
540 
541  // Special handling for synthesized statistics.
542  // These just have to be coded specially here for now.
543  // At present we only have one: the total parallel work done in each thread.
544  // The variance here makes it easy to see load imbalance over the whole program (though, of course,
545  // it's possible to have a code with awful load balance in every parallel region but perfect load
546  // balance oever the whole program.)
547  allStats[TIMER_Total_work].addSample ((*it)->getTimer(TIMER_OMP_work)->getTotal());
548 
549  // Time waiting for work (synthesized)
550  if ((t != 0) || !timeStat::workerOnly(timer_e(TIMER_OMP_await_work)))
551  allStats[TIMER_Total_await_work].addSample ((*it)->getTimer(TIMER_OMP_await_work)->getTotal());
552 
553  // Time in explicit barriers.
554  allStats[TIMER_Total_barrier].addSample ((*it)->getTimer(TIMER_OMP_barrier)->getTotal());
555 
556  for (int c = 0; c<COUNTER_LAST; c++) {
557  if (counter::masterOnly(counter_e(c)) && t != 0)
558  continue;
559  allCounters[c].addSample ((*it)->getCounter(counter_e(c))->getValue());
560  }
561  }
562 
563  if (eventPrintingEnabled()) {
564  printPloticusFile();
565  fclose(eventsOut);
566  }
567 
568  fprintf (statsOut, "Aggregate for all threads\n");
569  printStats (statsOut, &allStats[0], true);
570  fprintf (statsOut, "\n");
571  printStats (statsOut, &allCounters[0], false);
572 
573  if (statsOut != stderr)
574  fclose(statsOut);
575 
576 }
577 
578 /* ************************************************** */
579 /* ************* exported C functions ************** */
580 
581 // no name mangling for these functions, we want the c files to be able to get at these functions
582 extern "C" {
583 
584 void __kmp_reset_stats()
585 {
586  kmp_stats_list::iterator it;
587  for(it = __kmp_stats_list.begin(); it != __kmp_stats_list.end(); it++) {
588  timeStat * timers = (*it)->getTimers();
589  counter * counters = (*it)->getCounters();
590  explicitTimer * eTimers = (*it)->getExplicitTimers();
591 
592  for (int t = 0; t<TIMER_LAST; t++)
593  timers[t].reset();
594 
595  for (int c = 0; c<COUNTER_LAST; c++)
596  counters[c].reset();
597 
598  for (int t=0; t<EXPLICIT_TIMER_LAST; t++)
599  eTimers[t].reset();
600 
601  // reset the event vector so all previous events are "erased"
602  (*it)->resetEventVector();
603 
604  // May need to restart the explicit timers in thread zero?
605  }
606  KMP_START_EXPLICIT_TIMER(OMP_serial);
607  KMP_START_EXPLICIT_TIMER(OMP_start_end);
608 }
609 
610 // This function will reset all stats and stop all threads' explicit timers if they haven't been stopped already.
611 void __kmp_output_stats(const char * heading)
612 {
613  __kmp_stats_global_output.outputStats(heading);
614  __kmp_reset_stats();
615 }
616 
617 void __kmp_accumulate_stats_at_exit(void)
618 {
619  // Only do this once.
620  if (KMP_XCHG_FIXED32(&statsPrinted, 1) != 0)
621  return;
622 
623  __kmp_output_stats("Statistics on exit");
624  return;
625 }
626 
627 void __kmp_stats_init(void)
628 {
629  return;
630 }
631 
632 } // extern "C"
633 
634 #endif // KMP_STATS_ENABLED
#define KMP_FOREACH_TIMER(macro, arg)
Add new timers under KMP_FOREACH_TIMER() macro in kmp_stats.h.
Definition: kmp_stats.h:115
#define KMP_START_EXPLICIT_TIMER(name)
"Starts" an explicit timer which will need a corresponding KMP_STOP_EXPLICIT_TIMER() macro...
Definition: kmp_stats.h:668
#define KMP_FOREACH_COUNTER(macro, arg)
Add new counters under KMP_FOREACH_COUNTER() macro in kmp_stats.h.
Definition: kmp_stats.h:84