diff --git a/dune/common/test/CMakeLists.txt b/dune/common/test/CMakeLists.txt index a39fd5f5e4cecb3965e8cd59db725657253faa90..045c847e1592eeafe7fb8d22bffb9c78519b106b 100644 --- a/dune/common/test/CMakeLists.txt +++ b/dune/common/test/CMakeLists.txt @@ -348,6 +348,9 @@ dune_add_test(SOURCES typeutilitytest.cc dune_add_test(SOURCES typelisttest.cc LABELS quick) +dune_add_test(SOURCES timertest.cc + LABELS quick) + dune_add_test(SOURCES utilitytest.cc LABELS quick) diff --git a/dune/common/test/timertest.cc b/dune/common/test/timertest.cc new file mode 100644 index 0000000000000000000000000000000000000000..5cfc821ea44107e7d6d2f5c9034c7c0b5dc34834 --- /dev/null +++ b/dune/common/test/timertest.cc @@ -0,0 +1,73 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +// SPDX-FileCopyrightInfo: Copyright © DUNE Project contributors, see file LICENSE.md in module root +// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception + +#include <chrono> +#include <thread> + +#include <dune/common/timer.hh> + +#include <dune/common/test/testsuite.hh> + +int +main() +{ + Dune::TestSuite suite; + + using clock = std::chrono::high_resolution_clock; + using namespace std::chrono_literals; + Dune::Timer top_timer(false); + auto top_chrono_start = clock::now(); + std::this_thread::sleep_for(10ms); + top_timer.start(); + + Dune::Timer loop_timer; + for (std::size_t i = 0; i != 5; ++i) { + { // let chrono timer run for longer time + auto chrono_start = clock::now(); + loop_timer.start(); + std::this_thread::sleep_for(10ms); + loop_timer.stop(); + double dune_elapsed = loop_timer.lastElapsed(); + std::this_thread::sleep_for(10ms); + auto chrono_elapsed = clock::now() - chrono_start; + + std::this_thread::sleep_for(10ms); + suite.check(dune_elapsed == loop_timer.lastElapsed()) + << "Elapsed time is not the same as when timer is stop"; + suite.check(dune_elapsed < (chrono_elapsed / 1.0s)) + << "Dune timer " << dune_elapsed << "s takes more time than chrono timer " + << (chrono_elapsed / 1.0s) << "s"; + } + + { // let dune timer run for longer time + loop_timer.start(); + auto chrono_start = clock::now(); + std::this_thread::sleep_for(10ms); + auto chrono_elapsed = clock::now() - chrono_start; + std::this_thread::sleep_for(10ms); + loop_timer.stop(); + double dune_elapsed = loop_timer.lastElapsed(); + + std::this_thread::sleep_for(10ms); + suite.check(dune_elapsed == loop_timer.lastElapsed()) + << "Elapsed time is not the same as when timer is stop"; + suite.check(dune_elapsed > (chrono_elapsed / 1.0s)) + << "Dune timer " << dune_elapsed << "s takes less time than chrono timer " + << (chrono_elapsed / 1.0s) << "s"; + } + } + + double top_dune_elapsed = top_timer.stop(); + std::this_thread::sleep_for(10ms); + auto top_chrono_elapsed = clock::now() - top_chrono_start; + + suite.check(top_dune_elapsed < (top_chrono_elapsed / 1.0s)) + << "Top Dune timer " << top_dune_elapsed << "s takes less time than Top chrono timer " + << (top_chrono_elapsed / 1.0s) << "s"; + + suite.check(top_dune_elapsed > loop_timer.elapsed()) + << "Top Dune timer " << top_dune_elapsed << "s takes less time than Loop Dune timer " + << loop_timer.elapsed() << "s"; +} diff --git a/dune/common/timer.hh b/dune/common/timer.hh index a5349faa75e5d38fe63d0980519d153b6d21333f..aa34d7b0db1a5e001887566ac405f2d9854cf7f6 100644 --- a/dune/common/timer.hh +++ b/dune/common/timer.hh @@ -5,13 +5,7 @@ #ifndef DUNE_TIMER_HH #define DUNE_TIMER_HH -#ifndef TIMER_USE_STD_CLOCK -// headers for std::chrono #include <chrono> -#else -// headers for std::clock -#include <ctime> -#endif namespace Dune { @@ -26,21 +20,17 @@ namespace Dune { /** \brief A simple stop watch - This class reports the elapsed user-time, i.e. time spent computing, - after the last call to Timer::reset(). The results are seconds and - fractional seconds. Note that the resolution of the timing depends - on your OS kernel which should be somewhere in the millisecond range. - - The class is basically a wrapper for the libc-function getrusage() - - \warning In a multi-threading situation, this class does NOT return wall-time! - Instead, the run time for all threads will be added up. - For example, if you have four threads running in parallel taking one second each, - then the Timer class will return an elapsed time of four seconds. + This class reports the elapsed real time, i.e. time elapsed + after Timer::reset(). It does not measure the time spent computing, + i.e. time spend in concurrent threads is not added up while + time measurements include the time elapsed while sleeping. + The class is basically a wrapper around std::chrono::high_resolution_clock::now(). */ class Timer { + using Clock = std::chrono::high_resolution_clock; + using Units = std::chrono::duration<double>; // seconds stored as double public: /** \brief A new timer, create and reset @@ -56,9 +46,9 @@ namespace Dune { //! Reset timer while keeping the running/stopped state void reset() noexcept { - sumElapsed_ = 0.0; - storedLastElapsed_ = 0.0; - rawReset(); + sumElapsed_ = std::chrono::seconds{0}; + storedLastElapsed_ = std::chrono::seconds{0}; + cstart = Clock::now(); } @@ -67,7 +57,7 @@ namespace Dune { { if (not (isRunning_)) { - rawReset(); + cstart = Clock::now(); isRunning_ = true; } } @@ -76,33 +66,22 @@ namespace Dune { //! Get elapsed user-time from last reset until now/last stop in seconds. double elapsed () const noexcept { - // if timer is running add the time elapsed since last start to sum - if (isRunning_) - return sumElapsed_ + lastElapsed(); - - return sumElapsed_; + return durationCast(rawElapsed()); } - //! Get elapsed user-time from last start until now/last stop in seconds. double lastElapsed () const noexcept { - // if timer is running return the current value - if (isRunning_) - return rawElapsed(); - - // if timer is not running return stored value from last run - return storedLastElapsed_; + return durationCast(rawLastElapsed()); } - //! Stop the timer and return elapsed(). double stop() noexcept { if (isRunning_) { // update storedLastElapsed_ and sumElapsed_ and stop timer - storedLastElapsed_ = lastElapsed(); + storedLastElapsed_ = rawLastElapsed(); sumElapsed_ += storedLastElapsed_; isRunning_ = false; } @@ -113,37 +92,34 @@ namespace Dune { private: bool isRunning_; - double sumElapsed_; - double storedLastElapsed_; + Clock::duration sumElapsed_; + Clock::duration storedLastElapsed_; - -#ifdef TIMER_USE_STD_CLOCK - void rawReset() noexcept + Clock::duration rawElapsed () const noexcept { - cstart = std::clock(); - } + // if timer is running add the time elapsed since last start to sum + if (isRunning_) + return sumElapsed_ + rawLastElapsed(); - double rawElapsed () const noexcept - { - return (std::clock()-cstart) / static_cast<double>(CLOCKS_PER_SEC); + return sumElapsed_; } - std::clock_t cstart; -#else - void rawReset() noexcept + //! Get elapsed user-time from last start until now/last stop in seconds. + Clock::duration rawLastElapsed () const noexcept { - cstart = std::chrono::high_resolution_clock::now(); + // if timer is running return the current value + if (isRunning_) + return Clock::now() - cstart; + + // if timer is not running return stored value from last run + return storedLastElapsed_; } - double rawElapsed () const noexcept - { - std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now(); - std::chrono::duration<double> time_span = std::chrono::duration_cast<std::chrono::duration<double> >(now - cstart); - return time_span.count(); + double durationCast(Clock::duration duration) const noexcept { + return std::chrono::duration_cast<Units>(duration).count(); } - std::chrono::high_resolution_clock::time_point cstart; -#endif + Clock::time_point cstart; }; // end class Timer /** @} end documentation */