$extrastylesheet
00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2014 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 libmesh_error_msg("ERROR clearning performance log for class " \ 00076 << label_name \ 00077 << "\nevent " \ 00078 << pos->first.second \ 00079 << " is still being monitored!"); 00080 00081 gettimeofday (&tstart, NULL); 00082 00083 log.clear(); 00084 00085 while (!log_stack.empty()) 00086 log_stack.pop(); 00087 } 00088 } 00089 00090 00091 std::string PerfLog::get_info_header() const 00092 { 00093 std::ostringstream oss; 00094 00095 if (log_events) 00096 { 00097 std::string date = Utility::get_timestamp(); 00098 00099 // Get system information 00100 struct utsname sysInfo; 00101 uname(&sysInfo); 00102 00103 // Get user information 00104 // 00105 // Some systems, for example Crays, actually have getpwuid on the head-node 00106 // but (if I understand correctly) a dynamically-linked glibc is not available 00107 // on the backend, which is running a reduced operating system like Compute 00108 // Node Linux. Thus functions like getpwuid cannot be called. This makes 00109 // automatically testing for the existence of getpwuid on the login node 00110 // difficult. The configure test would work on the login node but will fail 00111 // on the backend. Hence we have added a configure flag, --disable-getpwuid, 00112 // to manually turn this off. 00113 #ifdef LIBMESH_HAVE_GETPWUID 00114 struct passwd* p = getpwuid(getuid()); 00115 #endif 00116 oss << "\n"; 00117 00118 // Construct string stream objects for each of the outputs 00119 std::ostringstream 00120 pid_stream, 00121 nprocs_stream, 00122 time_stream, 00123 os_stream, 00124 host_stream, 00125 osrel_stream, 00126 osver_stream, 00127 machine_stream, 00128 user_stream; 00129 00130 00131 // Put pointers to these streams in a vector 00132 std::vector<std::ostringstream*> v; 00133 v.push_back(&pid_stream); 00134 v.push_back(&nprocs_stream); 00135 v.push_back(&time_stream); 00136 v.push_back(&os_stream); 00137 v.push_back(&host_stream); 00138 v.push_back(&osrel_stream); 00139 v.push_back(&osver_stream); 00140 v.push_back(&machine_stream); 00141 v.push_back(&user_stream); 00142 00143 // Fill string stream objects 00144 if (libMesh::global_n_processors() > 1) 00145 { 00146 pid_stream << "| Processor id: " << libMesh::global_processor_id(); 00147 nprocs_stream << "| Num Processors: " << libMesh::global_n_processors(); 00148 } 00149 00150 time_stream << "| Time: " << date ; 00151 os_stream << "| OS: " << sysInfo.sysname ; 00152 host_stream << "| HostName: " << sysInfo.nodename ; 00153 osrel_stream << "| OS Release: " << sysInfo.release ; 00154 osver_stream << "| OS Version: " << sysInfo.version ; 00155 machine_stream << "| Machine: " << sysInfo.machine ; 00156 user_stream << "| Username: "; 00157 #ifdef LIBMESH_HAVE_GETPWUID 00158 if (p && p->pw_name) 00159 user_stream << p->pw_name; 00160 else 00161 #endif 00162 user_stream << "Unknown"; 00163 00164 // Parse the LIBMESH_CONFIGURE_INFO string literal before using it in PerfLog output 00165 std::string libmesh_configure_info(LIBMESH_CONFIGURE_INFO); 00166 std::vector<std::string> parsed_libmesh_configure_info; 00167 this->split_on_whitespace(libmesh_configure_info, 00168 parsed_libmesh_configure_info); 00169 00170 // There should always be at at least one entry in 00171 // parsed_libmesh_configure_info, even if the user just ran 00172 // ../configure. 00173 libmesh_assert_greater (parsed_libmesh_configure_info.size(), 0); 00174 00175 // Find the longest string in all the streams 00176 unsigned int max_length = 0; 00177 for (unsigned int i=0; i<v.size(); ++i) 00178 if (v[i]->str().size() > max_length) 00179 max_length = cast_int<unsigned int> 00180 (v[i]->str().size()); 00181 00182 // Find the longest string in the parsed_libmesh_configure_info 00183 for (unsigned i=0; i<parsed_libmesh_configure_info.size(); ++i) 00184 if (parsed_libmesh_configure_info[i].size() > max_length) 00185 max_length = cast_int<unsigned int> 00186 (parsed_libmesh_configure_info[i].size()); 00187 00188 // Print dashed line for the header 00189 oss << ' ' 00190 << std::string(max_length+1, '-') 00191 << '\n'; 00192 00193 // Loop over all the strings and add end formatting 00194 for (unsigned int i=0; i<v.size(); ++i) 00195 { 00196 if (v[i]->str().size()) 00197 oss << v[i]->str() 00198 << std::setw (cast_int<int> 00199 (max_length + 4 - v[i]->str().size())) 00200 << std::right 00201 << "|\n"; 00202 } 00203 00204 // Print out configuration header plus first parsed string. The 00205 // magic number 18 below accounts for the length of the word 00206 // 'Configuration'. 00207 oss << "| Configuration: " 00208 << parsed_libmesh_configure_info[0] 00209 << std::setw (cast_int<int> 00210 (max_length + 4 - 00211 parsed_libmesh_configure_info[0].size() - 18)) 00212 << std::right 00213 << "|\n"; 00214 00215 // Loop over the parsed_libmesh_configure_info and add end formatting. The magic 00216 // number 3 below accounts for the leading 'pipe' character and indentation 00217 for (unsigned i=1; i<parsed_libmesh_configure_info.size(); ++i) 00218 { 00219 oss << "| " 00220 << parsed_libmesh_configure_info[i] 00221 << std::setw (cast_int<int> 00222 (max_length + 4 - 00223 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 = 11; 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 = 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 = 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 PerfData PerfLog::get_perf_data(const std::string &label, const std::string &header) 00595 { 00596 return log[std::make_pair(header, label)]; 00597 } 00598 00599 void PerfLog::start_event(const std::string &label, 00600 const std::string &header) 00601 { 00602 this->push(label,header); 00603 } 00604 00605 00606 00607 void PerfLog::stop_event(const std::string &label, 00608 const std::string &header) 00609 { 00610 this->pop(label,header); 00611 } 00612 00613 00614 00615 void PerfLog::pause_event(const std::string &, 00616 const std::string &) 00617 { 00618 // nothing to do. pushing the next object on the stack will handle it 00619 } 00620 00621 00622 00623 void PerfLog::restart_event(const std::string &, 00624 const std::string &) 00625 { 00626 // nothing to do. popping the top off the stack will handle it. 00627 } 00628 00629 00630 00631 void PerfLog::split_on_whitespace(const std::string& input, std::vector<std::string>& output) const 00632 { 00633 // Check for easy return 00634 if (input.size()==0) 00635 return; 00636 00637 // Here we hard-code the string to split on, since the algorithm below 00638 // is somewhat specific to it... 00639 const std::string split_on("' '"); 00640 00641 size_t current_pos = 0; 00642 while (true) 00643 { 00644 // Find next end location 00645 size_t end_pos = input.find(split_on, current_pos); 00646 00647 if (end_pos != std::string::npos) 00648 { 00649 // Create substring. Note: the second argument to substr is 00650 // the *length* of string to create, not the ending position! 00651 output.push_back( input.substr(current_pos, end_pos - current_pos + 1) ); 00652 00653 // Update search starting position, make sure to go past the end of the split_on string, but 00654 // include the previous single quote (hence the -1). 00655 current_pos = end_pos + split_on.size() - 1; 00656 } 00657 else 00658 { 00659 // Push back whatever remains of the string onto the output. 00660 // Note that substr with only 1 argument pushes back 00661 // whatever remains of the string. This also handles the 00662 // case where the string does not contain any matches. 00663 output.push_back( input.substr(current_pos) ); 00664 00665 // We are done searching the string, so break out of the while loop 00666 break; 00667 } 00668 } 00669 } 00670 00671 } // namespace libMesh