LCOV - code coverage report
Current view: top level - include/utils - perf_log.h (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4343 (6ea38c) with base 8c3130 Lines: 76 89 85.4 %
Date: 2026-10-13 21:47:33 Functions: 12 12 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2026 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             : 
      19             : 
      20             : #ifndef LIBMESH_PERFLOG_H
      21             : #define LIBMESH_PERFLOG_H
      22             : 
      23             : 
      24             : // Local includes
      25             : #include "libmesh/libmesh_common.h"
      26             : 
      27             : #include "libmesh/threads.h"
      28             : 
      29             : // C++ includes
      30             : #include <cstddef>
      31             : #include <map>
      32             : #include <stack>
      33             : #include <string>
      34             : #include <vector>
      35             : #include <deque>
      36             : #include <memory>
      37             : #ifdef LIBMESH_HAVE_SYS_TIME_H
      38             : #include <sys/time.h> // gettimeofday() on Unix
      39             : #endif
      40             : #ifndef LIBMESH_HAVE_GETTIMEOFDAY
      41             : #include "libmesh/win_gettimeofday.h" // gettimeofday() on Windows
      42             : #endif
      43             : #ifdef LIBMESH_HAVE_NVTX_API
      44             : #include <nvtx3/nvtx3.hpp>
      45             : #endif
      46             : #ifdef LIBMESH_HAVE_OPENMP
      47             : #include <omp.h>
      48             : #endif
      49             : 
      50             : namespace libMesh
      51             : {
      52             : 
      53             : /**
      54             :  * The \p PerfData class simply contains the performance
      55             :  * data that is recorded for individual events.
      56             :  *
      57             :  * \author Benjamin Kirk
      58             :  * \date 2003
      59             :  * \brief Data object managed by PerfLog
      60             :  */
      61             : class PerfData
      62             : {
      63             : public:
      64             : 
      65             :   /**
      66             :    * Constructor.  Initializes data to be empty.
      67             :    */
      68      119529 :   PerfData () :
      69       64041 :     tot_time(0.),
      70       64041 :     tot_time_incl_sub(0.),
      71       64041 :     tstart(),
      72       64041 :     tstart_incl_sub(),
      73       64041 :     count(0),
      74       64041 :     open(false),
      75      119529 :     called_recursively(0)
      76       36144 :   {}
      77             : 
      78             : 
      79             :   /**
      80             :    * Total time spent in this event.
      81             :    */
      82             :   double tot_time;
      83             : 
      84             :   /**
      85             :    * Total time spent in this event, including sub-events.
      86             :    */
      87             :   double tot_time_incl_sub;
      88             : 
      89             :   /**
      90             :    * Structure defining when the event
      91             :    * was last started.
      92             :    */
      93             :   struct timeval tstart;
      94             : 
      95             :   /**
      96             :    * Structure defining when the event
      97             :    * was last started, including sub-events.
      98             :    */
      99             :   struct timeval tstart_incl_sub;
     100             : 
     101             :   /**
     102             :    * The number of times this event has
     103             :    * been executed
     104             :    */
     105             :   unsigned int count;
     106             : 
     107             :   /**
     108             :    * Flag indicating if we are currently
     109             :    * monitoring this event.  Should only
     110             :    * be true while the event is executing.
     111             :    */
     112             :   bool open;
     113             : 
     114             :   void   start ();
     115             :   void   restart ();
     116             :   double pause ();
     117             :   double pause_for(PerfData & other);
     118             :   double stopit ();
     119             : 
     120             :   /**
     121             :    * Sums timing results from \p other
     122             :    */
     123        1832 :   PerfData & operator += (const PerfData & other)
     124             :   {
     125        1832 :     libmesh_assert(!open);
     126        3664 :     tot_time += other.tot_time;
     127        3664 :     tot_time_incl_sub += other.tot_time_incl_sub;
     128        3664 :     count += other.count;
     129             : 
     130        1832 :     return *this;
     131             :   }
     132             : 
     133             :   int called_recursively;
     134             : 
     135             : protected:
     136             :   double stop_or_pause(const bool do_stop);
     137             : };
     138             : 
     139             : 
     140             : 
     141             : 
     142             : /**
     143             :  * The \p PerfLog class allows monitoring of specific events.
     144             :  * An event is defined by a unique string that functions as
     145             :  * a label.  Each time the event is executed data are recorded.
     146             :  * This class is particularly useful for finding performance
     147             :  * bottlenecks.
     148             :  *
     149             :  * \author Benjamin Kirk
     150             :  * \date 2003
     151             :  * \brief Responsible for timing and summarizing events.
     152             :  */
     153             : class PerfLog
     154             : {
     155             : public:
     156             : 
     157             :   /**
     158             :    * Constructor.  \p label_name is the name of the object, which
     159             :    * will bw print in the log to distinguish it from other objects.
     160             :    * \p log_events is a flag to optionally
     161             :    * disable logging.  You can use this flag to turn off
     162             :    * logging without touching any other code.
     163             :    */
     164             :   PerfLog(std::string label_name="",
     165             :           const bool log_events=true);
     166             : 
     167             :   /**
     168             :    * Destructor. Calls \p clear() and \p print_log().
     169             :    */
     170             :   ~PerfLog();
     171             : 
     172             :   /**
     173             :    * Clears all the internal data and restores the
     174             :    * data structures to a pristine state.  This function
     175             :    * checks to see if it is currently monitoring any
     176             :    * events, and if so errors.  Be sure you are not
     177             :    * logging any events when you call this function.
     178             :    */
     179             :   void clear();
     180             : 
     181             :   /**
     182             :    * Disables performance logging for an active object.
     183             :    */
     184        6092 :   void disable_logging() { log_events = false; }
     185             : 
     186             :   /**
     187             :    * Enables performance logging for an active object.
     188             :    */
     189        6092 :   void enable_logging() { log_events = true; }
     190             : 
     191             :   /**
     192             :    * \returns \p true iff performance logging is enabled
     193             :    */
     194        3048 :   bool logging_enabled() const { return log_events; }
     195             : 
     196             :   /**
     197             :    * Tells the PerfLog to only print log results summarized by header
     198             :    */
     199             :   void enable_summarized_logs() { summarize_logs = true; }
     200             : 
     201             :   /**
     202             :    * Tells the PerfLog to print detailed log results (this is the
     203             :    * default behavior)
     204             :    */
     205             :   void disable_summarized_logs() { summarize_logs = false; }
     206             : 
     207             :   /**
     208             :    * \returns \p true iff log results will be summarized by header
     209             :    */
     210             :   bool summarized_logs_enabled() { return summarize_logs; }
     211             : 
     212             :   /**
     213             :    * Push the event \p label onto the stack, pausing any active event.
     214             :    *
     215             :    * Note - for speed the PerfLog directly considers the *pointers*
     216             :    * here.  Supply pointers to string literals or other character
     217             :    * arrays whose lifetime will exceed the lifetime of the PerfLog
     218             :    * object, not to temporarily allocated arrays.
     219             :    *
     220             :    * Supply \p per_entity = \p true for events that fire once per mesh entity
     221             :    * or more often.  Such events are timed in the performance log but are
     222             :    * left out of trace-style profiler annotations: when the library is built
     223             :    * with NVTX support, every other event also opens an NVTX range.  Per-entity
     224             :    * events would produce large numbers of short ranges in a profiler capture,
     225             :    * which adds recording overhead, enlarges the capture, and crowds the
     226             :    * timeline and NVTX summaries.
     227             :    */
     228             :   void fast_push (const char * label,
     229             :                   const char * header="",
     230             :                   bool per_entity=false);
     231             : 
     232             :   /**
     233             :    * Push the event \p label onto the stack, pausing any active event.
     234             :    *
     235             :    * This method will eventually be deprecated.  For backwards
     236             :    * compatibility, the PerfLog must copy the contents of these
     237             :    * character arrays into strings, which ironically damages the
     238             :    * performance we are trying to profile.  Use fast_push() (with
     239             :    * compatible long-lived character array data) instead.
     240             :    */
     241             :   void push (const char * label,
     242             :              const char * header="");
     243             : 
     244             :   /**
     245             :    * Push the event \p label onto the stack, pausing any active event.
     246             :    *
     247             :    * This method will eventually be deprecated.  String manipulation
     248             :    * is too low performance to use when performance monitoring hot
     249             :    * spots.  Use fast_push() instead.
     250             :    */
     251             :   void push (const std::string & label,
     252             :              const std::string & header="");
     253             : 
     254             :   /**
     255             :    * Pop the event \p label off the stack, resuming any lower event.
     256             :    *
     257             :    * This method must be passed the exact same pointers as were passed
     258             :    * to fast_push, not merely pointers to identical strings.
     259             :    * This method is called from the PerfItem destructor, so it should
     260             :    * not throw. We have therefore marked it noexcept as a reminder.
     261             :    *
     262             :    * \p per_entity must match the value supplied to the corresponding
     263             :    * fast_push(), so that the NVTX range stack stays balanced.
     264             :    */
     265             :   void fast_pop (const char * label,
     266             :                  const char * header="",
     267             :                  bool per_entity=false) noexcept;
     268             : 
     269             :   /**
     270             :    * Pop the event \p label off the stack, resuming any lower event.
     271             :    *
     272             :    * This method will eventually be deprecated.  Use fast_pop() (with
     273             :    * the exact same pointers supplied to fast_push()) instead.
     274             :    */
     275             :   void pop (const char * label,
     276             :             const char * header="");
     277             : 
     278             :   /**
     279             :    * Pop the event \p label off the stack, resuming any lower event.
     280             :    *
     281             :    * This method will eventually be deprecated.  String manipulation
     282             :    * is too low performance to use when performance monitoring hot
     283             :    * spots.  Use fast_pop() instead.
     284             :    */
     285             :   void pop (const std::string & label,
     286             :             const std::string & header="");
     287             : 
     288             :   /**
     289             :    * Start monitoring the event named \p label.
     290             :    */
     291             :   void start_event(const std::string & label,
     292             :                    const std::string & header="");
     293             : 
     294             :   /**
     295             :    * Stop monitoring the event named \p label.
     296             :    */
     297             :   void stop_event(const std::string & label,
     298             :                   const std::string & header="");
     299             : 
     300             :   /**
     301             :    * Suspend monitoring of the event.
     302             :    */
     303             :   void pause_event(const std::string & label,
     304             :                    const std::string & header="");
     305             : 
     306             :   /**
     307             :    * Restart monitoring the event.
     308             :    */
     309             :   void restart_event(const std::string & label,
     310             :                      const std::string & header="");
     311             : 
     312             :   /**
     313             :    * \returns A string containing:
     314             :    * (1) Basic machine information (if first call)
     315             :    * (2) The performance log
     316             :    */
     317             :   std::string get_log() const;
     318             : 
     319             :   /**
     320             :    * \returns A string containing ONLY the information header.
     321             :    */
     322             :   std::string get_info_header() const;
     323             : 
     324             :   /**
     325             :    * \returns A string containing ONLY the log information
     326             :    */
     327             :   std::string get_perf_info() const;
     328             : 
     329             :   /**
     330             :    * Print the log.
     331             :    */
     332             :   void print_log() const;
     333             : 
     334             :   /**
     335             :    * \returns The total time spent on this event.
     336             :    */
     337             :   double get_elapsed_time() const;
     338             : 
     339             :   /**
     340             :    * \returns The active time
     341             :    */
     342             :   double get_active_time() const;
     343             : 
     344             :   /**
     345             :    * Return the PerfData object associated with a label and header.
     346             :    */
     347             :   PerfData get_perf_data(const std::string & label, const std::string & header="");
     348             : 
     349             :   /**
     350             :    * Typdef for the underlying logging data structure.
     351             :    */
     352             :   typedef std::map<std::pair<const char *,
     353             :                              const char *>,
     354             :                    PerfData> log_type;
     355             : 
     356             : private:
     357             : 
     358             : 
     359             :   /**
     360             :    * The label for this object.
     361             :    */
     362             :   const std::string label_name;
     363             : 
     364             :   /**
     365             :    * Flag to optionally disable all logging.
     366             :    */
     367             :   bool log_events;
     368             : 
     369             :   /**
     370             :    * Flag to optionally summarize logs
     371             :    */
     372             :   bool summarize_logs;
     373             : 
     374             :   /**
     375             :    * The total running time for recorded events.
     376             :    */
     377             :   double total_time;
     378             : 
     379             :   /**
     380             :    * The time we were constructed or last cleared.
     381             :    */
     382             :   struct timeval tstart;
     383             : 
     384             :   /**
     385             :    * The actual log.
     386             :    *
     387             :    * An unsorted_map would work fine here and would be asymptotically
     388             :    * faster, but in my tests for our log sizes there was no
     389             :    * improvement.
     390             :    */
     391             :   log_type log;
     392             : 
     393             :   /**
     394             :    * A stack to hold the current performance log trace.
     395             :    */
     396             :   std::stack<PerfData*> log_stack;
     397             : 
     398             :   /**
     399             :    * Flag indicating if print_log() has been called.
     400             :    * This is used to print a header with machine-specific
     401             :    * data the first time that print_log() is called.
     402             :    */
     403             :   static bool called;
     404             : 
     405             :   /**
     406             :    * Splits a string on whitespace into a vector of separate strings.  This is used to make the
     407             :    * LIBMESH_CONFIGURE_INFO a little more manageable.
     408             :    */
     409             :   void split_on_whitespace(const std::string & input,
     410             :                            std::vector<std::string> & output) const;
     411             : 
     412             :   /**
     413             :    * Workaround to give us fixed pointers to character arrays for
     414             :    * every string.  Using std::set instead might work: it won't
     415             :    * invalidate iterators, which I *think* means it doesn't have any
     416             :    * reason to modify or copy their contents or otherwise invalidate
     417             :    * their c_str() pointers... but I can't prove it from the standards
     418             :    * doc, so let's be safe.
     419             :    */
     420             :   std::map<std::string, std::unique_ptr<const char[]>> non_temporary_strings;
     421             : };
     422             : 
     423             : 
     424             : 
     425             : // ------------------------------------------------------------
     426             : // PerfData class member functions
     427             : inline
     428    10434375 : void PerfData::start ()
     429             : {
     430    20288602 :   this->count++;
     431    20288602 :   this->called_recursively++;
     432    20288602 :   gettimeofday (&(this->tstart), nullptr);
     433    20288602 :   this->tstart_incl_sub = this->tstart;
     434    10434375 : }
     435             : 
     436             : 
     437             : 
     438             : inline
     439    46074284 : void PerfData::restart ()
     440             : {
     441    91736263 :   gettimeofday (&(this->tstart), nullptr);
     442    46074686 : }
     443             : 
     444             : 
     445             : 
     446             : inline
     447             : double PerfData::pause ()
     448             : {
     449             :   return this->stop_or_pause(false);
     450             : }
     451             : 
     452             : 
     453             : inline
     454   112028399 : double PerfData::stop_or_pause(const bool do_stop)
     455             : {
     456             :   // save the start times, reuse the structure we have rather than create
     457             :   // a new one.
     458             :   const time_t
     459   112028399 :     tstart_tv_sec  = this->tstart.tv_sec,
     460   112028399 :     tstart_tv_usec = this->tstart.tv_usec;
     461             : 
     462   112028399 :   gettimeofday (&(this->tstart), nullptr);
     463             : 
     464   223524584 :   const double elapsed_time = (static_cast<double>(this->tstart.tv_sec  - tstart_tv_sec) +
     465   112028399 :                                static_cast<double>(this->tstart.tv_usec - tstart_tv_usec)*1.e-6);
     466             : 
     467   112028399 :   this->tot_time += elapsed_time;
     468             : 
     469   112028399 :   if (do_stop)
     470             :     {
     471   223524584 :       const double elapsed_time_incl_sub = (static_cast<double>(this->tstart.tv_sec  - this->tstart_incl_sub.tv_sec) +
     472   112028399 :                                             static_cast<double>(this->tstart.tv_usec - this->tstart_incl_sub.tv_usec)*1.e-6);
     473             : 
     474   112028399 :       this->tot_time_incl_sub += elapsed_time_incl_sub;
     475             :     }
     476             : 
     477   112028399 :   return elapsed_time;
     478             : }
     479             : 
     480             : 
     481             : 
     482             : inline
     483    91920646 : double PerfData::pause_for(PerfData & other)
     484             : {
     485    91920646 :   gettimeofday (&(other.tstart), nullptr);
     486             : 
     487   183840890 :   const double elapsed_time = (static_cast<double>(other.tstart.tv_sec  - this->tstart.tv_sec) +
     488    91920646 :                                static_cast<double>(other.tstart.tv_usec - this->tstart.tv_usec)*1.e-6);
     489    91920646 :   this->tot_time += elapsed_time;
     490             : 
     491    91920646 :   other.count++;
     492    91920646 :   other.called_recursively++;
     493    91920646 :   other.tstart_incl_sub = other.tstart;
     494             : 
     495    91920646 :   return elapsed_time;
     496             : }
     497             : 
     498             : 
     499             : 
     500             : inline
     501    55976847 : double PerfData::stopit ()
     502             : {
     503             :   // stopit is just similar to pause except that it decrements the
     504             :   // recursive call counter
     505             : 
     506   112028399 :   this->called_recursively--;
     507   112028399 :   return this->stop_or_pause(true);
     508             : }
     509             : 
     510             : 
     511             : 
     512             : // ------------------------------------------------------------
     513             : // PerfLog class inline member functions
     514             : inline
     515   132553320 : void PerfLog::fast_push (const char * label,
     516             :                          const char * header,
     517             :                          bool per_entity)
     518             : {
     519             : #ifndef LIBMESH_HAVE_NVTX_API
     520    66145491 :   libmesh_ignore(per_entity);
     521             : #endif
     522             : 
     523   132553320 :   if (this->log_events)
     524             :     {
     525             :       // The global perflog stack may not be thread-safe, but if we're
     526             :       // creating threads we should have disabled it already
     527    55976847 :       libmesh_assert_equal_to(Threads::active_threads, 1);
     528             : #ifdef LIBMESH_HAVE_OPENMP
     529             :       // Users might be doing their own non-libMesh threading.  We
     530             :       // can't catch every case of that but we can catch OpenMP
     531    55976847 :       libmesh_assert_equal_to(omp_get_num_threads(), 1);
     532             : #endif
     533             : 
     534             :       // Get a reference to the event data to avoid
     535             :       // repeated map lookups
     536   224162942 :       PerfData * perf_data = &(log[std::make_pair(header,label)]);
     537             : 
     538   112209248 :       if (!log_stack.empty())
     539    91920646 :         total_time += log_stack.top()->pause_for(*perf_data);
     540             :       else
     541    10434375 :         perf_data->start();
     542    55976847 :       log_stack.push(perf_data);
     543             : 
     544             : #ifdef LIBMESH_HAVE_NVTX_API
     545             :       // This takes a single const char *, and we don't want to waste
     546             :       // time allocating a new string on every push, so for now we'll
     547             :       // drop the header.  Maybe we should allocate a new string in
     548             :       // the PerfData?
     549             :       if (!per_entity)
     550             :         nvtxRangePushA(label);
     551             : #endif
     552             :     }
     553   132553320 : }
     554             : 
     555             : 
     556             : 
     557             : inline
     558   132380135 : void PerfLog::fast_pop(const char * libmesh_dbg_var(label),
     559             :                        const char * libmesh_dbg_var(header),
     560             :                        bool per_entity) noexcept
     561             : {
     562             : #ifndef LIBMESH_HAVE_NVTX_API
     563    66145491 :   libmesh_ignore(per_entity);
     564             : #endif
     565             : 
     566   132380135 :   if (this->log_events)
     567             :     {
     568             :       // The global perflog stack may not be thread-safe, but if we're
     569             :       // creating threads we should have disabled it already
     570    55976847 :       libmesh_exceptionless_assert(Threads::active_threads == 1);
     571             : #ifdef LIBMESH_HAVE_OPENMP
     572             :       // Users might be doing their own non-libMesh threading.  We
     573             :       // can't catch every case of that but we can catch OpenMP.
     574    55976847 :       libmesh_exceptionless_assert(omp_get_num_threads() == 1);
     575             : #endif
     576             : 
     577             : #ifdef LIBMESH_HAVE_NVTX_API
     578             :       if (!per_entity)
     579             :         nvtxRangePop();
     580             : #endif
     581             :       // If there's nothing on the stack, then we can't pop anything. Previously we
     582             :       // asserted that the log_stack was not empty, but we should not throw from
     583             :       // this function, so instead just return in that case.
     584   112028399 :       if (log_stack.empty())
     585           0 :         return;
     586             : 
     587             : #ifndef NDEBUG
     588             :       // In debug mode, we carefully check that before popping the stack,
     589             :       // we have the correct event.
     590             : 
     591             :       // Get pointer to corresponding PerfData, or nullptr if it does not exist.
     592    55976847 :       auto it = log.find(std::make_pair(header, label));
     593    55976847 :       PerfData * perf_data = (it == log.end()) ? nullptr : &(it->second);
     594             : 
     595             :       // If this event is not found in the log, then it could indicate
     596             :       // that you have a STOP_LOG(a,b) without a corresponding
     597             :       // START_LOG(a,b) call. We don't pop anything from the log_stack
     598             :       // in this case.
     599    55976847 :       if (!perf_data)
     600             :         {
     601           0 :           libMesh::err << "PerfLog can't pop (" << header << ',' << label << ')' << std::endl;
     602           0 :           libMesh::err << "No such event was found in the log, you may have a mismatched START/STOP_LOG statement." << std::endl;
     603           0 :           return;
     604             :         }
     605             : 
     606    55976847 :       if (perf_data != log_stack.top())
     607             :         {
     608             :           // We've been asked to pop an event which is not at the
     609             :           // top() of the log_stack. The event may be deeper in the
     610             :           // stack instead, so search for it now. If we find it, we'll
     611             :           // pop all intermediate items off the stack (abandoning any
     612             :           // attempt to time them accurately) and let the optimized
     613             :           // code path take over from there.
     614           0 :           std::deque<PerfData*> tmp_stack;
     615             : 
     616           0 :           while (!log_stack.empty() && perf_data != log_stack.top())
     617             :             {
     618           0 :               tmp_stack.push_front(log_stack.top());
     619           0 :               log_stack.pop();
     620             :             }
     621             : 
     622             :           // If we exhaustively searched the log_stack without finding
     623             :           // the event, we can't do anything else so just put
     624             :           // everything back and return, not doing anything for this
     625             :           // event.
     626           0 :           if (log_stack.empty())
     627             :             {
     628           0 :               while (!tmp_stack.empty())
     629             :                 {
     630           0 :                   log_stack.push(tmp_stack.back());
     631           0 :                   tmp_stack.pop_back();
     632             :                 }
     633             : 
     634           0 :               return;
     635             :             }
     636             :         }
     637             : #endif
     638             : 
     639             :       // In optimized mode, we just pop from the top of the stack and
     640             :       // resume timing the next entry.
     641   112028399 :       total_time += log_stack.top()->stopit();
     642             : 
     643    55976847 :       log_stack.pop();
     644             : 
     645   112028399 :       if (!log_stack.empty())
     646    91736263 :         log_stack.top()->restart();
     647             :     }
     648             : }
     649             : 
     650             : 
     651             : 
     652             : inline
     653             : double PerfLog::get_elapsed_time () const
     654             : {
     655             :   struct timeval tnow;
     656             : 
     657             :   gettimeofday (&tnow, nullptr);
     658             : 
     659             :   const double elapsed_time = (static_cast<double>(tnow.tv_sec  - tstart.tv_sec) +
     660             :                                static_cast<double>(tnow.tv_usec - tstart.tv_usec)*1.e-6);
     661             :   return elapsed_time;
     662             : }
     663             : 
     664             : inline
     665             : double PerfLog::get_active_time() const
     666             : {
     667             :   return total_time;
     668             : }
     669             : 
     670             : } // namespace libMesh
     671             : 
     672             : 
     673             : 
     674             : #endif // LIBMESH_PERFLOG_H

Generated by: LCOV version 1.14