]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Use std::chrono types in the Timer class.
authorDavid Wells <wellsd2@rpi.edu>
Thu, 24 Aug 2017 00:09:07 +0000 (20:09 -0400)
committerDavid Wells <wellsd2@rpi.edu>
Thu, 24 Aug 2017 16:45:28 +0000 (12:45 -0400)
This commit does two things:
1. The CPU time calculating function has been moved to a std::chrono-compliant
   clock class.
2. The various time measurements stored as private members of Timer have been
   reorganized into two structs of type ClockMeasurements. The new, equivalent
   fields use std::chrono types instead of double precision numbers.

include/deal.II/base/timer.h
source/base/timer.cc

index 71c227d31fab3a94b664bf84baae4e7b05dc83dc..8e1d211c6cfca4e64aec314306df577b4ebf7811 100644 (file)
 #include <deal.II/base/thread_management.h>
 #include <deal.II/base/utilities.h>
 
-#include <string>
+#include <chrono>
 #include <list>
 #include <map>
+#include <string>
 
 DEAL_II_NAMESPACE_OPEN
 
+/**
+ * A clock, compatible with the <code>std::chrono</code> notion of a clock,
+ * whose now() method returns a time point indicating the amount of CPU time
+ * that the current process has used.
+ */
+struct CPUClock
+{
+  /**
+   * Duration type. Windows measures CPU times in units of 100 nanoseconds
+   * and POSIX uses microseconds, so go with microseconds for uniformity.
+   */
+  typedef std::chrono::microseconds duration;
+
+  /**
+   * Signed integral type used to store the value returned by count().
+   */
+  typedef duration::rep rep;
+
+  /**
+   * Ratio representing the length of a period (in seconds).
+   */
+  typedef duration::period period;
+
+  /**
+   * Time point type.
+   */
+  typedef std::chrono::time_point<CPUClock, duration> time_point;
+
+  /**
+   * Boolean indicating that the clock monotonically increases.
+   */
+  static const bool is_steady = true;
+
+  /**
+   * Return the amount of CPU time that the current process has
+   * used. Unfortunately, this requires platform-specific calls, so this
+   * function returns 0 on platforms that are neither Windows nor POSIX.
+   */
+  static time_point now() noexcept;
+};
+
 /**
  * This is a very simple class which provides information about both the CPU
  * time and the wallclock time elapsed since the timer was started last time.
@@ -226,41 +268,83 @@ public:
   double get_lap_time () const DEAL_II_DEPRECATED;
 
 private:
-
   /**
-   * Amount of CPU time that the current process has used as of the last call
-   * to start(). Note that the constructor of this class calls the start()
-   * function.
+   * The Timer class stores timing information for two different clocks: a
+   * wall clock and a CPU usage clock. Since the logic for handling both
+   * clocks is, in most places, identical, we collect the relevant
+   * measurements for each clock into this <code>struct</code>.
+   *
+   * @tparam clock_type_ The type of the clock whose measurements are being
+   * stored. This class should conform to the usual clock interface expected
+   * by <code>std::chrono</code> (i.e., the correct <code>typedef</code>s and
+   * a static <code>now()</code> method).
    */
-  double              current_lap_starting_cpu_time;
+  template <class clock_type_>
+  struct ClockMeasurements
+  {
+    /**
+     * Store the clock type.
+     */
+    typedef clock_type_ clock_type;
 
-  /**
-   * Value of the wall time as of the last call to start(). Note that the
-   * constructor of this class calls the start() function.
-   */
-  double              current_lap_starting_wall_time;
+    /**
+     * The time point type of the provided clock.
+     */
+    typedef typename clock_type::time_point time_point_type;
+
+    /**
+     * The duration type of the provided clock.
+     */
+    typedef typename clock_type::duration duration_type;
+
+    /**
+     * The time point corresponding to the start of the current lap. This is
+     * obtained by calling <code>clock_type::now()</code>.
+     */
+    time_point_type current_lap_start_time;
+
+    /**
+     * The accumulated time over several laps.
+     */
+    duration_type accumulated_time;
+
+    /**
+     * The duration of the last lap.
+     */
+    duration_type last_lap_time;
+
+    /**
+     * Constructor. Sets <code>current_lap_start_time</code> to the
+     * current clock time and the durations to zero.
+     */
+    ClockMeasurements();
+
+    /**
+     * Reset the clock by setting <code>current_lap_start_time</code> to the
+     * current clock time and the durations to zero.
+     */
+    void reset();
+  };
 
   /**
-   * Accumulated CPU time for all previous start()/stop() cycles. The time for
-   * the present cycle is not included.
+   * typedef for the wall clock.
    */
-  double              accumulated_cpu_time;
+  typedef std::chrono::steady_clock wall_clock_type;
 
   /**
-   * Accumulated wall time for all previous start()/stop() cycles. The wall
-   * time for the present cycle is not included.
+   * typedef for the CPU clock.
    */
-  double              accumulated_wall_time;
+  typedef CPUClock cpu_clock_type;
 
   /**
-   * Wall time between the last start()/stop() cycle.
+   * Collection of wall time measurements.
    */
-  double              last_lap_time;
+  ClockMeasurements<wall_clock_type> wall_times;
 
   /**
-   * CPU time between the last start()/stop() cycle.
+   * Collection of CPU time measurements.
    */
-  double              last_lap_cpu_time;
+  ClockMeasurements<cpu_clock_type> cpu_times;
 
   /**
    * Whether or not the timer is presently running.
index 090668cd7db7864f267e5b66c0312809952e3fbd..ecd334f999f16fc1a4bf322eb8b6264f99edcd55 100644 (file)
@@ -29,8 +29,7 @@
 #include <string>
 #include <type_traits>
 
-#if defined(DEAL_II_HAVE_SYS_TIME_H) && defined(DEAL_II_HAVE_SYS_RESOURCE_H)
-#  include <sys/time.h>
+#ifdef DEAL_II_HAVE_SYS_RESOURCE_H
 #  include <sys/resource.h>
 #endif
 
@@ -85,41 +84,59 @@ namespace internal
         return Period::num*double(duration.count())/Period::den;
       }
 
-      /**
-       * Return the amount of CPU time that the current process has used so
-       * far. Unfortunately, this requires platform-specific calls, so this
-       * function returns 0 on platforms that are neither windows nor POSIX.
-       */
-      template <typename T>
-      T
-      get_current_cpu_time()
-      {
-        static_assert(is_duration<T>::value,
-                      "The template type should be a duration type.");
-        double system_cpu_duration = 0.0;
+    }
+  }
+}
+
+
+
+CPUClock::time_point CPUClock::now() noexcept
+{
+  double system_cpu_duration = 0.0;
 #ifdef DEAL_II_MSVC
-        FILETIME cpuTime, sysTime, createTime, exitTime;
-        if (GetProcessTimes(GetCurrentProcess(), &createTime,
-                            &exitTime, &sysTime, &cpuTime))
-          {
-            system_cpu_duration = (double)
-                                  (((unsigned long long)cpuTime.dwHighDateTime << 32)
-                                   | cpuTime.dwLowDateTime) / 1e6;
-          }
-        // keep the zero value if GetProcessTimes didn't work
+  FILETIME cpuTime, sysTime, createTime, exitTime;
+  const auto succeeded = GetProcessTimes
+  (GetCurrentProcess(), &createTime, &exitTime, &sysTime, &cpuTime);
+  if (succeeded)
+    {
+      system_cpu_duration = (double)
+      (((unsigned long long)cpuTime.dwHighDateTime << 32)
+      | cpuTime.dwLowDateTime) / 1e6;
+    }
+  // keep the zero value if GetProcessTimes didn't work
 #elif defined(DEAL_II_HAVE_SYS_RESOURCE_H)
-        rusage usage;
-        getrusage (RUSAGE_SELF, &usage);
-        system_cpu_duration = usage.ru_utime.tv_sec + 1.e-6 * usage.ru_utime.tv_usec;
+  rusage usage;
+  getrusage (RUSAGE_SELF, &usage);
+  system_cpu_duration = usage.ru_utime.tv_sec + 1.e-6 * usage.ru_utime.tv_usec;
 #else
 #  warning "Unsupported platform. Porting not finished."
 #endif
-        return from_seconds<T>(system_cpu_duration);
-      }
-    }
-  }
+  return time_point(internal::Timer::from_seconds<duration>(system_cpu_duration));
 }
 
+
+
+template <typename clock_type_>
+Timer::ClockMeasurements<clock_type_>::ClockMeasurements()
+  :
+  current_lap_start_time(clock_type::now()),
+  accumulated_time(duration_type::zero()),
+  last_lap_time(duration_type::zero())
+{}
+
+
+
+template <typename clock_type_>
+void
+Timer::ClockMeasurements<clock_type_>::reset()
+{
+  current_lap_start_time = clock_type::now();
+  accumulated_time = duration_type::zero();
+  last_lap_time = duration_type::zero();
+}
+
+
+
 Timer::Timer()
   :
   Timer(MPI_COMM_SELF, /*sync_wall_time=*/false)
@@ -130,12 +147,6 @@ Timer::Timer()
 Timer::Timer(MPI_Comm mpi_communicator,
              const bool sync_wall_time_)
   :
-  current_lap_starting_cpu_time (0.),
-  current_lap_starting_wall_time (0.),
-  accumulated_cpu_time (0.),
-  accumulated_wall_time (0.),
-  last_lap_time (numbers::signaling_nan<double>()),
-  last_lap_cpu_time (numbers::signaling_nan<double>()),
   running (false),
   mpi_communicator (mpi_communicator),
   sync_wall_time(sync_wall_time_)
@@ -150,25 +161,6 @@ Timer::Timer(MPI_Comm mpi_communicator,
 
 
 
-#ifdef DEAL_II_MSVC
-
-namespace
-{
-  namespace windows
-  {
-    double wall_clock()
-    {
-      LARGE_INTEGER freq, time;
-      QueryPerformanceFrequency(&freq);
-      QueryPerformanceCounter(&time);
-      return (double) time.QuadPart / freq.QuadPart;
-    }
-  }
-}
-
-#endif
-
-
 void Timer::start ()
 {
   running = true;
@@ -179,24 +171,8 @@ void Timer::start ()
       AssertThrowMPI(ierr);
     }
 #endif
-
-#if defined(DEAL_II_HAVE_SYS_TIME_H) && defined(DEAL_II_HAVE_SYS_RESOURCE_H)
-
-//TODO: Break this out into a function like the functions in
-//namespace windows above
-  struct timeval wall_timer;
-  gettimeofday(&wall_timer, nullptr);
-  current_lap_starting_wall_time = wall_timer.tv_sec + 1.e-6 * wall_timer.tv_usec;
-
-#elif defined(DEAL_II_MSVC)
-  current_lap_starting_wall_time = windows::wall_clock();
-#else
-#  error "Unsupported platform. Porting not finished."
-#endif
-  // TODO once we convert all private time variables to chrono types we will
-  // not need to call to_seconds here
-  current_lap_starting_cpu_time = internal::Timer::to_seconds
-                                  (internal::Timer::get_current_cpu_time<std::chrono::nanoseconds>());
+  wall_times.current_lap_start_time = wall_clock_type::now();
+  cpu_times.current_lap_start_time = cpu_clock_type::now();
 }
 
 
@@ -207,38 +183,28 @@ double Timer::stop ()
     {
       running = false;
 
-#if defined(DEAL_II_HAVE_SYS_TIME_H) && defined(DEAL_II_HAVE_SYS_RESOURCE_H)
-//TODO: Break this out into a function like the functions in
-//namespace windows above
-      struct timeval wall_timer;
-      gettimeofday(&wall_timer, nullptr);
-      last_lap_time = wall_timer.tv_sec + 1.e-6 * wall_timer.tv_usec
-                      - current_lap_starting_wall_time;
-#elif defined(DEAL_II_MSVC)
-      last_lap_time = windows::wall_clock() - current_lap_starting_wall_time;
-#else
-#  error "Unsupported platform. Porting not finished."
-#endif
-      // TODO once we convert all private time variables to chrono types we will
-      // not need to call to_seconds here
-      last_lap_cpu_time = internal::Timer::to_seconds
-                          (internal::Timer::get_current_cpu_time<std::chrono::nanoseconds>())
-                          - current_lap_starting_cpu_time;
-
-      last_lap_data = Utilities::MPI::min_max_avg (last_lap_time,
-                                                   mpi_communicator);
+      wall_times.last_lap_time = wall_clock_type::now() - wall_times.current_lap_start_time;
+      cpu_times.last_lap_time = cpu_clock_type::now() - cpu_times.current_lap_start_time;
+
+      last_lap_data = Utilities::MPI::min_max_avg
+                      (internal::Timer::to_seconds(wall_times.last_lap_time),
+                       mpi_communicator);
       if (sync_wall_time)
         {
-          last_lap_time = last_lap_data.max;
-          last_lap_cpu_time = Utilities::MPI::min_max_avg (last_lap_cpu_time,
-                                                           mpi_communicator).max;
+          wall_times.last_lap_time = internal::Timer::from_seconds<decltype(wall_times)::duration_type>
+                                     (last_lap_data.max);
+          cpu_times.last_lap_time = internal::Timer::from_seconds<decltype(cpu_times)::duration_type>
+                                    (Utilities::MPI::min_max_avg
+                                     (internal::Timer::to_seconds(cpu_times.last_lap_time),
+                                      mpi_communicator).max);
         }
-      accumulated_wall_time += last_lap_time;
-      accumulated_cpu_time += last_lap_cpu_time;
-      accumulated_wall_time_data = Utilities::MPI::min_max_avg (accumulated_wall_time,
-                                                                mpi_communicator);
+      wall_times.accumulated_time += wall_times.last_lap_time;
+      cpu_times.accumulated_time += cpu_times.last_lap_time;
+      accumulated_wall_time_data = Utilities::MPI::min_max_avg
+                                   (internal::Timer::to_seconds(wall_times.accumulated_time),
+                                    mpi_communicator);
     }
-  return accumulated_cpu_time;
+  return internal::Timer::to_seconds(cpu_times.accumulated_time);
 }
 
 
@@ -248,14 +214,15 @@ double Timer::cpu_time() const
   if (running)
     {
       const double running_time = internal::Timer::to_seconds
-                                  (internal::Timer::get_current_cpu_time<std::chrono::nanoseconds>())
-                                  - current_lap_starting_cpu_time
-                                  + accumulated_cpu_time;
+                                  (cpu_clock_type::now()
+                                   - cpu_times.current_lap_start_time
+                                   + cpu_times.accumulated_time);
       return Utilities::MPI::sum (running_time, mpi_communicator);
     }
   else
     {
-      return Utilities::MPI::sum (accumulated_cpu_time, mpi_communicator);
+      return Utilities::MPI::sum (internal::Timer::to_seconds(cpu_times.accumulated_time),
+                                  mpi_communicator);
     }
 }
 
@@ -263,14 +230,14 @@ double Timer::cpu_time() const
 
 double Timer::last_cpu_time() const
 {
-  return last_lap_cpu_time;
+  return internal::Timer::to_seconds(cpu_times.last_lap_time);
 }
 
 
 
 double Timer::get_lap_time() const
 {
-  return last_lap_time;
+  return internal::Timer::to_seconds(wall_times.last_lap_time);
 }
 
 
@@ -284,39 +251,30 @@ double Timer::operator() () const
 
 double Timer::wall_time () const
 {
+  wall_clock_type::duration current_elapsed_wall_time;
   if (running)
-    {
-#if defined(DEAL_II_HAVE_SYS_TIME_H) && defined(DEAL_II_HAVE_SYS_RESOURCE_H)
-      struct timeval wall_timer;
-      gettimeofday(&wall_timer, nullptr);
-      return (wall_timer.tv_sec
-              + 1.e-6 * wall_timer.tv_usec
-              - current_lap_starting_wall_time
-              + accumulated_wall_time);
-#else
-//TODO[BG]: Do something useful here
-      return 0;
-#endif
-    }
+    current_elapsed_wall_time = wall_clock_type::now()
+                                - wall_times.current_lap_start_time
+                                + wall_times.accumulated_time;
   else
-    return accumulated_wall_time;
+    current_elapsed_wall_time = wall_times.accumulated_time;
+
+  return internal::Timer::to_seconds(current_elapsed_wall_time);
 }
 
 
 
 double Timer::last_wall_time () const
 {
-  return last_lap_time;
+  return internal::Timer::to_seconds(wall_times.last_lap_time);
 }
 
 
 
 void Timer::reset ()
 {
-  last_lap_time = numbers::signaling_nan<double>();
-  last_lap_cpu_time = numbers::signaling_nan<double>();
-  accumulated_cpu_time = 0.;
-  accumulated_wall_time = 0.;
+  wall_times.reset();
+  cpu_times.reset();
   running         = false;
   last_lap_data.sum = last_lap_data.min = last_lap_data.max = last_lap_data.avg = numbers::signaling_nan<double>();
   last_lap_data.min_index = last_lap_data.max_index = numbers::invalid_unsigned_int;

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.