| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /////////////////////////////////////////////////////////////////////////////// | ||
| 2 | // Copyright (c) 2010-2013 Tommaso Urli (tommaso.urli@uniud.it; University of | ||
| 3 | // Udine) | ||
| 4 | // | ||
| 5 | // Permission is hereby granted, free of charge, to any person obtaining | ||
| 6 | // a copy of this software and associated documentation files (the | ||
| 7 | // "Software"), to deal in the Software without restriction, including | ||
| 8 | // without limitation the rights to use, copy, modify, merge, publish, | ||
| 9 | // distribute, sublicense, and/or sell copies of the Software, and to | ||
| 10 | // permit persons to whom the Software is furnished to do so, subject to | ||
| 11 | // the following conditions: | ||
| 12 | // | ||
| 13 | // The above copyright notice and this permission notice shall be | ||
| 14 | // included in all copies or substantial portions of the Software. | ||
| 15 | // | ||
| 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
| 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
| 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
| 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 23 | /////////////////////////////////////////////////////////////////////////////// | ||
| 24 | |||
| 25 | #ifndef CROCODDYL_CORE_UTILS_STOPWATCH_H_ | ||
| 26 | #define CROCODDYL_CORE_UTILS_STOPWATCH_H_ | ||
| 27 | |||
| 28 | #include <ctime> | ||
| 29 | #include <iostream> | ||
| 30 | #include <limits> | ||
| 31 | #include <map> | ||
| 32 | #include <sstream> | ||
| 33 | |||
| 34 | #ifndef WIN32 | ||
| 35 | /* The classes below are exported */ | ||
| 36 | #pragma GCC visibility push(default) | ||
| 37 | #endif | ||
| 38 | |||
| 39 | #define START_PROFILER(name) \ | ||
| 40 | getProfiler().profiler_status() ? getProfiler().start(name) : void() | ||
| 41 | #define STOP_PROFILER(name) \ | ||
| 42 | getProfiler().profiler_status() ? getProfiler().stop(name) : void() | ||
| 43 | |||
| 44 | #define STOP_WATCH_MAX_NAME_LENGTH 60 | ||
| 45 | #define STOP_WATCH_TIME_WIDTH 10 | ||
| 46 | |||
| 47 | namespace crocoddyl { | ||
| 48 | |||
| 49 | // Generic stopwatch exception class | ||
| 50 | struct StopwatchException { | ||
| 51 | public: | ||
| 52 | ✗ | StopwatchException(std::string error) : error(error) {} | |
| 53 | std::string error; | ||
| 54 | }; | ||
| 55 | |||
| 56 | enum StopwatchMode { | ||
| 57 | NONE = 0, // Clock is not initialized | ||
| 58 | CPU_TIME = 1, // Clock calculates time ranges using ctime and CLOCKS_PER_SEC | ||
| 59 | REAL_TIME = 2 // Clock calculates time by asking the operating system how | ||
| 60 | // much real time passed | ||
| 61 | }; | ||
| 62 | |||
| 63 | /** | ||
| 64 | @brief A class representing a stopwatch. | ||
| 65 | |||
| 66 | @code | ||
| 67 | Stopwatch swatch(); | ||
| 68 | @endcode | ||
| 69 | |||
| 70 | The Stopwatch class can be used to measure execution time of code, | ||
| 71 | algorithms, etc., // TODO: he Stopwatch can be initialized in two | ||
| 72 | time-taking modes, CPU time and real time: | ||
| 73 | |||
| 74 | @code | ||
| 75 | swatch.set_mode(REAL_TIME); | ||
| 76 | @endcode | ||
| 77 | |||
| 78 | CPU time is the time spent by the processor on a certain piece of code, | ||
| 79 | while real time is the real amount of time taken by a certain piece of | ||
| 80 | code to execute (i.e. in general if you are doing hard work such as | ||
| 81 | image or video editing on a different process the measured time will | ||
| 82 | probably increase). | ||
| 83 | |||
| 84 | How does it work? Basically, one wraps the code to be measured with the | ||
| 85 | following method calls: | ||
| 86 | |||
| 87 | @code | ||
| 88 | swatch.start("My astounding algorithm"); | ||
| 89 | // Hic est code | ||
| 90 | swatch.stop("My astounding algorithm"); | ||
| 91 | @endcode | ||
| 92 | |||
| 93 | A string representing the code ID is provided so that nested portions of | ||
| 94 | code can be profiled separately: | ||
| 95 | |||
| 96 | @code | ||
| 97 | swatch.start("My astounding algorithm"); | ||
| 98 | |||
| 99 | swatch.start("My astounding algorithm - Super smart init"); | ||
| 100 | // Initialization | ||
| 101 | swatch.stop("My astounding algorithm - Super smart init"); | ||
| 102 | |||
| 103 | swatch.start("My astounding algorithm - Main loop"); | ||
| 104 | // Loop | ||
| 105 | swatch.stop("My astounding algorithm - Main loop"); | ||
| 106 | |||
| 107 | swatch.stop("My astounding algorithm"); | ||
| 108 | @endcode | ||
| 109 | |||
| 110 | Note: ID strings can be whatever you like, in the previous example I have | ||
| 111 | used "My astounding algorithm - *" only to enforce the fact that the | ||
| 112 | measured code portions are part of My astounding algorithm, but there's no | ||
| 113 | connection between the three measurements. | ||
| 114 | |||
| 115 | If the code for a certain task is scattered through different files or | ||
| 116 | portions of the same file one can use the start-pause-stop method: | ||
| 117 | |||
| 118 | @code | ||
| 119 | swatch.start("Setup"); | ||
| 120 | // First part of setup | ||
| 121 | swatch.pause("Setup"); | ||
| 122 | |||
| 123 | swatch.start("Main logic"); | ||
| 124 | // Main logic | ||
| 125 | swatch.stop("Main logic"); | ||
| 126 | |||
| 127 | swatch.start("Setup"); | ||
| 128 | // Cleanup (part of the setup) | ||
| 129 | swatch.stop("Setup"); | ||
| 130 | @endcode | ||
| 131 | |||
| 132 | Finally, to report the results of the measurements just run: | ||
| 133 | |||
| 134 | @code | ||
| 135 | swatch.report("Code ID"); | ||
| 136 | @endcode | ||
| 137 | |||
| 138 | Thou can also provide an additional std::ostream& parameter to report() to | ||
| 139 | redirect the logging on a different output. Also, you can use the | ||
| 140 | get_total/min/max/average_time() methods to get the individual numeric data, | ||
| 141 | without all the details of the logging. You can also extend Stopwatch to | ||
| 142 | implement your own logging syntax. | ||
| 143 | |||
| 144 | To report all the measurements: | ||
| 145 | |||
| 146 | @code | ||
| 147 | swatch.report_all(); | ||
| 148 | @endcode | ||
| 149 | |||
| 150 | Same as above, you can redirect the output by providing a std::ostream& | ||
| 151 | parameter. | ||
| 152 | |||
| 153 | */ | ||
| 154 | class Stopwatch { | ||
| 155 | protected: | ||
| 156 | struct PerformanceData; | ||
| 157 | |||
| 158 | public: | ||
| 159 | struct Watcher { | ||
| 160 | Stopwatch &w; | ||
| 161 | std::string n; | ||
| 162 | PerformanceData *p; | ||
| 163 | |||
| 164 | ✗ | Watcher(Stopwatch &_w, std::string _n, PerformanceData *_p) | |
| 165 | ✗ | : w(_w), n(_n), p(_p) {} | |
| 166 | inline void start() { | ||
| 167 | if (w.profiler_active) _start(); | ||
| 168 | } | ||
| 169 | inline void stop() { | ||
| 170 | if (w.profiler_active) _stop(); | ||
| 171 | } | ||
| 172 | |||
| 173 | private: | ||
| 174 | void _start(); | ||
| 175 | void _stop(); | ||
| 176 | }; | ||
| 177 | |||
| 178 | /** @brief Constructor */ | ||
| 179 | Stopwatch(StopwatchMode _mode = NONE); | ||
| 180 | |||
| 181 | /** @brief Destructor */ | ||
| 182 | ~Stopwatch(); | ||
| 183 | |||
| 184 | /** @brief Enable the profiler **/ | ||
| 185 | void enable_profiler(); | ||
| 186 | |||
| 187 | /** @brief Disable the profiler **/ | ||
| 188 | void disable_profiler(); | ||
| 189 | |||
| 190 | /** @brief Return if the profiler is enable or disable **/ | ||
| 191 | ✗ | inline bool profiler_status() { return profiler_active; } | |
| 192 | |||
| 193 | /** @brief Tells if a performance with a certain ID exists */ | ||
| 194 | bool performance_exists(std::string perf_name); | ||
| 195 | |||
| 196 | /** @brief Initialize stopwatch to use a certain time taking mode */ | ||
| 197 | void set_mode(StopwatchMode mode); | ||
| 198 | |||
| 199 | /** @brief create a Start the stopwatch related to a certain piece of code */ | ||
| 200 | Watcher watcher(const std::string &perf_name); | ||
| 201 | |||
| 202 | /** @brief Start the stopwatch related to a certain piece of code */ | ||
| 203 | void start(const std::string &perf_name); | ||
| 204 | |||
| 205 | /** @brief Stops the stopwatch related to a certain piece of code */ | ||
| 206 | void stop(const std::string &perf_name); | ||
| 207 | |||
| 208 | /** @brief Stops the stopwatch related to a certain piece of code */ | ||
| 209 | void pause(const std::string &perf_name); | ||
| 210 | |||
| 211 | /** @brief Reset a certain performance record */ | ||
| 212 | void reset(const std::string &perf_name); | ||
| 213 | |||
| 214 | /** @brief Resets all the performance records */ | ||
| 215 | void reset_all(); | ||
| 216 | |||
| 217 | /** @brief Dump the data of a certain performance record */ | ||
| 218 | void report(const std::string &perf_name, int precision = 2, | ||
| 219 | std::ostream &output = std::cout); | ||
| 220 | |||
| 221 | /** @brief Dump the data of all the performance records */ | ||
| 222 | void report_all(int precision = 2, std::ostream &output = std::cout); | ||
| 223 | |||
| 224 | /** @brief Returns total execution time of a certain performance */ | ||
| 225 | long double get_total_time(const std::string &perf_name); | ||
| 226 | |||
| 227 | /** @brief Returns average execution time of a certain performance */ | ||
| 228 | long double get_average_time(const std::string &perf_name); | ||
| 229 | |||
| 230 | /** @brief Returns minimum execution time of a certain performance */ | ||
| 231 | long double get_min_time(const std::string &perf_name); | ||
| 232 | |||
| 233 | /** @brief Returns maximum execution time of a certain performance */ | ||
| 234 | long double get_max_time(const std::string &perf_name); | ||
| 235 | |||
| 236 | /** @brief Return last measurement of a certain performance */ | ||
| 237 | long double get_last_time(const std::string &perf_name); | ||
| 238 | |||
| 239 | /** @brief Return the time since the start of the last measurement of a given | ||
| 240 | performance. | ||
| 241 | */ | ||
| 242 | long double get_time_so_far(const std::string &perf_name); | ||
| 243 | |||
| 244 | /** @brief Turn off clock, all the Stopwatch::* methods return without doing | ||
| 245 | anything after this method is called. */ | ||
| 246 | void turn_off(); | ||
| 247 | |||
| 248 | /** @brief Turn on clock, restore clock operativity after a turn_off(). */ | ||
| 249 | void turn_on(); | ||
| 250 | |||
| 251 | /** @brief Take time, depends on mode. */ | ||
| 252 | long double take_time(); | ||
| 253 | |||
| 254 | protected: | ||
| 255 | /** @brief Struct to hold the performance data */ | ||
| 256 | struct PerformanceData { | ||
| 257 | ✗ | PerformanceData() | |
| 258 | ✗ | : clock_start(0), | |
| 259 | ✗ | total_time(0), | |
| 260 | ✗ | min_time(std::numeric_limits<long double>::max()), | |
| 261 | ✗ | max_time(std::numeric_limits<long double>::min()), | |
| 262 | ✗ | last_time(0), | |
| 263 | ✗ | paused(false), | |
| 264 | ✗ | stops(0) {} | |
| 265 | |||
| 266 | long double clock_start; //!< Start time | ||
| 267 | long double total_time; //!< Cumulative total time | ||
| 268 | long double min_time; //!< Minimum time | ||
| 269 | long double max_time; //!< Maximum time | ||
| 270 | long double last_time; //!< Last time | ||
| 271 | bool paused; //!< Tells if this performance has been paused, only for | ||
| 272 | //!< internal use | ||
| 273 | int stops; //!< How many cycles have been this stopwatch executed? | ||
| 274 | }; | ||
| 275 | |||
| 276 | PerformanceData &get_or_create_perf(const std::string &perf_name); | ||
| 277 | |||
| 278 | void stop_perf(PerformanceData &perf_info, long double clock_end); | ||
| 279 | |||
| 280 | bool active; //!< Flag to hold the clock's status | ||
| 281 | StopwatchMode mode; //!< Time taking mode | ||
| 282 | std::map<std::string, PerformanceData> | ||
| 283 | *records_of; //!< Dynamic collection of performance data | ||
| 284 | bool profiler_active; //!< Indicates if the profiler is enabled | ||
| 285 | }; | ||
| 286 | |||
| 287 | Stopwatch &getProfiler(); | ||
| 288 | |||
| 289 | } // namespace crocoddyl | ||
| 290 | |||
| 291 | #ifndef WIN32 | ||
| 292 | #pragma GCC visibility pop | ||
| 293 | #endif | ||
| 294 | |||
| 295 | #endif | ||
| 296 |