1 // Copyright Contributors to the OpenVDB Project 2 // SPDX-License-Identifier: MPL-2.0 3 4 #ifndef OPENVDB_UTIL_CPUTIMER_HAS_BEEN_INCLUDED 5 #define OPENVDB_UTIL_CPUTIMER_HAS_BEEN_INCLUDED 6 7 #include <openvdb/version.h> 8 #include <string> 9 #include <chrono> 10 #include <iostream>// for std::cerr 11 #include <sstream>// for ostringstream 12 #include <iomanip>// for setprecision 13 #include "Formats.h"// for printTime 14 15 namespace openvdb { 16 OPENVDB_USE_VERSION_NAMESPACE 17 namespace OPENVDB_VERSION_NAME { 18 namespace util { 19 20 /// @brief Simple timer for basic profiling. 21 /// 22 /// @code 23 /// util::CpuTimer timer; 24 /// // code here will not be timed! 25 /// timer.start("algorithm"); 26 /// // code to be timed goes here 27 /// timer.stop(); 28 /// @endcode 29 /// 30 /// or to time multiple blocks of code 31 /// 32 /// @code 33 /// util::CpuTimer timer("algorithm 1"); 34 /// // code to be timed goes here 35 /// timer.restart("algorithm 2"); 36 /// // code to be timed goes here 37 /// timer.stop(); 38 /// @endcode 39 /// 40 /// or to measure speedup between multiple runs 41 /// 42 /// @code 43 /// util::CpuTimer timer("algorithm 1"); 44 /// // code for the first run goes here 45 /// const double t1 = timer.restart("algorithm 2"); 46 /// // code for the second run goes here 47 /// const double t2 = timer.stop(); 48 /// std::cerr << "Algorithm 1 is " << (t2/t1) 49 /// << " timers faster than algorithm 2\n"; 50 /// @endcode 51 /// 52 /// or to measure multiple blocks of code with deferred output 53 /// 54 /// @code 55 /// util::CpuTimer timer(); 56 /// // code here will not be timed! 57 /// timer.start(); 58 /// // code for the first run goes here 59 /// const double t1 = timer.restart();//time in milliseconds 60 /// // code for the second run goes here 61 /// const double t2 = timer.restart();//time in milliseconds 62 /// // code here will not be timed! 63 /// util::printTime(std::cout, t1, "Algorithm 1 completed in "); 64 /// util::printTime(std::cout, t2, "Algorithm 2 completed in "); 65 /// @endcode 66 class CpuTimer 67 { 68 public: 69 /// @brief Initiate timer mOutStream(os)70 CpuTimer(std::ostream& os = std::cerr) : mOutStream(os), mT0(this->now()) {} 71 72 /// @brief Prints message and start timer. 73 /// 74 /// @note Should normally be followed by a call to stop() mOutStream(os)75 CpuTimer(const std::string& msg, std::ostream& os = std::cerr) : mOutStream(os) { this->start(msg); } 76 77 /// @brief Start timer. 78 /// 79 /// @note Should normally be followed by a call to milliseconds() or stop(std::string) start()80 inline void start() { mT0 = this->now(); } 81 82 /// @brief Print message and start timer. 83 /// 84 /// @note Should normally be followed by a call to stop() start(const std::string & msg)85 inline void start(const std::string& msg) 86 { 87 mOutStream << msg << " ..."; 88 this->start(); 89 } 90 91 /// @brief Return Time difference in microseconds since construction or start was called. 92 /// 93 /// @note Combine this method with start() to get timing without any outputs. microseconds()94 inline int64_t microseconds() const 95 { 96 return (this->now() - mT0); 97 } 98 99 /// @brief Return Time difference in milliseconds since construction or start was called. 100 /// 101 /// @note Combine this method with start() to get timing without any outputs. milliseconds()102 inline double milliseconds() const 103 { 104 static constexpr double resolution = 1.0 / 1E3; 105 return static_cast<double>(this->microseconds()) * resolution; 106 } 107 108 /// @brief Return Time difference in seconds since construction or start was called. 109 /// 110 /// @note Combine this method with start() to get timing without any outputs. seconds()111 inline double seconds() const 112 { 113 static constexpr double resolution = 1.0 / 1E6; 114 return static_cast<double>(this->microseconds()) * resolution; 115 } 116 time()117 inline std::string time() const 118 { 119 const double msec = this->milliseconds(); 120 std::ostringstream os; 121 printTime(os, msec, "", "", 4, 1, 1); 122 return os.str(); 123 } 124 125 /// @brief Returns and prints time in milliseconds since construction or start was called. 126 /// 127 /// @note Combine this method with start(std::string) to print at start and stop of task being timed. stop()128 inline double stop() const 129 { 130 const double msec = this->milliseconds(); 131 printTime(mOutStream, msec, " completed in ", "\n", 4, 3, 1); 132 return msec; 133 } 134 135 /// @brief Returns and prints time in milliseconds since construction or start was called. 136 /// 137 /// @note Combine this method with start() to delay output of task being timed. stop(const std::string & msg)138 inline double stop(const std::string& msg) const 139 { 140 const double msec = this->milliseconds(); 141 mOutStream << msg << " ..."; 142 printTime(mOutStream, msec, " completed in ", "\n", 4, 3, 1); 143 return msec; 144 } 145 146 /// @brief Re-start timer. 147 /// @return time in milliseconds since previous start or restart. 148 /// 149 /// @note Should normally be followed by a call to stop() or restart() restart()150 inline double restart() 151 { 152 const double msec = this->milliseconds(); 153 this->start(); 154 return msec; 155 } 156 157 /// @brief Stop previous timer, print message and re-start timer. 158 /// @return time in milliseconds since previous start or restart. 159 /// 160 /// @note Should normally be followed by a call to stop() or restart() restart(const std::string & msg)161 inline double restart(const std::string& msg) 162 { 163 const double delta = this->stop(); 164 this->start(msg); 165 return delta; 166 } 167 168 private: now()169 static int64_t now() 170 { 171 // steady_clock is a monotonically increasing clock designed for timing duration 172 // note that high_resolution_clock is aliased to either steady_clock or system_clock 173 // depending on the platform, so it is preferrable to use steady_clock 174 const auto time_since_epoch = 175 std::chrono::steady_clock::now().time_since_epoch(); 176 // cast time since epoch into microseconds (1 / 1000000 seconds) 177 const auto microseconds = 178 std::chrono::duration_cast<std::chrono::microseconds>(time_since_epoch).count(); 179 // cast to a a 64-bit signed integer as this will overflow in 2262! 180 return static_cast<int64_t>(microseconds); 181 } 182 183 std::ostream& mOutStream; 184 int64_t mT0{0}; 185 };// CpuTimer 186 187 } // namespace util 188 } // namespace OPENVDB_VERSION_NAME 189 } // namespace openvdb 190 191 192 #endif // OPENVDB_UTIL_CPUTIMER_HAS_BEEN_INCLUDED 193