21 #include <sys/param.h> 23 #ifdef FLOW123D_HAVE_PYTHON 25 #endif // FLOW123D_HAVE_PYTHON 31 #include <boost/format.hpp> 32 #include <boost/property_tree/ptree.hpp> 33 #include <boost/property_tree/json_parser.hpp> 34 #include <boost/unordered_map.hpp> 42 namespace property_tree = boost::property_tree;
102 #ifdef FLOW123D_DEBUG_PROFILER 107 const int timer_no_child=-1;
109 Timer::Timer(
const CodePoint &cp,
int parent)
115 full_hash_(cp.hash_),
116 hash_idx_(cp.hash_idx_),
117 parent_timer(parent),
119 total_deallocated_(0),
121 current_allocated_(0),
124 #ifdef FLOW123D_HAVE_PETSC 125 , petsc_start_memory(0),
126 petsc_end_memory (0),
127 petsc_memory_difference(0),
128 petsc_peak_memory(0),
129 petsc_local_peak_memory(0)
130 #endif // FLOW123D_HAVE_PETSC 132 for(
unsigned int i=0; i< max_n_childs ;i++) child_timers[i]=timer_no_child;
139 ostream &
operator <<(ostream& os,
const Timer& timer) {
140 os <<
" Timer: " << timer.tag() << endl;
141 os <<
" malloc: " << timer.total_allocated_ << endl;
142 os <<
" dalloc: " << timer.total_deallocated_ << endl;
143 #ifdef FLOW123D_HAVE_PETSC 144 os <<
" start: " << timer.petsc_start_memory << endl;
145 os <<
" stop : " << timer.petsc_end_memory << endl;
146 os <<
" diff : " << timer.petsc_memory_difference <<
" (" << timer.petsc_end_memory - timer.petsc_start_memory <<
")" << endl;
147 os <<
" peak : " << timer.petsc_peak_memory <<
" (" << timer.petsc_local_peak_memory <<
")" << endl;
148 #endif // FLOW123D_HAVE_PETSC 154 double Timer::cumulative_time()
const {
158 void Profiler::accept_from_child(Timer &parent, Timer &child) {
160 for (
unsigned int i = 0; i < Timer::max_n_childs; i++) {
161 child_timer = child.child_timers[i];
162 if (child_timer != timer_no_child) {
164 accept_from_child(child, timers_[child_timer]);
168 parent.total_allocated_ += child.total_allocated_;
169 parent.total_deallocated_ += child.total_deallocated_;
170 parent.alloc_called += child.alloc_called;
171 parent.dealloc_called += child.dealloc_called;
173 #ifdef FLOW123D_HAVE_PETSC 174 if (petsc_monitor_memory) {
176 parent.petsc_memory_difference += child.petsc_memory_difference;
177 parent.current_allocated_ += child.current_allocated_;
181 parent.petsc_peak_memory =
max(parent.petsc_peak_memory, child.petsc_peak_memory);
183 #endif // FLOW123D_HAVE_PETSC 185 parent.max_allocated_ =
max(parent.max_allocated_, child.max_allocated_);
189 void Timer::pause() {
190 #ifdef FLOW123D_HAVE_PETSC 191 if (Profiler::get_petsc_memory_monitoring()) {
193 PetscMemoryGetMaximumUsage(&petsc_local_peak_memory);
194 if (petsc_peak_memory < petsc_local_peak_memory)
195 petsc_peak_memory = petsc_local_peak_memory;
197 #endif // FLOW123D_HAVE_PETSC 200 void Timer::resume() {
201 #ifdef FLOW123D_HAVE_PETSC 202 if (Profiler::get_petsc_memory_monitoring()) {
205 PetscMemorySetGetMaximumUsage();
207 #endif // FLOW123D_HAVE_PETSC 210 void Timer::start() {
211 #ifdef FLOW123D_HAVE_PETSC 212 if (Profiler::get_petsc_memory_monitoring()) {
215 PetscMemorySetGetMaximumUsage();
216 PetscMemoryGetCurrentUsage (&petsc_start_memory);
218 #endif // FLOW123D_HAVE_PETSC 220 if (start_count == 0) {
229 bool Timer::stop(
bool forced) {
230 #ifdef FLOW123D_HAVE_PETSC 231 if (Profiler::get_petsc_memory_monitoring()) {
233 PetscMemoryGetCurrentUsage (&petsc_end_memory);
234 petsc_memory_difference += petsc_end_memory - petsc_start_memory;
237 PetscMemoryGetMaximumUsage(&petsc_local_peak_memory);
238 if (petsc_peak_memory < petsc_local_peak_memory)
239 petsc_peak_memory = petsc_local_peak_memory;
241 #endif // FLOW123D_HAVE_PETSC 243 if (forced) start_count=1;
245 if (start_count == 1) {
246 cumul_time += (
TimePoint() - start_time);
257 void Timer::add_child(
int child_index,
const Timer &child)
259 unsigned int idx = child.hash_idx_;
260 if (child_timers[idx] != timer_no_child) {
264 i=( i < max_n_childs ? i+1 : 0);
265 }
while (i!=idx && child_timers[i] != timer_no_child);
266 ASSERT(i!=idx)(tag()).error(
"Too many children of the timer");
270 child_timers[idx] = child_index;
275 string Timer::code_point_str()
const {
276 return boost::str(
boost::format(
"%s:%d, %s()") % code_point_->file_ % code_point_->line_ % code_point_->func_ );
286 if (_instance == NULL) {
287 MemoryAlloc::malloc_map().reserve(Profiler::malloc_map_reserve);
294 static CONSTEXPR_ CodePoint main_cp = CODE_POINT(
"Whole Program");
296 const long Profiler::malloc_map_reserve = 100 * 1000;
297 CodePoint Profiler::null_code_point = CodePoint(
"__no_tag__",
"__no_file__",
"__no_func__", 0);
301 set_memory_monitoring(
true,
true);
308 start_time( time(NULL) ),
312 #ifdef FLOW123D_DEBUG_PROFILER 313 timers_.push_back( Timer(main_cp, 0) );
320 void Profiler::propagate_timers() {
321 for (
unsigned int i = 0; i < Timer::max_n_childs; i++) {
322 unsigned int child_timer = timers_[0].child_timers[i];
323 if ((
signed int)child_timer != timer_no_child) {
325 accept_from_child(timers_[0], timers_[child_timer]);
333 task_description_ = description;
340 flow_name_ = program_name;
341 flow_version_ = program_version;
342 flow_branch_ = branch;
343 flow_revision_ = revision;
349 int Profiler::start_timer(
const CodePoint &cp) {
350 unsigned int parent_node = actual_node;
352 int child_idx = find_child(cp);
356 child_idx=timers_.size();
357 timers_.push_back( Timer(cp, actual_node) );
358 timers_[actual_node].add_child(child_idx , timers_.back() );
360 actual_node=child_idx;
363 timers_[parent_node].pause();
365 timers_[actual_node].start();
372 int Profiler::find_child(
const CodePoint &cp) {
373 Timer &timer =timers_[actual_node];
374 unsigned int idx = cp.hash_idx_;
375 unsigned int child_idx;
377 if (timer.child_timers[idx] == timer_no_child)
break;
379 child_idx=timer.child_timers[idx];
380 ASSERT_LT(child_idx, timers_.size()).error();
381 if (timers_[child_idx].full_hash_ == cp.hash_)
return child_idx;
382 idx = ( (
unsigned int)(idx)==(Timer::max_n_childs - 1) ? 0 : idx+1 );
383 }
while ( (
unsigned int)(idx) != cp.hash_idx_ );
389 void Profiler::stop_timer(
const CodePoint &cp) {
390 #ifdef FLOW123D_DEBUG 392 Timer &timer=timers_[actual_node];
393 for(
unsigned int i=0; i < Timer::max_n_childs; i++)
394 if (timer.child_timers[i] != timer_no_child)
395 ASSERT(! timers_[timer.child_timers[i]].running())(timers_[timer.child_timers[i]].tag())(timer.tag())
396 .error(
"Child timer running while closing timer.");
398 unsigned int child_timer = actual_node;
399 if ( cp.hash_ != timers_[actual_node].full_hash_) {
401 for(
unsigned int node=actual_node; node != 0; node=timers_[node].parent_timer) {
402 if ( cp.hash_ == timers_[node].full_hash_) {
404 for(; (
unsigned int)(actual_node) != node; actual_node=timers_[actual_node].parent_timer) {
405 WarningOut() <<
"Timer to close '" << cp.tag_ <<
"' do not match actual timer '" 406 << timers_[actual_node].tag() <<
"'. Force closing actual." << std::endl;
407 timers_[actual_node].stop(
true);
410 timers_[actual_node].stop(
false);
411 actual_node = timers_[actual_node].parent_timer;
414 if (actual_node == child_timer)
418 timers_[actual_node].resume();
426 timers_[actual_node].stop(
false);
427 actual_node = timers_[actual_node].parent_timer;
430 if (actual_node == child_timer)
434 timers_[actual_node].resume();
439 void Profiler::stop_timer(
int timer_index) {
443 if (timers_[timer_index].running()) {
444 ASSERT_EQ(timer_index, (
int)actual_node).error();
445 stop_timer(*timers_[timer_index].code_point_);
452 void Profiler::add_calls(
unsigned int n_calls) {
453 timers_[actual_node].call_count += n_calls-1;
459 if (!global_monitor_memory)
462 MemoryAlloc::malloc_map()[p] =
static_cast<int>(size);
463 timers_[actual_node].total_allocated_ += size;
464 timers_[actual_node].current_allocated_ += size;
465 timers_[actual_node].alloc_called++;
467 if (timers_[actual_node].current_allocated_ > timers_[actual_node].max_allocated_)
468 timers_[actual_node].max_allocated_ = timers_[actual_node].current_allocated_;
474 if (!global_monitor_memory)
477 int size =
sizeof(p);
478 if (MemoryAlloc::malloc_map()[(long)p] > 0) {
479 size = MemoryAlloc::malloc_map()[(long)p];
480 MemoryAlloc::malloc_map().erase((
long)p);
482 timers_[actual_node].total_deallocated_ += size;
483 timers_[actual_node].current_allocated_ -= size;
484 timers_[actual_node].dealloc_called++;
489 const int measurements = 100;
493 for (
unsigned int i = 1; i < measurements; i++) {
498 while ((t2 - t1) == 0) t2 =
TimePoint ();
504 return (result / measurements) * 1000;
508 std::string common_prefix( std::string a, std::string b ) {
509 if( a.size() > b.size() ) std::swap(a,b) ;
510 return std::string( a.begin(), std::mismatch( a.begin(), a.end(), b.begin() ).first ) ;
515 template<
typename ReduceFunctor>
516 void Profiler::add_timer_info(ReduceFunctor reduce, property_tree::ptree* holder,
int timer_idx,
double parent_time) {
519 Timer &timer = timers_[timer_idx];
520 ASSERT(timer_idx >=0)(timer_idx).error(
"Wrong timer index.");
521 ASSERT(timer.parent_timer >=0).error(
"Inconsistent tree.");
524 string filepath = timer.code_point_->file_;
527 #ifdef FLOW123D_SOURCE_DIR 528 string common_path = common_prefix (
string(FLOW123D_SOURCE_DIR), filepath);
529 filepath.erase (0, common_path.size());
535 property_tree::ptree node;
536 double cumul_time_sum;
537 node.put (
"tag", (timer.tag()) );
538 node.put (
"file-path", (filepath) );
539 node.put (
"file-line", (timer.code_point_->line_) );
540 node.put (
"function", (timer.code_point_->func_) );
541 cumul_time_sum = reduce (timer, node);
545 if (timer_idx == 0) parent_time = cumul_time_sum;
546 double percent = parent_time > 1.0e-10 ? cumul_time_sum / parent_time * 100.0 : 0.0;
547 node.put<
double> (
"percent", percent);
578 property_tree::ptree children;
579 bool has_children =
false;
580 for (
unsigned int i = 0; i < Timer::max_n_childs; i++) {
581 if (timer.child_timers[i] != timer_no_child) {
582 add_timer_info (reduce, &children, timer.child_timers[i], cumul_time_sum);
592 node.add_child (
"children", children);
595 holder->push_back (std::make_pair (
"", node));
600 void save_nonmpi_metric (property_tree::ptree &node, T * ptr,
string name) {
601 node.put (name+
"-min", *ptr);
602 node.put (name+
"-max", *ptr);
603 node.put (name+
"-sum", *ptr);
606 std::shared_ptr<std::ostream> Profiler::get_default_output_stream() {
607 char filename[PATH_MAX];
608 strftime(filename,
sizeof (filename) - 1,
"profiler_info_%y.%m.%d_%H-%M-%S.log.json", localtime(&start_time));
612 return make_shared<ofstream>(json_filepath.c_str());
616 #ifdef FLOW123D_HAVE_MPI 618 void save_mpi_metric (property_tree::ptree &node,
MPI_Comm comm, T * ptr,
string name) {
625 int mpi_rank, mpi_size;
632 bool temp_memory_monitoring = global_monitor_memory;
633 set_memory_monitoring(
false, petsc_monitor_memory);
639 property_tree::ptree root, children;
640 output_header (root, mpi_size);
645 auto reduce = [=] (Timer &timer, property_tree::ptree &node) ->
double {
646 int call_count = timer.call_count;
647 double cumul_time = timer.cumulative_time ();
649 long memory_allocated = (long)timer.total_allocated_;
650 long memory_deallocated = (
long)timer.total_deallocated_;
651 long memory_peak = (long)timer.max_allocated_;
653 int alloc_called = timer.alloc_called;
654 int dealloc_called = timer.dealloc_called;
657 save_mpi_metric<double>(node, comm, &cumul_time,
"cumul-time");
658 save_mpi_metric<int>(node, comm, &call_count,
"call-count");
660 save_mpi_metric<long>(node, comm, &memory_allocated,
"memory-alloc");
661 save_mpi_metric<long>(node, comm, &memory_deallocated,
"memory-dealloc");
662 save_mpi_metric<long>(node, comm, &memory_peak,
"memory-peak");
664 save_mpi_metric<int>(node, comm, &alloc_called,
"memory-alloc-called");
665 save_mpi_metric<int>(node, comm, &dealloc_called,
"memory-dealloc-called");
667 #ifdef FLOW123D_HAVE_PETSC 668 long petsc_memory_difference = (long)timer.petsc_memory_difference;
669 long petsc_peak_memory = (
long)timer.petsc_peak_memory;
670 save_mpi_metric<long>(node, comm, &petsc_memory_difference,
"memory-petsc-diff");
671 save_mpi_metric<long>(node, comm, &petsc_peak_memory,
"memory-petsc-peak");
672 #endif // FLOW123D_HAVE_PETSC 677 add_timer_info (reduce, &children, 0, 0.0);
678 root.add_child (
"children", children);
688 const int FLOW123D_JSON_HUMAN_READABLE = 1;
690 property_tree::write_json (os, root, FLOW123D_JSON_HUMAN_READABLE);
693 set_memory_monitoring(temp_memory_monitoring, petsc_monitor_memory);
701 output(comm, *get_default_output_stream());
716 property_tree::ptree root, children;
721 const int FLOW123D_MPI_SINGLE_PROCESS = 1;
722 output_header (root, FLOW123D_MPI_SINGLE_PROCESS);
728 auto reduce = [=] (Timer &timer, property_tree::ptree &node) ->
double {
729 int call_count = timer.call_count;
730 double cumul_time = timer.cumulative_time ();
732 long memory_allocated = (long)timer.total_allocated_;
733 long memory_deallocated = (
long)timer.total_deallocated_;
734 long memory_peak = (long)timer.max_allocated_;
736 int alloc_called = timer.alloc_called;
737 int dealloc_called = timer.dealloc_called;
739 save_nonmpi_metric<double>(node, &cumul_time,
"cumul-time");
740 save_nonmpi_metric<int>(node, &call_count,
"call-count");
742 save_nonmpi_metric<long>(node, &memory_allocated,
"memory-alloc");
743 save_nonmpi_metric<long>(node, &memory_deallocated,
"memory-dealloc");
744 save_nonmpi_metric<long>(node, &memory_peak,
"memory-peak");
746 save_nonmpi_metric<int>(node, &alloc_called,
"memory-alloc-called");
747 save_nonmpi_metric<int>(node, &dealloc_called,
"memory-dealloc-called");
749 #ifdef FLOW123D_HAVE_PETSC 750 long petsc_memory_difference = (long)timer.petsc_memory_difference;
751 long petsc_peak_memory = (
long)timer.petsc_peak_memory;
752 save_nonmpi_metric<long>(node, &petsc_memory_difference,
"memory-petsc-diff");
753 save_nonmpi_metric<long>(node, &petsc_peak_memory,
"memory-petsc-peak");
754 #endif // FLOW123D_HAVE_PETSC 759 add_timer_info (reduce, &children, 0, 0.0);
760 root.add_child (
"children", children);
767 const int FLOW123D_JSON_HUMAN_READABLE = 1;
769 property_tree::write_json (os, root, FLOW123D_JSON_HUMAN_READABLE);
774 output(*get_default_output_stream());
777 void Profiler::output_header (property_tree::ptree &root,
int mpi_size) {
778 time_t end_time = time(NULL);
780 const char format[] =
"%x %X";
781 char start_time_string[BUFSIZ] = {0};
782 strftime(start_time_string,
sizeof (start_time_string) - 1, format, localtime(&start_time));
784 char end_time_string[BUFSIZ] = {0};
785 strftime(end_time_string,
sizeof (end_time_string) - 1, format, localtime(&end_time));
789 root.put (
"program-name", flow_name_);
790 root.put (
"program-version", flow_version_);
791 root.put (
"program-branch", flow_branch_);
792 root.put (
"program-revision", flow_revision_);
793 root.put (
"program-build", flow_build_);
796 #ifdef FLOW123D_SOURCE_DIR 800 root.put (
"task-description", task_description_);
801 root.put (
"task-size", task_size_);
804 root.put (
"run-process-count", mpi_size);
805 root.put (
"run-started-at", start_time_string);
806 root.put (
"run-finished-at", end_time_string);
809 #ifdef FLOW123D_HAVE_PYTHON 812 if (json_filepath==
"")
return;
821 #ifndef FLOW123D_HAVE_CYGWIN 823 PyObject * python_module = PythonLoader::load_module_by_name (
"profiler.profiler_formatter_module");
827 PyObject * convert_method = PythonLoader::get_callable (python_module,
"convert" );
829 int argument_index = 0;
830 PyObject * arguments = PyTuple_New (3);
833 PyObject * tmp = PyString_FromString (json_filepath.c_str());
834 PyTuple_SetItem (arguments, argument_index++, tmp);
837 tmp = PyString_FromString ((json_filepath + output_file_suffix).c_str());
838 PyTuple_SetItem (arguments, argument_index++, tmp);
841 tmp = PyString_FromString (formatter.c_str());
842 PyTuple_SetItem (arguments, argument_index++, tmp);
845 PyObject_CallObject (convert_method, arguments);
847 PythonLoader::check_error();
852 MessageOut() <<
"# Note: converting json profiler reports is not" 853 <<
" supported under Windows or Cygwin environment for now.\n" 854 <<
"# You can use python script located in bin/python folder" 855 <<
" in order to convert json report to txt or csv format.\n" 856 <<
"python profiler_formatter_script.py --input \"" << json_filepath
857 <<
"\" --output \"profiler.txt\"" << std::endl;
858 #endif // FLOW123D_HAVE_CYGWIN 864 #endif // FLOW123D_HAVE_PYTHON 869 ASSERT(_instance->actual_node==0)(_instance->timers_[_instance->actual_node].tag())
870 .error(
"Forbidden to uninitialize the Profiler when actual timer is not zero.");
871 _instance->stop_timer(0);
872 set_memory_monitoring(
false,
false);
877 bool Profiler::global_monitor_memory =
false;
878 bool Profiler::petsc_monitor_memory =
true;
879 void Profiler::set_memory_monitoring(
const bool global_monitor,
const bool petsc_monitor) {
880 global_monitor_memory = global_monitor;
881 petsc_monitor_memory = petsc_monitor;
884 bool Profiler::get_global_memory_monitoring() {
885 return global_monitor_memory;
888 bool Profiler::get_petsc_memory_monitoring() {
889 return petsc_monitor_memory;
892 unordered_map_with_alloc & MemoryAlloc::malloc_map() {
893 static unordered_map_with_alloc static_malloc_map;
894 return static_malloc_map;
897 void * Profiler::operator
new (
size_t size) {
898 return malloc (size);
901 void Profiler::operator
delete (
void* p) {
906 void * p = malloc(size);
912 void * p = malloc(size);
917 void *
operator new[] (std::size_t size,
const std::nothrow_t&)
throw() {
918 void * p = malloc(size);
923 void operator delete(
void *p)
throw() {
928 void operator delete[](
void *p)
throw() {
933 #else // def FLOW123D_DEBUG_PROFILER 940 Profiler* Profiler::_instance = NULL;
943 if (_instance == NULL) {
945 set_memory_monitoring(
true,
true);
951 ASSERT(_instance->actual_node==0)(_instance->timers_[_instance->actual_node].tag())
952 .error(
"Forbidden to uninitialize the Profiler when actual timer is not zero.");
953 set_memory_monitoring(
false,
false);
954 _instance->stop_timer(0);
961 #endif // def FLOW123D_DEBUG_PROFILER
static int min(int *val, MPI_Comm comm)
#define OPERATOR_NEW_THROW_EXCEPTION
static int sum(int *val, MPI_Comm comm)
#define MessageOut()
Macro defining 'message' record of log.
double get_resolution() const
std::string format(CStringRef format_str, ArgList args)
static double min(double *val, MPI_Comm comm)
void chkerr(unsigned int ierr)
Replacement of new/delete operator in the spirit of xmalloc.
#define ASSERT(expr)
Allow use shorter versions of macro names if these names is not used with external library...
void notify_free(const size_t size)
#define MPI_Reduce(sendbuf, recvbuf, count, datatype, op, root, comm)
static void uninitialize()
static int max(int *val, MPI_Comm comm)
void set_program_info(string program_name, string program_version, string branch, string revision, string build)
STREAM & operator<<(STREAM &s, UpdateFlags u)
void transform_profiler_data(const string &output_file_suffix, const string &formatter)
Dedicated class for storing path to input and output files.
#define ASSERT_LT(a, b)
Definition of comparative assert macro (Less Than)
static Profiler * instance()
#define WarningOut()
Macro defining 'warning' record of log.
static Profiler * _instance
#define MPI_Barrier(comm)
void output(MPI_Comm comm, ostream &os)
#define ASSERT_EQ(a, b)
Definition of comparative assert macro (EQual)
void set_task_info(string description, int size)
void notify_malloc(const size_t size)