From 57bddfa3be4f967a23acf6dec0f972630c75ae5b Mon Sep 17 00:00:00 2001 From: David Wells Date: Wed, 23 Aug 2017 20:09:07 -0400 Subject: [PATCH] Use std::chrono types in the Timer class. 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 | 126 +++++++++++++++++---- source/base/timer.cc | 210 ++++++++++++++--------------------- 2 files changed, 189 insertions(+), 147 deletions(-) diff --git a/include/deal.II/base/timer.h b/include/deal.II/base/timer.h index 71c227d31f..8e1d211c6c 100644 --- a/include/deal.II/base/timer.h +++ b/include/deal.II/base/timer.h @@ -22,12 +22,54 @@ #include #include -#include +#include #include #include +#include DEAL_II_NAMESPACE_OPEN +/** + * A clock, compatible with the std::chrono 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 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 struct. + * + * @tparam clock_type_ The type of the clock whose measurements are being + * stored. This class should conform to the usual clock interface expected + * by std::chrono (i.e., the correct typedefs and + * a static now() method). */ - double current_lap_starting_cpu_time; + template + 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 clock_type::now(). + */ + 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 current_lap_start_time to the + * current clock time and the durations to zero. + */ + ClockMeasurements(); + + /** + * Reset the clock by setting current_lap_start_time 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_times; /** - * CPU time between the last start()/stop() cycle. + * Collection of CPU time measurements. */ - double last_lap_cpu_time; + ClockMeasurements cpu_times; /** * Whether or not the timer is presently running. diff --git a/source/base/timer.cc b/source/base/timer.cc index 090668cd7d..ecd334f999 100644 --- a/source/base/timer.cc +++ b/source/base/timer.cc @@ -29,8 +29,7 @@ #include #include -#if defined(DEAL_II_HAVE_SYS_TIME_H) && defined(DEAL_II_HAVE_SYS_RESOURCE_H) -# include +#ifdef DEAL_II_HAVE_SYS_RESOURCE_H # include #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 - T - get_current_cpu_time() - { - static_assert(is_duration::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(system_cpu_duration); - } - } - } + return time_point(internal::Timer::from_seconds(system_cpu_duration)); } + + +template +Timer::ClockMeasurements::ClockMeasurements() + : + current_lap_start_time(clock_type::now()), + accumulated_time(duration_type::zero()), + last_lap_time(duration_type::zero()) +{} + + + +template +void +Timer::ClockMeasurements::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()), - last_lap_cpu_time (numbers::signaling_nan()), 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()); + 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()) - - 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 + (last_lap_data.max); + cpu_times.last_lap_time = internal::Timer::from_seconds + (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()) - - 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(); - last_lap_cpu_time = numbers::signaling_nan(); - 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(); last_lap_data.min_index = last_lap_data.max_index = numbers::invalid_unsigned_int; -- 2.39.5