Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 :
4 : // This library is free software; you can redistribute it and/or
5 : // modify it under the terms of the GNU Lesser General Public
6 : // License as published by the Free Software Foundation; either
7 : // version 2.1 of the License, or (at your option) any later version.
8 :
9 : // This library is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : // Lesser General Public License for more details.
13 :
14 : // You should have received a copy of the GNU Lesser General Public
15 : // License along with this library; if not, write to the Free Software
16 : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 :
18 : #include "libmesh/perf_log.h"
19 :
20 : // Local includes
21 : #include "libmesh/int_range.h"
22 : #include "libmesh/timestamp.h"
23 :
24 : // C++ includes
25 : #include <algorithm>
26 : #include <iostream>
27 : #include <iomanip>
28 : #include <cstring>
29 : #include <ctime>
30 : #ifdef LIBMESH_HAVE_UNISTD_H
31 : #include <unistd.h> // for getuid()
32 : #endif
33 : #include <sys/types.h>
34 : #include <vector>
35 : #include <sstream>
36 :
37 : #ifdef LIBMESH_HAVE_SYS_UTSNAME_H
38 : #include <sys/utsname.h>
39 : #endif
40 :
41 : #ifdef LIBMESH_HAVE_PWD_H
42 : #include <pwd.h>
43 : #endif
44 :
45 : namespace libMesh
46 : {
47 :
48 :
49 : // ------------------------------------------------------------
50 : // PerfLog class member functions
51 :
52 : bool PerfLog::called = false;
53 :
54 :
55 318988 : PerfLog::PerfLog(std::string ln,
56 318988 : const bool le) :
57 312672 : label_name(std::move(ln)),
58 305948 : log_events(le),
59 305948 : summarize_logs(false),
60 318988 : total_time(0.)
61 : {
62 318988 : gettimeofday (&tstart, nullptr);
63 :
64 318988 : if (log_events)
65 1773 : this->clear();
66 318988 : }
67 :
68 :
69 :
70 325304 : PerfLog::~PerfLog()
71 : {
72 318988 : if (log_events)
73 1773 : this->print_log();
74 318988 : }
75 :
76 :
77 :
78 18221 : void PerfLog::clear()
79 : {
80 18221 : if (log_events)
81 : {
82 : // check that all events are closed
83 87103 : for (auto pos : log)
84 84339 : libmesh_error_msg_if(pos.second.open,
85 : "ERROR clearing performance log for class "
86 : << label_name
87 : << "\nevent "
88 : << pos.first.second
89 : << " is still being monitored!");
90 :
91 2764 : gettimeofday (&tstart, nullptr);
92 :
93 948 : log.clear();
94 :
95 2764 : while (!log_stack.empty())
96 0 : log_stack.pop();
97 : }
98 18221 : }
99 :
100 :
101 :
102 655907 : void PerfLog::push (const std::string & label,
103 : const std::string & header)
104 : {
105 : const char * label_c_str;
106 : const char * header_c_str;
107 :
108 655907 : if (const auto label_it = non_temporary_strings.find(label);
109 50601 : label_it != non_temporary_strings.end())
110 48503 : label_c_str = label_it->second.get();
111 : else
112 : {
113 54312 : const std::size_t labelsizep1 = label.size()+1;
114 54312 : auto newcopy = std::make_unique<char[]>(labelsizep1);
115 54312 : std::strncpy(newcopy.get(), label.c_str(), labelsizep1);
116 2098 : label_c_str = newcopy.get();
117 54312 : non_temporary_strings[label] = std::move(newcopy);
118 : }
119 :
120 655907 : if (const auto header_it = non_temporary_strings.find(header);
121 50601 : header_it != non_temporary_strings.end())
122 49679 : header_c_str = header_it->second.get();
123 : else
124 : {
125 16433 : const std::size_t headersizep1 = header.size()+1;
126 16433 : auto newcopy = std::make_unique<char[]>(headersizep1);
127 16433 : std::strncpy(newcopy.get(), header.c_str(), headersizep1);
128 922 : header_c_str = newcopy.get();
129 16433 : non_temporary_strings[header] = std::move(newcopy);
130 : }
131 :
132 655907 : if (this->log_events)
133 493331 : this->fast_push(label_c_str, header_c_str);
134 655907 : }
135 :
136 :
137 :
138 350881 : void PerfLog::push (const char * label,
139 : const char * header)
140 : {
141 660281 : this->push(std::string(label), std::string(header));
142 350881 : }
143 :
144 :
145 :
146 :
147 :
148 655907 : void PerfLog::pop (const std::string & label,
149 : const std::string & header)
150 : {
151 :
152 655907 : const char * label_c_str = non_temporary_strings[label].get();
153 655907 : const char * header_c_str = non_temporary_strings[header].get();
154 :
155 : // This could happen if users are *mixing* string and char* APIs for
156 : // the same label/header combination. For perfect backwards
157 : // compatibility we should handle that, but there's just no fast way
158 : // to do so.
159 50601 : libmesh_assert(label_c_str);
160 50601 : libmesh_assert(header_c_str);
161 :
162 655907 : if (this->log_events)
163 493331 : this->fast_pop(label_c_str, header_c_str);
164 655907 : }
165 :
166 :
167 :
168 350881 : void PerfLog::pop (const char * label,
169 : const char * header)
170 : {
171 660281 : this->pop(std::string(label), std::string(header));
172 350881 : }
173 :
174 :
175 :
176 1552 : std::string PerfLog::get_info_header() const
177 : {
178 2400 : std::ostringstream oss;
179 :
180 1552 : if (log_events)
181 : {
182 1976 : std::string date = Utility::get_timestamp();
183 :
184 : #ifdef LIBMESH_HAVE_SYS_UTSNAME_H
185 : // Get system information
186 : struct utsname sysInfo;
187 1552 : uname(&sysInfo);
188 : #endif
189 :
190 : // Get user information
191 : //
192 : // Some systems, for example Crays, actually have getpwuid on the head-node
193 : // but (if I understand correctly) a dynamically-linked glibc is not available
194 : // on the backend, which is running a reduced operating system like Compute
195 : // Node Linux. Thus functions like getpwuid cannot be called. This makes
196 : // automatically testing for the existence of getpwuid on the login node
197 : // difficult. The configure test would work on the login node but will fail
198 : // on the backend. Hence we have added a configure flag, --disable-getpwuid,
199 : // to manually turn this off.
200 : #ifdef LIBMESH_HAVE_GETPWUID
201 1552 : struct passwd * p = getpwuid(getuid());
202 : #endif
203 1552 : oss << "\n";
204 :
205 : // Construct string stream objects for each of the outputs
206 : std::ostringstream
207 2400 : pid_stream,
208 2400 : nprocs_stream,
209 2400 : time_stream,
210 2400 : os_stream,
211 2400 : host_stream,
212 2400 : osrel_stream,
213 2400 : osver_stream,
214 2400 : machine_stream,
215 2400 : user_stream;
216 :
217 :
218 : // Put pointers to these streams in a vector
219 848 : std::vector<std::ostringstream*> v;
220 1552 : v.push_back(&pid_stream);
221 1552 : v.push_back(&nprocs_stream);
222 1552 : v.push_back(&time_stream);
223 1552 : v.push_back(&os_stream);
224 1552 : v.push_back(&host_stream);
225 1552 : v.push_back(&osrel_stream);
226 1552 : v.push_back(&osver_stream);
227 1552 : v.push_back(&machine_stream);
228 1552 : v.push_back(&user_stream);
229 :
230 : // Fill string stream objects
231 1552 : if (libMesh::global_n_processors() > 1)
232 : {
233 1109 : pid_stream << "| Processor id: " << libMesh::global_processor_id();
234 1109 : nprocs_stream << "| Num Processors: " << libMesh::global_n_processors();
235 : }
236 :
237 1552 : time_stream << "| Time: ";
238 1552 : os_stream << "| OS: ";
239 1552 : host_stream << "| HostName: ";
240 1552 : osrel_stream << "| OS Release: ";
241 1552 : osver_stream << "| OS Version: ";
242 1552 : machine_stream << "| Machine: ";
243 :
244 424 : time_stream << date;
245 : #ifdef LIBMESH_HAVE_SYS_UTSNAME_H
246 1552 : os_stream << sysInfo.sysname ;
247 1552 : host_stream << sysInfo.nodename ;
248 1552 : osrel_stream << sysInfo.release ;
249 1552 : osver_stream << sysInfo.version ;
250 1552 : machine_stream << sysInfo.machine ;
251 : #else
252 : os_stream << "Unknown";
253 : host_stream << "Unknown";
254 : osrel_stream << "Unknown";
255 : osver_stream << "Unknown";
256 : machine_stream << "Unknown";
257 :
258 : #endif
259 1552 : user_stream << "| Username: ";
260 : #ifdef LIBMESH_HAVE_GETPWUID
261 1552 : if (p && p->pw_name)
262 1552 : user_stream << p->pw_name;
263 : else
264 : #endif
265 0 : user_stream << "Unknown";
266 :
267 : // Parse the LIBMESH_CONFIGURE_INFO string literal before using it in PerfLog output
268 1976 : std::string libmesh_configure_info(LIBMESH_CONFIGURE_INFO);
269 848 : std::vector<std::string> parsed_libmesh_configure_info;
270 1552 : this->split_on_whitespace(libmesh_configure_info,
271 : parsed_libmesh_configure_info);
272 :
273 : // There should always be at at least one entry in
274 : // parsed_libmesh_configure_info, even if the user just ran
275 : // ../configure.
276 424 : libmesh_assert_greater (parsed_libmesh_configure_info.size(), 0);
277 :
278 : // Find the longest string in all the streams
279 424 : unsigned int max_length = 0;
280 15520 : for (auto & v_i : v)
281 17784 : if (v_i->str().size() > max_length)
282 : max_length = cast_int<unsigned int>
283 11292 : (v_i->str().size());
284 :
285 : // Find the longest string in the parsed_libmesh_configure_info
286 28134 : for (auto & plci_i : parsed_libmesh_configure_info)
287 26582 : if (plci_i.size() > max_length)
288 0 : max_length = cast_int<unsigned int> (plci_i.size());
289 :
290 : // Print dashed line for the header
291 : oss << ' '
292 1976 : << std::string(max_length+1, '-')
293 3104 : << '\n';
294 :
295 : // Loop over all the strings and add end formatting
296 15520 : for (auto & v_i : v)
297 : {
298 24120 : if (v_i->str().size())
299 27860 : oss << v_i->str()
300 : << std::setw (cast_int<int>
301 27860 : (max_length + 4 - v_i->str().size()))
302 3816 : << std::right
303 13930 : << "|\n";
304 : }
305 :
306 : // Print out configuration header plus first parsed string. The
307 : // magic number 18 below accounts for the length of the word
308 : // 'Configuration'.
309 : oss << "| Configuration: "
310 848 : << parsed_libmesh_configure_info[0]
311 : << std::setw (cast_int<int>
312 2400 : (max_length + 4 -
313 2256 : parsed_libmesh_configure_info[0].size() - 18))
314 424 : << std::right
315 1552 : << "|\n";
316 :
317 : // Loop over the parsed_libmesh_configure_info and add end formatting. The magic
318 : // number 3 below accounts for the leading 'pipe' character and indentation
319 26582 : for (auto i : IntRange<std::size_t>(1, parsed_libmesh_configure_info.size()))
320 : {
321 : oss << "| "
322 14416 : << parsed_libmesh_configure_info[i]
323 : << std::setw (cast_int<int>
324 32238 : (max_length + 4 -
325 42852 : parsed_libmesh_configure_info[i].size() - 3))
326 7208 : << std::right
327 25030 : << "|\n";
328 : }
329 :
330 :
331 : // Print dashed line
332 : oss << ' '
333 1976 : << std::string(max_length+1, '-')
334 3104 : << '\n';
335 1408 : }
336 :
337 1976 : return oss.str();
338 704 : }
339 :
340 :
341 :
342 :
343 1600 : std::string PerfLog::get_perf_info() const
344 : {
345 2496 : std::ostringstream oss;
346 :
347 1600 : if (!log_events || log.empty())
348 0 : return oss.str();
349 :
350 : // Stop timing for this event.
351 : struct timeval tstop;
352 :
353 1600 : gettimeofday (&tstop, nullptr);
354 :
355 2496 : const double elapsed_time = (static_cast<double>(tstop.tv_sec - tstart.tv_sec) +
356 1600 : static_cast<double>(tstop.tv_usec - tstart.tv_usec)*1.e-6);
357 :
358 : // Figure out the formatting required based on the event names
359 : // Unsigned ints for each of the column widths
360 448 : unsigned int event_col_width = 30;
361 448 : const unsigned int ncalls_col_width = 11;
362 448 : const unsigned int tot_time_col_width = 12;
363 448 : const unsigned int avg_time_col_width = 12;
364 448 : const unsigned int tot_time_incl_sub_col_width = 12;
365 448 : const unsigned int avg_time_incl_sub_col_width = 12;
366 448 : const unsigned int pct_active_col_width = 9;
367 448 : const unsigned int pct_active_incl_sub_col_width = 9;
368 :
369 : // Reset the event column width based on the longest event name plus
370 : // a possible 2-character indentation, plus a space.
371 45370 : for (auto pos : log)
372 43770 : if (std::strlen(pos.first.second)+3 > event_col_width)
373 : event_col_width = cast_int<unsigned int>
374 2140 : (std::strlen(pos.first.second)+3);
375 :
376 : // Set the total width of the column
377 1600 : const unsigned int total_col_width =
378 : event_col_width +
379 : ncalls_col_width +
380 : tot_time_col_width +
381 : avg_time_col_width +
382 : tot_time_incl_sub_col_width +
383 : avg_time_incl_sub_col_width +
384 : pct_active_col_width+
385 : pct_active_incl_sub_col_width+1;
386 :
387 : // Print dashed line
388 : oss << ' '
389 1600 : << std::string(total_col_width, '-')
390 3200 : << '\n';
391 :
392 : {
393 : // Construct temporary message string
394 2048 : std::ostringstream temp;
395 1856 : temp << "| " << label_name << " Performance: Alive time=" << elapsed_time
396 1600 : << ", Active time=" << total_time;
397 :
398 : // Get the size of the temporary string
399 : const unsigned int temp_size = cast_int<unsigned int>
400 2048 : (temp.str().size());
401 :
402 : // Send the temporary message to the output
403 1152 : oss << temp.str();
404 :
405 : // If this string is longer than the previously computed total
406 : // column width, skip the additional formatting... this shouldn't
407 : // happen often, hopefully. Add two additional characters for a
408 : // space and a "|" character at the end.
409 1600 : if (temp_size < total_col_width+2)
410 1600 : oss << std::setw(total_col_width - temp_size + 2)
411 448 : << std::right
412 1600 : << "|";
413 :
414 1600 : oss << '\n';
415 704 : }
416 :
417 : // Print dashed line
418 : oss << ' '
419 2048 : << std::string(total_col_width, '-')
420 3200 : << '\n';
421 :
422 :
423 : // Write out the header for the events listing
424 : oss << "| "
425 1600 : << std::setw(event_col_width)
426 448 : << std::left
427 : << "Event"
428 1152 : << std::setw(ncalls_col_width)
429 448 : << std::left
430 : << "nCalls"
431 1152 : << std::setw(tot_time_col_width)
432 448 : << std::left
433 : << "Total Time"
434 1152 : << std::setw(avg_time_col_width)
435 448 : << std::left
436 : << "Avg Time"
437 1152 : << std::setw(tot_time_incl_sub_col_width)
438 448 : << std::left
439 : << "Total Time"
440 1152 : << std::setw(avg_time_incl_sub_col_width)
441 448 : << std::left
442 : << "Avg Time"
443 1152 : << std::setw(pct_active_col_width+pct_active_incl_sub_col_width)
444 448 : << std::left
445 : << "% of Active Time"
446 : << "|\n"
447 : << "| "
448 1152 : << std::setw(event_col_width)
449 448 : << std::left
450 : << ""
451 1152 : << std::setw(ncalls_col_width)
452 448 : << std::left
453 : << ""
454 1152 : << std::setw(tot_time_col_width)
455 448 : << std::left
456 : << "w/o Sub"
457 1152 : << std::setw(avg_time_col_width)
458 448 : << std::left
459 : << "w/o Sub"
460 1152 : << std::setw(tot_time_incl_sub_col_width)
461 448 : << std::left
462 : << "With Sub"
463 1152 : << std::setw(avg_time_incl_sub_col_width)
464 448 : << std::left
465 : << "With Sub"
466 1152 : << std::setw(pct_active_col_width)
467 448 : << std::left
468 : << "w/o S"
469 1152 : << std::setw(pct_active_incl_sub_col_width)
470 448 : << std::left
471 : << "With S"
472 : << "|\n|"
473 1600 : << std::string(total_col_width, '-')
474 : << "|\n|"
475 2048 : << std::string(total_col_width, ' ')
476 3904 : << "|\n";
477 :
478 448 : unsigned int summed_function_calls = 0;
479 448 : double summed_total_time = 0;
480 448 : double summed_percentage = 0;
481 :
482 2048 : std::string last_header("");
483 :
484 : // Make a new log to sort entries alphabetically
485 896 : std::map<std::pair<std::string, std::string>, PerfData> string_log;
486 :
487 45370 : for (auto char_data : log)
488 43770 : if (summarize_logs)
489 : {
490 4146 : string_log[std::make_pair(std::string(), char_data.first.first)] +=
491 1382 : char_data.second;
492 : }
493 : else
494 : {
495 21463 : string_log[std::make_pair(char_data.first.first,
496 64237 : char_data.first.second)] =
497 : char_data.second;
498 : }
499 :
500 43006 : for (auto pos : string_log)
501 : {
502 21571 : const PerfData & perf_data = pos.second;
503 :
504 : // Only print the event if the count is non-zero.
505 41406 : if (perf_data.count != 0)
506 : {
507 21571 : const unsigned int perf_count = perf_data.count;
508 41406 : const double perf_time = perf_data.tot_time;
509 41406 : const double perf_avg_time = perf_time / static_cast<double>(perf_count);
510 41406 : const double perf_time_incl_sub = perf_data.tot_time_incl_sub;
511 41406 : const double perf_avg_time_incl_sub = perf_time_incl_sub / static_cast<double>(perf_count);
512 41406 : const double perf_percent = (total_time != 0.) ? perf_time / total_time * 100. : 0.;
513 41406 : const double perf_percent_incl_sub = (total_time != 0.) ? perf_time_incl_sub / total_time * 100. : 0.;
514 :
515 41406 : summed_function_calls += perf_count;
516 41406 : summed_total_time += perf_time;
517 41406 : summed_percentage += perf_percent;
518 :
519 : // Print the event name
520 41406 : if (pos.first.first == "")
521 : oss << "| "
522 2148 : << std::setw(event_col_width)
523 304 : << std::left
524 304 : << pos.first.second;
525 :
526 : else
527 : {
528 38954 : if (last_header != pos.first.first)
529 : {
530 7949 : last_header = pos.first.first;
531 :
532 : // print blank line followed by header name
533 : // (account for additional space before the
534 : // header)
535 : oss << "|"
536 23433 : << std::string(total_col_width, ' ')
537 : << "|\n| "
538 23433 : << std::setw(total_col_width-1)
539 7949 : << std::left
540 : << pos.first.first
541 15484 : << "|\n";
542 : }
543 :
544 : oss << "| "
545 38954 : << std::setw(event_col_width-2)
546 21267 : << std::left
547 21267 : << pos.first.second;
548 : }
549 :
550 :
551 : // Print the number of calls to the event.
552 21571 : oss << std::setw(ncalls_col_width)
553 21571 : << perf_count;
554 :
555 : // Save the original stream flags
556 39562 : std::ios_base::fmtflags out_flags = oss.flags();
557 :
558 : // Print the total time spent in the event
559 21571 : oss << std::fixed
560 : << std::setprecision(4)
561 21571 : << std::setw(tot_time_col_width)
562 21571 : << std::left
563 21571 : << perf_time;
564 :
565 :
566 : // Print the average time per function call
567 21571 : oss << std::fixed
568 : << std::setprecision(6)
569 21571 : << std::setw(avg_time_col_width)
570 21571 : << std::left
571 21571 : << perf_avg_time;
572 :
573 : // Print the total time spent in the event incl. sub-events
574 21571 : oss << std::fixed
575 : << std::setprecision(4)
576 21571 : << std::setw(tot_time_incl_sub_col_width)
577 21571 : << std::left
578 21571 : << perf_time_incl_sub;
579 :
580 : // Print the average time per function call incl. sub-events
581 21571 : oss << std::fixed
582 : << std::setprecision(6)
583 21571 : << std::setw(avg_time_incl_sub_col_width)
584 21571 : << std::left
585 21571 : << perf_avg_time_incl_sub;
586 :
587 : // Print the percentage of the time spent in the event
588 21571 : oss << std::fixed
589 : << std::setprecision(2)
590 21571 : << std::setw(pct_active_col_width)
591 21571 : << std::left
592 21571 : << perf_percent;
593 :
594 : // Print the percentage of the time spent in the event incl. sub-events
595 21571 : oss << std::fixed
596 : << std::setprecision(2)
597 21571 : << std::setw(pct_active_incl_sub_col_width)
598 21571 : << std::left
599 21571 : << perf_percent_incl_sub;
600 :
601 : // Reset the stream flags
602 21571 : oss.flags(out_flags);
603 :
604 41406 : oss << "|\n";
605 : }
606 : }
607 :
608 : oss << ' '
609 1600 : << std::string(total_col_width, '-')
610 : << "\n| "
611 2752 : << std::setw(event_col_width)
612 448 : << std::left
613 1600 : << "Totals:";
614 :
615 : // Print the total number of logged function calls
616 : // For routines which are called many times, summed_function_calls may
617 : // exceed 7 digits. If this happens use, scientific notation.
618 1600 : if (summed_function_calls < 9999999)
619 448 : oss << std::setw(ncalls_col_width)
620 448 : << summed_function_calls;
621 :
622 : else
623 : {
624 : // Save the original stream flags
625 0 : std::ios_base::fmtflags out_flags = oss.flags();
626 :
627 0 : oss << std::scientific
628 : << std::setprecision(3)
629 0 : << std::setw(ncalls_col_width)
630 0 : << std::left
631 0 : << static_cast<Real>(summed_function_calls);
632 :
633 : // Reset the stream flags
634 0 : oss.flags(out_flags);
635 : }
636 :
637 : // Print the total time spent in logged function calls. Don't bother saving/restoring
638 : // the flags here since we are almost done with this stream anyway...
639 448 : oss << std::fixed
640 : << std::setprecision(4)
641 448 : << std::setw(tot_time_col_width)
642 448 : << std::left
643 448 : << summed_total_time;
644 :
645 : // Null, the average time doesn't make sense as a total
646 1600 : oss << std::setw(avg_time_col_width) << "";
647 :
648 : // Same for times that include sub-events
649 : oss << std::setw(tot_time_incl_sub_col_width)
650 : << ""
651 : << std::setw(avg_time_incl_sub_col_width)
652 1600 : << "";
653 :
654 : // Print the total percentage followed by dashed line
655 448 : oss << std::fixed
656 : << std::setprecision(2)
657 448 : << std::setw(pct_active_col_width)
658 448 : << std::left
659 448 : << summed_percentage
660 : << std::setw(pct_active_incl_sub_col_width)
661 : << ""
662 : << "|\n "
663 2048 : << std::string(total_col_width, '-')
664 2752 : << '\n';
665 :
666 448 : return oss.str();
667 704 : }
668 :
669 :
670 :
671 2697 : std::string PerfLog::get_log() const
672 : {
673 4593 : std::ostringstream oss;
674 :
675 2697 : if (log_events)
676 : {
677 : // Only print the log
678 : // if it isn't empty
679 2697 : if (!log.empty())
680 : {
681 : // Possibly print machine info,
682 : // but only do this once
683 1600 : if (!called)
684 : {
685 1552 : called = true;
686 2680 : oss << get_info_header();
687 : }
688 2752 : oss << get_perf_info();
689 : }
690 : }
691 :
692 3645 : return oss.str();
693 801 : }
694 :
695 :
696 :
697 18154 : void PerfLog::print_log() const
698 : {
699 18154 : if (log_events)
700 : {
701 : // Check to see if the log_string is empty, and if so,
702 : // avoid printing an unnecessary newline.
703 3645 : std::string log_string = this->get_log();
704 2697 : if (log_string.size() > 0)
705 448 : libMesh::out << log_string << std::endl;
706 : }
707 18154 : }
708 :
709 0 : PerfData PerfLog::get_perf_data(const std::string & label, const std::string & header)
710 : {
711 0 : if (non_temporary_strings.count(label) &&
712 0 : non_temporary_strings.count(header))
713 : {
714 0 : const char * label_c_str = non_temporary_strings[label].get();
715 0 : const char * header_c_str = non_temporary_strings[header].get();
716 0 : return log[std::make_pair(header_c_str, label_c_str)];
717 : }
718 :
719 : auto iter = std::find_if
720 0 : (log.begin(), log.end(),
721 0 : [&label, &header] (log_type::const_reference a)
722 : {
723 : return
724 0 : !std::strcmp(header.c_str(), a.first.first) &&
725 0 : !std::strcmp(label.c_str(), a.first.second);
726 0 : });
727 :
728 0 : libmesh_assert(iter != log.end());
729 :
730 0 : return iter->second;
731 : }
732 :
733 0 : void PerfLog::start_event(const std::string & label,
734 : const std::string & header)
735 : {
736 0 : this->push(label,header);
737 0 : }
738 :
739 :
740 :
741 0 : void PerfLog::stop_event(const std::string & label,
742 : const std::string & header)
743 : {
744 0 : this->pop(label,header);
745 0 : }
746 :
747 :
748 :
749 0 : void PerfLog::pause_event(const std::string &,
750 : const std::string &)
751 : {
752 : // nothing to do. pushing the next object on the stack will handle it
753 0 : }
754 :
755 :
756 :
757 0 : void PerfLog::restart_event(const std::string &,
758 : const std::string &)
759 : {
760 : // nothing to do. popping the top off the stack will handle it.
761 0 : }
762 :
763 :
764 :
765 1552 : void PerfLog::split_on_whitespace(const std::string & input, std::vector<std::string> & output) const
766 : {
767 : // Check for easy return
768 1552 : if (input.size()==0)
769 0 : return;
770 :
771 : // Here we hard-code the string to split on, since the algorithm below
772 : // is somewhat specific to it...
773 1976 : const std::string split_on("' '");
774 :
775 424 : size_t current_pos = 0;
776 : while (true)
777 : {
778 : // Find next end location
779 26582 : if (size_t end_pos = input.find(split_on, current_pos);
780 : end_pos != std::string::npos)
781 : {
782 : // Create substring. Note: the second argument to substr is
783 : // the *length* of string to create, not the ending position!
784 35644 : output.push_back( input.substr(current_pos, end_pos - current_pos + 1) );
785 :
786 : // Update search starting position, make sure to go past the end of the split_on string, but
787 : // include the previous single quote (hence the -1).
788 25030 : current_pos = end_pos + split_on.size() - 1;
789 : }
790 : else
791 : {
792 : // Push back whatever remains of the string onto the output.
793 : // Note that substr with only 1 argument pushes back
794 : // whatever remains of the string. This also handles the
795 : // case where the string does not contain any matches.
796 2680 : output.push_back( input.substr(current_pos) );
797 :
798 : // We are done searching the string, so break out of the while loop
799 424 : break;
800 : }
801 25030 : }
802 : }
803 :
804 : } // namespace libMesh
|