perf_log.C
Go to the documentation of this file.00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2012 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner 00003 00004 // This library is free software; you can redistribute it and/or 00005 // modify it under the terms of the GNU Lesser General Public 00006 // License as published by the Free Software Foundation; either 00007 // version 2.1 of the License, or (at your option) any later version. 00008 00009 // This library is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // Lesser General Public License for more details. 00013 00014 // You should have received a copy of the GNU Lesser General Public 00015 // License along with this library; if not, write to the Free Software 00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 00018 00019 00020 // C++ includes 00021 #include <iostream> 00022 #include <iomanip> 00023 #include <ctime> 00024 #include <unistd.h> 00025 #include <sys/utsname.h> 00026 #include <sys/types.h> 00027 #include <pwd.h> 00028 #include <vector> 00029 #include <sstream> 00030 00031 // Local includes 00032 #include "libmesh/perf_log.h" 00033 #include "libmesh/timestamp.h" 00034 00035 namespace libMesh 00036 { 00037 00038 00039 // ------------------------------------------------------------ 00040 // PerfLog class member funcions 00041 00042 bool PerfLog::called = false; 00043 00044 00045 PerfLog::PerfLog(const std::string& ln, 00046 const bool le) : 00047 label_name(ln), 00048 log_events(le), 00049 total_time(0.) 00050 { 00051 gettimeofday (&tstart, NULL); 00052 00053 if (log_events) 00054 this->clear(); 00055 } 00056 00057 00058 00059 PerfLog::~PerfLog() 00060 { 00061 if (log_events) 00062 this->print_log(); 00063 } 00064 00065 00066 00067 void PerfLog::clear() 00068 { 00069 if (log_events) 00070 { 00071 // check that all events are closed 00072 for (std::map<std::pair<std::string,std::string>, PerfData>::iterator 00073 pos = log.begin(); pos != log.end(); ++pos) 00074 if (pos->second.open) 00075 { 00076 libMesh::out 00077 << "ERROR clearning performance log for class " 00078 << label_name << std::endl 00079 << "event " << pos->first.second << " is still being monitored!" 00080 << std::endl; 00081 00082 libmesh_error(); 00083 } 00084 00085 00086 gettimeofday (&tstart, NULL); 00087 00088 log.clear(); 00089 00090 while (!log_stack.empty()) 00091 log_stack.pop(); 00092 } 00093 } 00094 00095 00096 std::string PerfLog::get_info_header() const 00097 { 00098 std::ostringstream oss; 00099 00100 if (log_events) 00101 { 00102 std::string date = Utility::get_timestamp(); 00103 00104 // Get system information 00105 struct utsname sysInfo; 00106 uname(&sysInfo); 00107 00108 // Get user information 00109 // 00110 // Some systems, for example Crays, actually have getpwuid on the head-node 00111 // but (if I understand correctly) a dynamically-linked glibc is not available 00112 // on the backend, which is running a reduced operating system like Compute 00113 // Node Linux. Thus functions like getpwuid cannot be called. This makes 00114 // automatically testing for the existence of getpwuid on the login node 00115 // difficult. The configure test would work on the login node but will fail 00116 // on the backend. Hence we have added a configure flag, --disable-getpwuid, 00117 // to manually turn this off. 00118 #ifdef LIBMESH_HAVE_GETPWUID 00119 struct passwd* p = getpwuid(getuid()); 00120 #endif 00121 oss << "\n"; 00122 00123 // Construct string stream objects for each of the outputs 00124 std::ostringstream 00125 pid_stream, 00126 nprocs_stream, 00127 time_stream, 00128 os_stream, 00129 host_stream, 00130 osrel_stream, 00131 osver_stream, 00132 machine_stream, 00133 user_stream; 00134 00135 00136 // Put pointers to these streams in a vector 00137 std::vector<std::ostringstream*> v; 00138 v.push_back(&pid_stream); 00139 v.push_back(&nprocs_stream); 00140 v.push_back(&time_stream); 00141 v.push_back(&os_stream); 00142 v.push_back(&host_stream); 00143 v.push_back(&osrel_stream); 00144 v.push_back(&osver_stream); 00145 v.push_back(&machine_stream); 00146 v.push_back(&user_stream); 00147 00148 // Fill string stream objects 00149 if (libMesh::n_processors() > 1) 00150 { 00151 pid_stream << "| Processor id: " << libMesh::processor_id(); 00152 nprocs_stream << "| Num Processors: " << libMesh::n_processors(); 00153 } 00154 00155 time_stream << "| Time: " << date ; 00156 os_stream << "| OS: " << sysInfo.sysname ; 00157 host_stream << "| HostName: " << sysInfo.nodename ; 00158 osrel_stream << "| OS Release: " << sysInfo.release ; 00159 osver_stream << "| OS Version: " << sysInfo.version ; 00160 machine_stream << "| Machine: " << sysInfo.machine ; 00161 user_stream << "| Username: "; 00162 #ifdef LIBMESH_HAVE_GETPWUID 00163 if (p && p->pw_name) 00164 user_stream << p->pw_name; 00165 else 00166 #endif 00167 user_stream << "Unknown"; 00168 00169 // Parse the LIBMESH_CONFIGURE_INFO string literal before using it in PerfLog output 00170 std::string libmesh_configure_info(LIBMESH_CONFIGURE_INFO); 00171 std::vector<std::string> parsed_libmesh_configure_info; 00172 this->split_on_whitespace(libmesh_configure_info, 00173 parsed_libmesh_configure_info); 00174 00175 // There should always be at at least one entry in 00176 // parsed_libmesh_configure_info, even if the user just ran 00177 // ../configure. 00178 libmesh_assert_greater (parsed_libmesh_configure_info.size(), 0); 00179 00180 // Find the longest string in all the streams 00181 unsigned int max_length = 0; 00182 for (unsigned int i=0; i<v.size(); ++i) 00183 if (v[i]->str().size() > max_length) 00184 max_length = libmesh_cast_int<unsigned int> 00185 (v[i]->str().size()); 00186 00187 // Find the longest string in the parsed_libmesh_configure_info 00188 for (unsigned i=0; i<parsed_libmesh_configure_info.size(); ++i) 00189 if (parsed_libmesh_configure_info[i].size() > max_length) 00190 max_length = libmesh_cast_int<unsigned int> 00191 (parsed_libmesh_configure_info[i].size()); 00192 00193 // Print dashed line for the header 00194 oss << ' ' 00195 << std::string(max_length+1, '-') 00196 << '\n'; 00197 00198 // Loop over all the strings and add end formatting 00199 for (unsigned int i=0; i<v.size(); ++i) 00200 { 00201 if (v[i]->str().size()) 00202 oss << v[i]->str() 00203 << std::setw(max_length + 4 - v[i]->str().size()) 00204 << std::right 00205 << "|\n"; 00206 } 00207 00208 // Print out configuration header plus first parsed string. The 00209 // magic number 18 below accounts for the length of the word 00210 // 'Configuration'. 00211 oss << "| Configuration: " 00212 << parsed_libmesh_configure_info[0] 00213 << std::setw(max_length + 4 - parsed_libmesh_configure_info[0].size() - 18) 00214 << std::right 00215 << "|\n"; 00216 00217 // Loop over the parsed_libmesh_configure_info and add end formatting. The magic 00218 // number 3 below accounts for the leading 'pipe' character and indentation 00219 for (unsigned i=1; i<parsed_libmesh_configure_info.size(); ++i) 00220 { 00221 oss << "| " 00222 << parsed_libmesh_configure_info[i] 00223 << std::setw(max_length + 4 - parsed_libmesh_configure_info[i].size() - 3) 00224 << std::right 00225 << "|\n"; 00226 } 00227 00228 00229 // Print dashed line 00230 oss << ' ' 00231 << std::string(max_length+1, '-') 00232 << '\n'; 00233 } 00234 00235 return oss.str(); 00236 } 00237 00238 00239 00240 00241 std::string PerfLog::get_perf_info() const 00242 { 00243 std::ostringstream oss; 00244 00245 if (log_events && !log.empty()) 00246 { 00247 // Stop timing for this event. 00248 struct timeval tstop; 00249 00250 gettimeofday (&tstop, NULL); 00251 00252 const double elapsed_time = (static_cast<double>(tstop.tv_sec - tstart.tv_sec) + 00253 static_cast<double>(tstop.tv_usec - tstart.tv_usec)*1.e-6); 00254 00255 // Figure out the formatting required based on the event names 00256 // Unsigned ints for each of the column widths 00257 unsigned int event_col_width = 30; 00258 const unsigned int ncalls_col_width = 10; 00259 const unsigned int tot_time_col_width = 12; 00260 const unsigned int avg_time_col_width = 12; 00261 const unsigned int tot_time_incl_sub_col_width = 12; 00262 const unsigned int avg_time_incl_sub_col_width = 12; 00263 const unsigned int pct_active_col_width = 9; 00264 const unsigned int pct_active_incl_sub_col_width = 9; 00265 00266 // Iterator to be used to loop over the map of timed events 00267 std::map<std::pair<std::string,std::string>, PerfData>::const_iterator pos; 00268 00269 // Reset the event column width based on the longest event name plus 00270 // a possible 2-character indentation, plus a space. 00271 for (pos = log.begin(); pos != log.end(); ++pos) 00272 if (pos->first.second.size()+3 > event_col_width) 00273 event_col_width = libmesh_cast_int<unsigned int> 00274 (pos->first.second.size()+3); 00275 00276 // Set the total width of the column 00277 const unsigned int total_col_width = 00278 event_col_width + 00279 ncalls_col_width + 00280 tot_time_col_width + 00281 avg_time_col_width + 00282 tot_time_incl_sub_col_width + 00283 avg_time_incl_sub_col_width + 00284 pct_active_col_width+ 00285 pct_active_incl_sub_col_width+1; 00286 00287 // Print dashed line 00288 oss << ' ' 00289 << std::string(total_col_width, '-') 00290 << '\n'; 00291 00292 { 00293 // Construct temporary message string 00294 std::ostringstream temp; 00295 temp << "| " << label_name << " Performance: Alive time=" << elapsed_time 00296 << ", Active time=" << total_time; 00297 00298 // Get the size of the temporary string 00299 const unsigned int temp_size = libmesh_cast_int<unsigned int> 00300 (temp.str().size()); 00301 00302 // Send the temporary message to the output 00303 oss << temp.str(); 00304 00305 // If this string is longer than the previously computed total 00306 // column width, skip the additional formatting... this shouldn't 00307 // happen often, hopefully. Add two additional characters for a 00308 // space and a "|" character at the end. 00309 if (temp_size < total_col_width+2) 00310 oss << std::setw(total_col_width - temp_size + 2) 00311 << std::right 00312 << "|"; 00313 00314 oss << '\n'; 00315 } 00316 00317 // Print dashed line 00318 oss << ' ' 00319 << std::string(total_col_width, '-') 00320 << '\n'; 00321 00322 00323 // Write out the header for the events listing 00324 oss << "| " 00325 << std::setw(event_col_width) 00326 << std::left 00327 << "Event" 00328 << std::setw(ncalls_col_width) 00329 << std::left 00330 << "nCalls" 00331 << std::setw(tot_time_col_width) 00332 << std::left 00333 << "Total Time" 00334 << std::setw(avg_time_col_width) 00335 << std::left 00336 << "Avg Time" 00337 << std::setw(tot_time_incl_sub_col_width) 00338 << std::left 00339 << "Total Time" 00340 << std::setw(avg_time_incl_sub_col_width) 00341 << std::left 00342 << "Avg Time" 00343 << std::setw(pct_active_col_width+pct_active_incl_sub_col_width) 00344 << std::left 00345 << "% of Active Time" 00346 << "|\n" 00347 << "| " 00348 << std::setw(event_col_width) 00349 << std::left 00350 << "" 00351 << std::setw(ncalls_col_width) 00352 << std::left 00353 << "" 00354 << std::setw(tot_time_col_width) 00355 << std::left 00356 << "w/o Sub" 00357 << std::setw(avg_time_col_width) 00358 << std::left 00359 << "w/o Sub" 00360 << std::setw(tot_time_incl_sub_col_width) 00361 << std::left 00362 << "With Sub" 00363 << std::setw(avg_time_incl_sub_col_width) 00364 << std::left 00365 << "With Sub" 00366 << std::setw(pct_active_col_width) 00367 << std::left 00368 << "w/o S" 00369 << std::setw(pct_active_incl_sub_col_width) 00370 << std::left 00371 << "With S" 00372 << "|\n|" 00373 << std::string(total_col_width, '-') 00374 << "|\n|" 00375 << std::string(total_col_width, ' ') 00376 << "|\n"; 00377 00378 unsigned int summed_function_calls = 0; 00379 double summed_total_time = 0; 00380 double summed_percentage = 0; 00381 00382 std::string last_header(""); 00383 00384 for (pos = log.begin(); pos != log.end(); ++pos) 00385 { 00386 const PerfData& perf_data = pos->second; 00387 00388 // Only print the event if the count is non-zero. 00389 if (perf_data.count != 0) 00390 { 00391 const unsigned int perf_count = perf_data.count; 00392 const double perf_time = perf_data.tot_time; 00393 const double perf_avg_time = perf_time / static_cast<double>(perf_count); 00394 const double perf_time_incl_sub = perf_data.tot_time_incl_sub; 00395 const double perf_avg_time_incl_sub = perf_time_incl_sub / static_cast<double>(perf_count); 00396 const double perf_percent = (total_time != 0.) ? perf_time / total_time * 100. : 0.; 00397 const double perf_percent_incl_sub = (total_time != 0.) ? perf_time_incl_sub / total_time * 100. : 0.; 00398 00399 summed_function_calls += perf_count; 00400 summed_total_time += perf_time; 00401 summed_percentage += perf_percent; 00402 00403 // Print the event name 00404 if (pos->first.first == "") 00405 oss << "| " 00406 << std::setw(event_col_width) 00407 << std::left 00408 << pos->first.second; 00409 00410 else 00411 { 00412 if (last_header != pos->first.first) 00413 { 00414 last_header = pos->first.first; 00415 00416 // print blank line followed by header name 00417 // (account for additional space before the 00418 // header) 00419 oss << "|" 00420 << std::string(total_col_width, ' ') 00421 << "|\n| " 00422 << std::setw(total_col_width-1) 00423 << std::left 00424 << pos->first.first 00425 << "|\n"; 00426 } 00427 00428 oss << "| " 00429 << std::setw(event_col_width-2) 00430 << std::left 00431 << pos->first.second; 00432 } 00433 00434 00435 // Print the number of calls to the event. 00436 oss << std::setw(ncalls_col_width) 00437 << perf_count; 00438 00439 // Save the original stream flags 00440 std::ios_base::fmtflags out_flags = oss.flags(); 00441 00442 // Print the total time spent in the event 00443 oss << std::fixed 00444 << std::setprecision(4) 00445 << std::setw(tot_time_col_width) 00446 << std::left 00447 << perf_time; 00448 00449 00450 // Print the average time per function call 00451 oss << std::fixed 00452 << std::setprecision(6) 00453 << std::setw(avg_time_col_width) 00454 << std::left 00455 << perf_avg_time; 00456 00457 // Print the total time spent in the event incl. sub-events 00458 oss << std::fixed 00459 << std::setprecision(4) 00460 << std::setw(tot_time_incl_sub_col_width) 00461 << std::left 00462 << perf_time_incl_sub; 00463 00464 // Print the average time per function call incl. sub-events 00465 oss << std::fixed 00466 << std::setprecision(6) 00467 << std::setw(avg_time_incl_sub_col_width) 00468 << std::left 00469 << perf_avg_time_incl_sub; 00470 00471 // Print the percentage of the time spent in the event 00472 oss << std::fixed 00473 << std::setprecision(2) 00474 << std::setw(pct_active_col_width) 00475 << std::left 00476 << perf_percent; 00477 00478 // Print the percentage of the time spent in the event incl. sub-events 00479 oss << std::fixed 00480 << std::setprecision(2) 00481 << std::setw(pct_active_incl_sub_col_width) 00482 << std::left 00483 << perf_percent_incl_sub; 00484 00485 // Reset the stream flags 00486 oss.flags(out_flags); 00487 00488 oss << "|\n"; 00489 } 00490 } 00491 00492 oss << ' ' 00493 << std::string(total_col_width, '-') 00494 << "\n| " 00495 << std::setw(event_col_width) 00496 << std::left 00497 << "Totals:"; 00498 00499 // Print the total number of logged function calls 00500 // For routines which are called many times, summed_function_calls may 00501 // exceed 7 digits. If this happens use, scientific notation. 00502 if (summed_function_calls < 9999999) 00503 oss << std::setw(ncalls_col_width) 00504 << summed_function_calls; 00505 00506 else 00507 { 00508 // Save the original stream flags 00509 std::ios_base::fmtflags out_flags = oss.flags(); 00510 00511 oss << std::scientific 00512 << std::setprecision(3) 00513 << std::setw(ncalls_col_width) 00514 << std::left 00515 << static_cast<Real>(summed_function_calls); 00516 00517 // Reset the stream flags 00518 oss.flags(out_flags); 00519 } 00520 00521 // Print the total time spent in logged function calls. Don't bother saving/restoring 00522 // the flags here since we are almost done with this stream anyway... 00523 oss << std::fixed 00524 << std::setprecision(4) 00525 << std::setw(tot_time_col_width) 00526 << std::left 00527 << summed_total_time; 00528 00529 // Null, the average time doesn't make sense as a total 00530 oss << std::setw(avg_time_col_width) << ""; 00531 00532 // Same for times that include sub-events 00533 oss << std::setw(tot_time_incl_sub_col_width) 00534 << "" 00535 << std::setw(avg_time_incl_sub_col_width) 00536 << ""; 00537 00538 // Print the total percentage followed by dashed line 00539 oss << std::fixed 00540 << std::setprecision(2) 00541 << std::setw(pct_active_col_width) 00542 << std::left 00543 << summed_percentage 00544 << std::setw(pct_active_incl_sub_col_width) 00545 << "" 00546 << "|\n " 00547 << std::string(total_col_width, '-') 00548 << '\n'; 00549 } 00550 00551 return oss.str(); 00552 } 00553 00554 00555 00556 std::string PerfLog::get_log() const 00557 { 00558 std::ostringstream oss; 00559 00560 if (log_events) 00561 { 00562 // Only print the log 00563 // if it isn't empty 00564 if (!log.empty()) 00565 { 00566 // Possibly print machine info, 00567 // but only do this once 00568 if (!called) 00569 { 00570 called = true; 00571 oss << get_info_header(); 00572 } 00573 oss << get_perf_info(); 00574 } 00575 } 00576 00577 return oss.str(); 00578 } 00579 00580 00581 00582 void PerfLog::print_log() const 00583 { 00584 if (log_events) 00585 { 00586 // Check to see if the log_string is empty, and if so, 00587 // avoid printing an unnecessary newline. 00588 std::string log_string = this->get_log(); 00589 if (log_string.size() > 0) 00590 libMesh::out << log_string << std::endl; 00591 } 00592 } 00593 00594 00595 00596 void PerfLog::start_event(const std::string &label, 00597 const std::string &header) 00598 { 00599 this->push(label,header); 00600 } 00601 00602 00603 00604 void PerfLog::stop_event(const std::string &label, 00605 const std::string &header) 00606 { 00607 this->pop(label,header); 00608 } 00609 00610 00611 00612 void PerfLog::pause_event(const std::string &, 00613 const std::string &) 00614 { 00615 // nothing to do. pushing the next object on the stack will handle it 00616 } 00617 00618 00619 00620 void PerfLog::restart_event(const std::string &, 00621 const std::string &) 00622 { 00623 // nothing to do. popping the top off the stack will handle it. 00624 } 00625 00626 00627 00628 void PerfLog::split_on_whitespace(const std::string& input, std::vector<std::string>& output) const 00629 { 00630 // Check for easy return 00631 if (input.size()==0) 00632 return; 00633 00634 // Here we hard-code the string to split on, since the algorithm below 00635 // is somewhat specific to it... 00636 const std::string split_on("' '"); 00637 00638 size_t current_pos = 0; 00639 while (true) 00640 { 00641 // Find next end location 00642 size_t end_pos = input.find(split_on, current_pos); 00643 00644 if (end_pos != std::string::npos) 00645 { 00646 // Create substring. Note: the second argument to substr is 00647 // the *length* of string to create, not the ending position! 00648 output.push_back( input.substr(current_pos, end_pos - current_pos + 1) ); 00649 00650 // Update search starting position, make sure to go past the end of the split_on string, but 00651 // include the previous single quote (hence the -1). 00652 current_pos = end_pos + split_on.size() - 1; 00653 } 00654 else 00655 { 00656 // Push back whatever remains of the string onto the output. 00657 // Note that substr with only 1 argument pushes back 00658 // whatever remains of the string. This also handles the 00659 // case where the string does not contain any matches. 00660 output.push_back( input.substr(current_pos) ); 00661 00662 // We are done searching the string, so break out of the while loop 00663 break; 00664 } 00665 } 00666 } 00667 00668 00669 } // namespace libMesh
Site Created By: libMesh Developers
Last modified: February 05 2013 19:54:48 UTC
Hosted By: