#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.
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.
#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
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)
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_)
-#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;
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();
}
{
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);
}
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);
}
}
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);
}
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;