Flow123d  JS_before_hm-896-g486f41f
sys_profiler.cc
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2015 Technical University of Liberec. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License version 3 as published by the
7  * Free Software Foundation. (http://www.gnu.org/licenses/gpl-3.0.en.html)
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12  *
13  *
14  * @file sys_profiler.cc
15  * @ingroup system
16  * @brief Profiler
17  */
18 
19 
20 // Fat header
21 
22 #include <fstream>
23 #include <iomanip>
24 #include <sys/param.h>
25 
26 #ifdef FLOW123D_HAVE_PYTHON
27  #include "Python.h"
28 #endif // FLOW123D_HAVE_PYTHON
29 
30 #include "sys_profiler.hh"
31 #include "system/system.hh"
32 #include "system/python_loader.hh"
33 #include <iostream>
34 #include <boost/format.hpp>
35 #include <boost/unordered_map.hpp>
36 
37 #include "system/file_path.hh"
38 #include "system/python_loader.hh"
39 #include "mpi.h"
40 #include "time_point.hh"
41 
42 /*
43  * These should be replaced by using boost MPI interface
44  */
45 int MPI_Functions::sum(int* val, MPI_Comm comm) {
46  int total = 0;
47  MPI_Reduce(val, &total, 1, MPI_INT, MPI_SUM, 0, comm);
48  return total;
49  }
50 
51 double MPI_Functions::sum(double* val, MPI_Comm comm) {
52  double total = 0;
53  MPI_Reduce(val, &total, 1, MPI_DOUBLE, MPI_SUM, 0, comm);
54  return total;
55  }
56 
57 long MPI_Functions::sum(long* val, MPI_Comm comm) {
58  long total = 0;
59  MPI_Reduce(val, &total, 1, MPI_LONG, MPI_SUM, 0, comm);
60  return total;
61  }
62 
63 int MPI_Functions::min(int* val, MPI_Comm comm) {
64  int min = 0;
65  MPI_Reduce(val, &min, 1, MPI_INT, MPI_MIN, 0, comm);
66  return min;
67  }
68 
69 double MPI_Functions::min(double* val, MPI_Comm comm) {
70  double min = 0;
71  MPI_Reduce(val, &min, 1, MPI_DOUBLE, MPI_MIN, 0, comm);
72  return min;
73  }
74 
75 long MPI_Functions::min(long* val, MPI_Comm comm) {
76  long min = 0;
77  MPI_Reduce(val, &min, 1, MPI_LONG, MPI_MIN, 0, comm);
78  return min;
79  }
80 
81 int MPI_Functions::max(int* val, MPI_Comm comm) {
82  int max = 0;
83  MPI_Reduce(val, &max, 1, MPI_INT, MPI_MAX, 0, comm);
84  return max;
85  }
86 
87 double MPI_Functions::max(double* val, MPI_Comm comm) {
88  double max = 0;
89  MPI_Reduce(val, &max, 1, MPI_DOUBLE, MPI_MAX, 0, comm);
90  return max;
91  }
92 
93 long MPI_Functions::max(long* val, MPI_Comm comm) {
94  long max = 0;
95  MPI_Reduce(val, &max, 1, MPI_LONG, MPI_MAX, 0, comm);
96  return max;
97  }
98 
99 
100 #ifdef FLOW123D_DEBUG_PROFILER
101 /*********************************************************************************************
102  * Implementation of class Timer
103  */
104 
105 const int timer_no_child=-1;
106 
107 Timer::Timer(const CodePoint &cp, int parent)
108 : start_time(TimePoint()),
109  cumul_time(0.0),
110  call_count(0),
111  start_count(0),
112  code_point_(&cp),
113  full_hash_(cp.hash_),
114  hash_idx_(cp.hash_idx_),
115  parent_timer(parent),
116  total_allocated_(0),
117  total_deallocated_(0),
118  max_allocated_(0),
119  current_allocated_(0),
120  alloc_called(0),
121  dealloc_called(0)
122 #ifdef FLOW123D_HAVE_PETSC
123 , petsc_start_memory(0),
124  petsc_end_memory (0),
125  petsc_memory_difference(0),
126  petsc_peak_memory(0),
127  petsc_local_peak_memory(0)
128 #endif // FLOW123D_HAVE_PETSC
129 {
130  for(unsigned int i=0; i< max_n_childs ;i++) child_timers[i]=timer_no_child;
131 }
132 
133 
134 /**
135  * Debug information of the timer
136  */
137 ostream & operator <<(ostream& os, const Timer& timer) {
138  os << " Timer: " << timer.tag() << endl;
139  os << " malloc: " << timer.total_allocated_ << endl;
140  os << " dalloc: " << timer.total_deallocated_ << endl;
141  #ifdef FLOW123D_HAVE_PETSC
142  os << " start: " << timer.petsc_start_memory << endl;
143  os << " stop : " << timer.petsc_end_memory << endl;
144  os << " diff : " << timer.petsc_memory_difference << " (" << timer.petsc_end_memory - timer.petsc_start_memory << ")" << endl;
145  os << " peak : " << timer.petsc_peak_memory << " (" << timer.petsc_local_peak_memory << ")" << endl;
146  #endif // FLOW123D_HAVE_PETSC
147  os << endl;
148  return os;
149 }
150 
151 
152 double Timer::cumulative_time() const {
153  return cumul_time;
154 }
155 
156 void Profiler::accept_from_child(Timer &parent, Timer &child) {
157  int child_timer = 0;
158  for (unsigned int i = 0; i < Timer::max_n_childs; i++) {
159  child_timer = child.child_timers[i];
160  if (child_timer != timer_no_child) {
161  // propagate metrics from child to parent
162  accept_from_child(child, timers_[child_timer]);
163  }
164  }
165  // compute totals by adding values from child
166  parent.total_allocated_ += child.total_allocated_;
167  parent.total_deallocated_ += child.total_deallocated_;
168  parent.alloc_called += child.alloc_called;
169  parent.dealloc_called += child.dealloc_called;
170 
171 #ifdef FLOW123D_HAVE_PETSC
172  if (petsc_monitor_memory) {
173  // add differences from child
174  parent.petsc_memory_difference += child.petsc_memory_difference;
175  parent.current_allocated_ += child.current_allocated_;
176 
177  // when computing maximum, we take greater value from parent and child
178  // PetscMemoryGetCurrentUsage always return absolute (not relative) value
179  parent.petsc_peak_memory = max(parent.petsc_peak_memory, child.petsc_peak_memory);
180  }
181 #endif // FLOW123D_HAVE_PETSC
182 
183  parent.max_allocated_ = max(parent.max_allocated_, child.max_allocated_);
184 }
185 
186 
187 void Timer::pause() {
188 #ifdef FLOW123D_HAVE_PETSC
189  if (Profiler::get_petsc_memory_monitoring()) {
190  // get the maximum resident set size (memory used) for the program.
191  PetscMemoryGetMaximumUsage(&petsc_local_peak_memory);
192  if (petsc_peak_memory < petsc_local_peak_memory)
193  petsc_peak_memory = petsc_local_peak_memory;
194  }
195 #endif // FLOW123D_HAVE_PETSC
196 }
197 
198 void Timer::resume() {
199 #ifdef FLOW123D_HAVE_PETSC
200  if (Profiler::get_petsc_memory_monitoring()) {
201  // tell PETSc to monitor the maximum memory usage so
202  // that PetscMemoryGetMaximumUsage() will work.
203  PetscMemorySetGetMaximumUsage();
204  }
205 #endif // FLOW123D_HAVE_PETSC
206 }
207 
208 void Timer::start() {
209 #ifdef FLOW123D_HAVE_PETSC
210  if (Profiler::get_petsc_memory_monitoring()) {
211  // Tell PETSc to monitor the maximum memory usage so
212  // that PetscMemoryGetMaximumUsage() will work.
213  PetscMemorySetGetMaximumUsage();
214  PetscMemoryGetCurrentUsage (&petsc_start_memory);
215  }
216 #endif // FLOW123D_HAVE_PETSC
217 
218  if (start_count == 0) {
219  start_time = TimePoint();
220  }
221  call_count++;
222  start_count++;
223 }
224 
225 
226 
227 bool Timer::stop(bool forced) {
228 #ifdef FLOW123D_HAVE_PETSC
229  if (Profiler::get_petsc_memory_monitoring()) {
230  // get current memory usage
231  PetscMemoryGetCurrentUsage (&petsc_end_memory);
232  petsc_memory_difference += petsc_end_memory - petsc_start_memory;
233 
234  // get the maximum resident set size (memory used) for the program.
235  PetscMemoryGetMaximumUsage(&petsc_local_peak_memory);
236  if (petsc_peak_memory < petsc_local_peak_memory)
237  petsc_peak_memory = petsc_local_peak_memory;
238  }
239 #endif // FLOW123D_HAVE_PETSC
240 
241  if (forced) start_count=1;
242 
243  if (start_count == 1) {
244  cumul_time += (TimePoint() - start_time);
245  start_count--;
246  return true;
247  } else {
248  start_count--;
249  }
250  return false;
251 }
252 
253 
254 
255 void Timer::add_child(int child_index, const Timer &child)
256 {
257  unsigned int idx = child.hash_idx_;
258  if (child_timers[idx] != timer_no_child) {
259  // hash collision, find first empty place
260  unsigned int i=idx;
261  do {
262  i=( i < max_n_childs ? i+1 : 0);
263  } while (i!=idx && child_timers[i] != timer_no_child);
264  ASSERT(i!=idx)(tag()).error("Too many children of the timer");
265  idx=i;
266  }
267  //DebugOut().fmt("Adding child {} at index: {}\n", child_index, idx);
268  child_timers[idx] = child_index;
269 }
270 
271 
272 
273 string Timer::code_point_str() const {
274  return boost::str( boost::format("%s:%d, %s()") % code_point_->file_ % code_point_->line_ % code_point_->func_ );
275 }
276 
277 
278 /***********************************************************************************************
279  * Implementation of Profiler
280  */
281 
282 
283 Profiler * Profiler::instance(bool clear) {
284  static Profiler * _instance = NULL;
285 
286  if (clear) {
287  if (_instance != NULL) {
288  delete _instance;
289  _instance = NULL;
290  }
291  return _instance;
292  }
293 
294  if (_instance == NULL) {
295  MemoryAlloc::malloc_map().reserve(Profiler::malloc_map_reserve);
296  _instance = new Profiler();
297  }
298 
299  return _instance;
300  }
301 
302 
303 // static CONSTEXPR_ CodePoint main_cp = CODE_POINT("Whole Program");
304 const long Profiler::malloc_map_reserve = 100 * 1000;
305 
307 : actual_node(0),
308  task_size_(1),
309  start_time( time(NULL) ),
310  json_filepath("")
311 
312 {
313  static CONSTEXPR_ CodePoint main_cp = CODE_POINT("Whole Program");
314  set_memory_monitoring(true, true);
315 #ifdef FLOW123D_DEBUG_PROFILER
316  MemoryAlloc::malloc_map().reserve(Profiler::malloc_map_reserve);
317  timers_.push_back( Timer(main_cp, 0) );
318  timers_[0].start();
319 #endif
320 }
321 
322 
323 
324 void Profiler::propagate_timers() {
325  for (unsigned int i = 0; i < Timer::max_n_childs; i++) {
326  unsigned int child_timer = timers_[0].child_timers[i];
327  if ((signed int)child_timer != timer_no_child) {
328  // propagate metrics from child to Whole-Program time-frame
329  accept_from_child(timers_[0], timers_[child_timer]);
330  }
331  }
332 }
333 
334 
335 
336 void Profiler::set_task_info(string description, int size) {
337  task_description_ = description;
338  task_size_ = size;
339 }
340 
341 
342 
343 void Profiler::set_program_info(string program_name, string program_version, string branch, string revision, string build) {
344  flow_name_ = program_name;
345  flow_version_ = program_version;
346  flow_branch_ = branch;
347  flow_revision_ = revision;
348  flow_build_ = build;
349 }
350 
351 
352 
353 int Profiler::start_timer(const CodePoint &cp) {
354  unsigned int parent_node = actual_node;
355  //DebugOut().fmt("Start timer: {}\n", cp.tag_);
356  int child_idx = find_child(cp);
357  if (child_idx < 0) {
358  //DebugOut().fmt("Adding timer: {}\n", cp.tag_);
359  // tag not present - create new timer
360  child_idx=timers_.size();
361  timers_.push_back( Timer(cp, actual_node) );
362  timers_[actual_node].add_child(child_idx , timers_.back() );
363  }
364  actual_node=child_idx;
365 
366  // pause current timer
367  timers_[parent_node].pause();
368 
369  timers_[actual_node].start();
370 
371  return actual_node;
372 }
373 
374 
375 
376 int Profiler::find_child(const CodePoint &cp) {
377  Timer &timer =timers_[actual_node];
378  unsigned int idx = cp.hash_idx_;
379  unsigned int child_idx;
380  do {
381  if (timer.child_timers[idx] == timer_no_child) break; // tag is not there
382 
383  child_idx=timer.child_timers[idx];
384  ASSERT_LT(child_idx, timers_.size()).error();
385  if (timers_[child_idx].full_hash_ == cp.hash_) return child_idx;
386  idx = ( (unsigned int)(idx)==(Timer::max_n_childs - 1) ? 0 : idx+1 );
387  } while ( (unsigned int)(idx) != cp.hash_idx_ ); // passed through whole array
388  return -1;
389 }
390 
391 
392 
393 void Profiler::stop_timer(const CodePoint &cp) {
394 #ifdef FLOW123D_DEBUG
395  // check that all childrens are closed
396  Timer &timer=timers_[actual_node];
397  for(unsigned int i=0; i < Timer::max_n_childs; i++)
398  if (timer.child_timers[i] != timer_no_child)
399  ASSERT(! timers_[timer.child_timers[i]].running())(timers_[timer.child_timers[i]].tag())(timer.tag())
400  .error("Child timer running while closing timer.");
401 #endif
402  unsigned int child_timer = actual_node;
403  if ( cp.hash_ != timers_[actual_node].full_hash_) {
404  // timer to close is not actual - we search for it above actual
405  for(unsigned int node=actual_node; node != 0; node=timers_[node].parent_timer) {
406  if ( cp.hash_ == timers_[node].full_hash_) {
407  // found above - close all nodes between
408  for(; (unsigned int)(actual_node) != node; actual_node=timers_[actual_node].parent_timer) {
409  WarningOut() << "Timer to close '" << cp.tag_ << "' do not match actual timer '"
410  << timers_[actual_node].tag() << "'. Force closing actual." << std::endl;
411  timers_[actual_node].stop(true);
412  }
413  // close 'node' itself
414  timers_[actual_node].stop(false);
415  actual_node = timers_[actual_node].parent_timer;
416 
417  // actual_node == child_timer indicates this is root
418  if (actual_node == child_timer)
419  return;
420 
421  // resume current timer
422  timers_[actual_node].resume();
423  return;
424  }
425  }
426  // node not found - do nothing
427  return;
428  }
429  // node to close match the actual
430  timers_[actual_node].stop(false);
431  actual_node = timers_[actual_node].parent_timer;
432 
433  // actual_node == child_timer indicates this is root
434  if (actual_node == child_timer)
435  return;
436 
437  // resume current timer
438  timers_[actual_node].resume();
439 }
440 
441 
442 
443 void Profiler::stop_timer(int timer_index) {
444  // stop_timer with CodePoint type
445  // timer which is still running MUST be the same as actual_node index
446  // if timer is not running index will differ
447  if (timers_[timer_index].running()) {
448  ASSERT_EQ(timer_index, (int)actual_node).error();
449  stop_timer(*timers_[timer_index].code_point_);
450  }
451 
452 }
453 
454 
455 
456 void Profiler::add_calls(unsigned int n_calls) {
457  timers_[actual_node].call_count += n_calls-1;
458 }
459 
460 
461 
462 void Profiler::notify_malloc(const size_t size, const long p) {
463  if (!global_monitor_memory)
464  return;
465 
466  MemoryAlloc::malloc_map()[p] = static_cast<int>(size);
467  timers_[actual_node].total_allocated_ += size;
468  timers_[actual_node].current_allocated_ += size;
469  timers_[actual_node].alloc_called++;
470 
471  if (timers_[actual_node].current_allocated_ > timers_[actual_node].max_allocated_)
472  timers_[actual_node].max_allocated_ = timers_[actual_node].current_allocated_;
473 }
474 
475 
476 
477 void Profiler::notify_free(const long p) {
478  if (!global_monitor_memory)
479  return;
480 
481  int size = sizeof(p);
482  if (MemoryAlloc::malloc_map()[(long)p] > 0) {
483  size = MemoryAlloc::malloc_map()[(long)p];
484  MemoryAlloc::malloc_map().erase((long)p);
485  }
486  timers_[actual_node].total_deallocated_ += size;
487  timers_[actual_node].current_allocated_ -= size;
488  timers_[actual_node].dealloc_called++;
489 }
490 
491 
492 double Profiler::get_resolution () {
493  const int measurements = 100;
494  double result = 0;
495 
496  // perform 100 measurements
497  for (unsigned int i = 1; i < measurements; i++) {
498  TimePoint t1 = TimePoint ();
499  TimePoint t2 = TimePoint ();
500 
501  // double comparison should be avoided
502  while ((t2 - t1) == 0) t2 = TimePoint ();
503  // while ((t2.ticks - t1.ticks) == 0) t2 = TimePoint ();
504 
505  result += t2 - t1;
506  }
507 
508  return (result / measurements) * 1000; // ticks to seconds to microseconds conversion
509 }
510 
511 
512 std::string common_prefix( std::string a, std::string b ) {
513  if( a.size() > b.size() ) std::swap(a,b) ;
514  return std::string( a.begin(), std::mismatch( a.begin(), a.end(), b.begin() ).first ) ;
515 }
516 
517 
518 
519 template<typename ReduceFunctor>
520 void Profiler::add_timer_info(ReduceFunctor reduce, nlohmann::json* holder, int timer_idx, double parent_time) {
521 
522  // get timer and check preconditions
523  Timer &timer = timers_[timer_idx];
524  ASSERT(timer_idx >=0)(timer_idx).error("Wrong timer index.");
525  ASSERT(timer.parent_timer >=0).error("Inconsistent tree.");
526 
527  // fix path
528  string filepath = timer.code_point_->file_;
529 
530  // if constant FLOW123D_SOURCE_DIR is defined, we try to erase it from beginning of each CodePoint's filepath
531  #ifdef FLOW123D_SOURCE_DIR
532  string common_path = common_prefix (string(FLOW123D_SOURCE_DIR), filepath);
533  filepath.erase (0, common_path.size());
534  #endif
535 
536 
537  // generate node representing this timer
538  // add basic information
539  nlohmann::json node;
540  double cumul_time_sum;
541  node["tag"] = timer.tag();
542  node["file-path"] = filepath;
543  node["file-line"] = timer.code_point_->line_;
544  node["function"] = timer.code_point_->func_;
545  cumul_time_sum = reduce(timer, node);
546 
547 
548  // statistical info
549  if (timer_idx == 0) parent_time = cumul_time_sum;
550  double percent = parent_time > 1.0e-10 ? cumul_time_sum / parent_time * 100.0 : 0.0;
551  node["percent"] = percent;
552 
553  // write times children timers using secured child_timers array
554  nlohmann::json children;
555  bool has_children = false;
556  for (unsigned int i = 0; i < Timer::max_n_childs; i++) {
557  if (timer.child_timers[i] != timer_no_child) {
558  add_timer_info (reduce, &children, timer.child_timers[i], cumul_time_sum);
559  /*
560  if (child_timers[i] != timer_no_child) {
561  add_timer_info (reduce, &children, child_timers[i], cumul_time_sum); */
562  has_children = true;
563  }
564  }
565 
566  // add children tag and other info if present
567  if (has_children) {
568  node["children"] = children;
569  }
570 
571  // add to the array
572  holder->push_back(node);
573 }
574 
575 
576 template <class T>
577 void save_nonmpi_metric (nlohmann::json &node, T * ptr, string name) {
578  node[name + "-min"] = *ptr;
579  node[name + "-max"] = *ptr;
580  node[name + "-sum"] = *ptr;
581 }
582 
583 std::shared_ptr<std::ostream> Profiler::get_default_output_stream() {
584  json_filepath = FilePath("profiler_info.log.json", FilePath::output_file);
585 
586  //LogOut() << "output into: " << json_filepath << std::endl;
587  return make_shared<ofstream>(json_filepath.c_str());
588 }
589 
590 
591 #ifdef FLOW123D_HAVE_MPI
592 template <class T>
593 void save_mpi_metric (nlohmann::json &node, MPI_Comm comm, T * ptr, string name) {
594  node[name + "-min"] = MPI_Functions::min(ptr, comm);
595  node[name + "-max"] = MPI_Functions::max(ptr, comm);
596  node[name + "-sum"] = MPI_Functions::sum(ptr, comm);
597 }
598 
599 void Profiler::output(MPI_Comm comm, ostream &os) {
600  int mpi_rank, mpi_size;
601  //wait until profiling on all processors is finished
602  MPI_Barrier(comm);
603  stop_timer(0);
604  propagate_timers();
605 
606  // stop monitoring memory
607  bool temp_memory_monitoring = global_monitor_memory;
608  set_memory_monitoring(false, petsc_monitor_memory);
609 
610  chkerr( MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank) );
611  MPI_Comm_size(comm, &mpi_size);
612 
613  // output header
614  nlohmann::json jsonRoot, jsonChildren;
615 
616  // recursively add all timers info
617  // define lambda function which reduces timer from multiple processors
618  // MPI implementation uses MPI call to reduce values
619  auto reduce = [=] (Timer &timer, nlohmann::json &node) -> double {
620  int call_count = timer.call_count;
621  double cumul_time = timer.cumulative_time ();
622 
623  long memory_allocated = (long)timer.total_allocated_;
624  long memory_deallocated = (long)timer.total_deallocated_;
625  long memory_peak = (long)timer.max_allocated_;
626 
627  int alloc_called = timer.alloc_called;
628  int dealloc_called = timer.dealloc_called;
629 
630 
631  save_mpi_metric<double>(node, comm, &cumul_time, "cumul-time");
632  save_mpi_metric<int>(node, comm, &call_count, "call-count");
633 
634  save_mpi_metric<long>(node, comm, &memory_allocated, "memory-alloc");
635  save_mpi_metric<long>(node, comm, &memory_deallocated, "memory-dealloc");
636  save_mpi_metric<long>(node, comm, &memory_peak, "memory-peak");
637  //
638  save_mpi_metric<int>(node, comm, &alloc_called, "memory-alloc-called");
639  save_mpi_metric<int>(node, comm, &dealloc_called, "memory-dealloc-called");
640 
641 #ifdef FLOW123D_HAVE_PETSC
642  long petsc_memory_difference = (long)timer.petsc_memory_difference;
643  long petsc_peak_memory = (long)timer.petsc_peak_memory;
644  save_mpi_metric<long>(node, comm, &petsc_memory_difference, "memory-petsc-diff");
645  save_mpi_metric<long>(node, comm, &petsc_peak_memory, "memory-petsc-peak");
646 #endif // FLOW123D_HAVE_PETSC
647 
648  return MPI_Functions::sum(&cumul_time, comm);
649  };
650 
651  add_timer_info (reduce, &jsonChildren, 0, 0.0);
652  jsonRoot["children"] = jsonChildren;
653  output_header(jsonRoot, mpi_size);
654 
655 
656  // create profiler output only once (on the first processor)
657  // only active communicator should be the one with mpi_rank 0
658  if (mpi_rank == 0) {
659  try {
660  /**
661  * indent size
662  * results in json human readable format (indents, newlines)
663  */
664  const int FLOW123D_JSON_HUMAN_READABLE = 2;
665  // write result to stream
666  os << jsonRoot.dump(FLOW123D_JSON_HUMAN_READABLE) << endl;
667 
668  } catch (exception & e) {
669  stringstream ss;
670  ss << "nlohmann::json::dump error: " << e.what() << "\n";
671  THROW( ExcMessage() << EI_Message(ss.str()) );
672  }
673  }
674  // restore memory monitoring
675  set_memory_monitoring(temp_memory_monitoring, petsc_monitor_memory);
676 }
677 
678 
679 void Profiler::output(MPI_Comm comm, string profiler_path /* = "" */) {
680  int mpi_rank;
681  chkerr(MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank));
682 
683  if (mpi_rank == 0) {
684  if (profiler_path == "") {
685  output(comm, *get_default_output_stream());
686  } else {
687  json_filepath = profiler_path;
688  std::shared_ptr<std::ostream> os = make_shared<ofstream>(profiler_path.c_str());
689  output(comm, *os);
690  }
691  } else {
692  ostringstream os;
693  output(comm, os);
694  }
695 }
696 
697 #endif /* FLOW123D_HAVE_MPI */
698 
699 void Profiler::output(ostream &os) {
700  // last update
701  stop_timer(0);
702  propagate_timers();
703 
704  // output header
705  nlohmann::json jsonRoot, jsonChildren;
706  /**
707  * Constant representing number of MPI processes
708  * where there is no MPI to work with (so 1 process)
709  */
710  const int FLOW123D_MPI_SINGLE_PROCESS = 1;
711  output_header(jsonRoot, FLOW123D_MPI_SINGLE_PROCESS);
712 
713 
714  // recursively add all timers info
715  // define lambda function which reduces timer from multiple processors
716  // non-MPI implementation is just dummy repetition of initial value
717  auto reduce = [=] (Timer &timer, nlohmann::json &node) -> double {
718  int call_count = timer.call_count;
719  double cumul_time = timer.cumulative_time ();
720 
721  long memory_allocated = (long)timer.total_allocated_;
722  long memory_deallocated = (long)timer.total_deallocated_;
723  long memory_peak = (long)timer.max_allocated_;
724 
725  int alloc_called = timer.alloc_called;
726  int dealloc_called = timer.dealloc_called;
727 
728  save_nonmpi_metric<double>(node, &cumul_time, "cumul-time");
729  save_nonmpi_metric<int>(node, &call_count, "call-count");
730 
731  save_nonmpi_metric<long>(node, &memory_allocated, "memory-alloc");
732  save_nonmpi_metric<long>(node, &memory_deallocated, "memory-dealloc");
733  save_nonmpi_metric<long>(node, &memory_peak, "memory-peak");
734 
735  save_nonmpi_metric<int>(node, &alloc_called, "memory-alloc-called");
736  save_nonmpi_metric<int>(node, &dealloc_called, "memory-dealloc-called");
737 
738 #ifdef FLOW123D_HAVE_PETSC
739  long petsc_memory_difference = (long)timer.petsc_memory_difference;
740  long petsc_peak_memory = (long)timer.petsc_peak_memory;
741  save_nonmpi_metric<long>(node, &petsc_memory_difference, "memory-petsc-diff");
742  save_nonmpi_metric<long>(node, &petsc_peak_memory, "memory-petsc-peak");
743 #endif // FLOW123D_HAVE_PETSC
744 
745  return cumul_time;
746  };
747 
748  add_timer_info(reduce, &jsonChildren, 0, 0.0);
749  jsonRoot["children"] = jsonChildren;
750 
751  try {
752  /**
753  * indent size
754  * results in json human readable format (indents, newlines)
755  */
756  const int FLOW123D_JSON_HUMAN_READABLE = 2;
757  // write result to stream
758  os << jsonRoot.dump(FLOW123D_JSON_HUMAN_READABLE) << endl;
759 
760  } catch (exception & e) {
761  stringstream ss;
762  ss << "nlohmann::json::dump error: " << e.what() << "\n";
763  THROW( ExcMessage() << EI_Message(ss.str()) );
764  }
765 }
766 
767 
768 void Profiler::output(string profiler_path /* = "" */) {
769  if(profiler_path == "") {
770  output(*get_default_output_stream());
771  } else {
772  json_filepath = profiler_path;
773  std::shared_ptr<std::ostream> os = make_shared<ofstream>(profiler_path.c_str());
774  output(*os);
775  }
776 }
777 
778 void Profiler::output_header (nlohmann::json &root, int mpi_size) {
779  time_t end_time = time(NULL);
780 
781  const char format[] = "%x %X";
782  char start_time_string[BUFSIZ] = {0};
783  strftime(start_time_string, sizeof (start_time_string) - 1, format, localtime(&start_time));
784 
785  char end_time_string[BUFSIZ] = {0};
786  strftime(end_time_string, sizeof (end_time_string) - 1, format, localtime(&end_time));
787 
788  // generate current run details
789  root["program-name"] = flow_name_;
790  root["program-version"] = flow_version_;
791  root["program-branch"] = flow_branch_;
792  root["program-revision"] = flow_revision_;
793  root["program-build"] = flow_build_;
794  root["timer-resolution"] = Profiler::get_resolution();
795 
796  // print some information about the task at the beginning
797  root["task-description"] = task_description_;
798  root["task-size"] = task_size_;
799 
800  //print some information about the task at the beginning
801  root["run-process-count"] = mpi_size;
802  root["run-started-at"] = start_time_string;
803  root["run-finished-at"] = end_time_string;
804 }
805 
806 #ifdef FLOW123D_HAVE_PYTHON
807 void Profiler::transform_profiler_data (const string &output_file_suffix, const string &formatter) {
808 
809  if (json_filepath == "") return;
810 
811  // error under CYGWIN environment : more details in this repo
812  // https://github.com/x3mSpeedy/cygwin-python-issue
813  //
814  // For now we only support profiler report conversion in UNIX environment
815  // Windows users will have to use a python script located in bin folder
816  //
817 
818  #ifndef FLOW123D_HAVE_CYGWIN
819  // grab module and function by importing module profiler_formatter_module.py
820  PyObject * python_module = PythonLoader::load_module_by_name ("profiler.profiler_formatter_module");
821  //
822  // def convert (json_location, output_file, formatter):
823  //
824  PyObject * convert_method = PythonLoader::get_callable (python_module, "convert" );
825 
826  int argument_index = 0;
827  PyObject * arguments = PyTuple_New (3);
828 
829  // set json path location as first argument
830  PyObject * tmp = PyUnicode_FromString (json_filepath.c_str());
831  PyTuple_SetItem (arguments, argument_index++, tmp);
832 
833  // set output path location as second argument
834  tmp = PyUnicode_FromString ((json_filepath + output_file_suffix).c_str());
835  PyTuple_SetItem (arguments, argument_index++, tmp);
836 
837  // set Formatter class as third value
838  tmp = PyUnicode_FromString (formatter.c_str());
839  PyTuple_SetItem (arguments, argument_index++, tmp);
840 
841  // execute method with arguments
842  PyObject_CallObject (convert_method, arguments);
843 
844  PythonLoader::check_error();
845 
846  #else
847 
848  // print information about windows-cygwin issue and offer manual solution
849  MessageOut() << "# Note: converting json profiler reports is not"
850  << " supported under Windows or Cygwin environment for now.\n"
851  << "# You can use python script located in bin/python folder"
852  << " in order to convert json report to txt or csv format.\n"
853  << "python profiler_formatter_script.py --input \"" << json_filepath
854  << "\" --output \"profiler.txt\"" << std::endl;
855  #endif // FLOW123D_HAVE_CYGWIN
856 }
857 #else
858 void Profiler::transform_profiler_data (const string &output_file_suffix, const string &formatter) {
859 }
860 
861 #endif // FLOW123D_HAVE_PYTHON
862 
863 
864 void Profiler::uninitialize() {
865  if (Profiler::instance()) {
866  ASSERT(Profiler::instance()->actual_node==0)(Profiler::instance()->timers_[Profiler::instance()->actual_node].tag())
867  .error("Forbidden to uninitialize the Profiler when actual timer is not zero.");
868  Profiler::instance()->stop_timer(0);
869  set_memory_monitoring(false, false);
870  Profiler::instance(true);
871  }
872 }
873 bool Profiler::global_monitor_memory = false;
874 bool Profiler::petsc_monitor_memory = true;
875 void Profiler::set_memory_monitoring(const bool global_monitor, const bool petsc_monitor) {
876  global_monitor_memory = global_monitor;
877  petsc_monitor_memory = petsc_monitor;
878 }
879 
880 bool Profiler::get_global_memory_monitoring() {
881  return global_monitor_memory;
882 }
883 
884 bool Profiler::get_petsc_memory_monitoring() {
885  return petsc_monitor_memory;
886 }
887 
888 unordered_map_with_alloc & MemoryAlloc::malloc_map() {
889  static unordered_map_with_alloc static_malloc_map;
890  return static_malloc_map;
891 }
892 
893 void * Profiler::operator new (size_t size) {
894  return malloc (size);
895 }
896 
897 void Profiler::operator delete (void* p) {
898  free(p);
899 }
900 
901 void *operator new (std::size_t size) OPERATOR_NEW_THROW_EXCEPTION {
902  void * p = malloc(size);
903  Profiler::instance()->notify_malloc(size, (long)p);
904  return p;
905 }
906 
907 void *operator new[] (std::size_t size) OPERATOR_NEW_THROW_EXCEPTION {
908  void * p = malloc(size);
909  Profiler::instance()->notify_malloc(size, (long)p);
910  return p;
911 }
912 
913 void *operator new[] (std::size_t size, const std::nothrow_t&) throw() {
914  void * p = malloc(size);
915  Profiler::instance()->notify_malloc(size, (long)p);
916  return p;
917 }
918 
919 void operator delete( void *p) throw() {
920  Profiler::instance()->notify_free((long)p);
921  free(p);
922 }
923 
924 void operator delete( void *p, std::size_t) throw() {
925  Profiler::instance()->notify_free((long)p);
926  free(p);
927 }
928 
929 void operator delete[]( void *p) throw() {
930  Profiler::instance()->notify_free((long)p);
931  free(p);
932 }
933 
934 void operator delete[]( void *p, std::size_t) throw() {
935  Profiler::instance()->notify_free((long)p);
936  free(p);
937 }
938 
939 #else // def FLOW123D_DEBUG_PROFILER
940 
941 Profiler * Profiler::instance(bool clear) {
942  static Profiler * _instance = new Profiler();
943  return _instance;
944 }
945 
946 
947 // void Profiler::initialize() {
948 // if (_instance == NULL) {
949 // _instance = new Profiler();
950 // set_memory_monitoring(true, true);
951 // }
952 // }
953 
955  if (Profiler::instance()) {
956  ASSERT(Profiler::instance()->actual_node==0)(Profiler::instance()->timers_[Profiler::instance()->actual_node].tag())
957  .error("Forbidden to uninitialize the Profiler when actual timer is not zero.");
958  set_memory_monitoring(false, false);
959  Profiler::instance()->stop_timer(0);
960  // delete _instance;
961  // _instance = NULL;
962  }
963 }
964 
965 
966 #endif // def FLOW123D_DEBUG_PROFILER
#define MPI_LONG
Definition: mpi.h:161
static int min(int *val, MPI_Comm comm)
Definition: sys_profiler.cc:63
#define CONSTEXPR_
Definition: sys_profiler.hh:84
int MPI_Comm
Definition: mpi.h:141
#define OPERATOR_NEW_THROW_EXCEPTION
Definition: system.hh:54
static int sum(int *val, MPI_Comm comm)
Definition: sys_profiler.cc:45
a class to store JSON values
Definition: json.hpp:173
#define MessageOut()
Macro defining &#39;message&#39; record of log.
Definition: logger.hh:255
double get_resolution() const
std::string format(CStringRef format_str, ArgList args)
Definition: format.h:3141
#define MPI_SUM
Definition: mpi.h:196
void chkerr(unsigned int ierr)
Replacement of new/delete operator in the spirit of xmalloc.
Definition: system.hh:148
#define ASSERT(expr)
Allow use shorter versions of macro names if these names is not used with external library...
Definition: asserts.hh:347
void notify_free(const size_t size)
#define MPI_Reduce(sendbuf, recvbuf, count, datatype, op, root, comm)
Definition: mpi.h:608
static void uninitialize()
#define MPI_MIN
Definition: mpi.h:198
static int max(int *val, MPI_Comm comm)
Definition: sys_profiler.cc:81
void set_program_info(string program_name, string program_version, string branch, string revision, string build)
void start()
Definition: memory.cc:39
void swap(nlohmann::json &j1, nlohmann::json &j2) noexcept(is_nothrow_move_constructible< nlohmann::json >::value andis_nothrow_move_assignable< nlohmann::json >::value)
exchanges the values of two JSON objects
Definition: json.hpp:8688
#define MPI_Comm_size
Definition: mpi.h:235
#define MPI_DOUBLE
Definition: mpi.h:156
STREAM & operator<<(STREAM &s, UpdateFlags u)
void push_back(basic_json &&val)
add an object to an array
Definition: json.hpp:4686
void transform_profiler_data(const string &output_file_suffix, const string &formatter)
#define MPI_Comm_rank
Definition: mpi.h:236
Dedicated class for storing path to input and output files.
Definition: file_path.hh:54
#define MPI_INT
Definition: mpi.h:160
#define ASSERT_LT(a, b)
Definition of comparative assert macro (Less Than)
Definition: asserts.hh:296
string_t dump(const int indent=-1) const
serialization
Definition: json.hpp:2079
#define WarningOut()
Macro defining &#39;warning&#39; record of log.
Definition: logger.hh:258
#define MPI_COMM_WORLD
Definition: mpi.h:123
#define MPI_MAX
Definition: mpi.h:197
static Profiler * instance(bool clear=false)
Definition: memory.cc:33
#define MPI_Barrier(comm)
Definition: mpi.h:531
void stop()
Definition: memory.cc:42
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:53
void output(MPI_Comm comm, ostream &os)
#define ASSERT_EQ(a, b)
Definition of comparative assert macro (EQual)
Definition: asserts.hh:328
void set_task_info(string description, int size)
void notify_malloc(const size_t size)