| 1 | // Copyright (c) 2018 Google LLC. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | // Contains utils for getting resource utilization |
| 16 | |
| 17 | #ifndef SOURCE_UTIL_TIMER_H_ |
| 18 | #define SOURCE_UTIL_TIMER_H_ |
| 19 | |
| 20 | #if defined(SPIRV_TIMER_ENABLED) |
| 21 | |
| 22 | #include <sys/resource.h> |
| 23 | #include <cassert> |
| 24 | #include <iostream> |
| 25 | |
| 26 | // A macro to call spvtools::utils::PrintTimerDescription(std::ostream*, bool). |
| 27 | // The first argument must be given as std::ostream*. If it is NULL, the |
| 28 | // function does nothing. Otherwise, it prints resource types measured by Timer |
| 29 | // class. The second is optional and if it is true, the function also prints |
| 30 | // resource type fields related to memory. Otherwise, it does not print memory |
| 31 | // related fields. Its default is false. In usual, this must be placed before |
| 32 | // calling Timer::Report() to inform what those fields printed by |
| 33 | // Timer::Report() indicate (or spvtools::utils::PrintTimerDescription() must be |
| 34 | // used instead). |
| 35 | #define SPIRV_TIMER_DESCRIPTION(...) \ |
| 36 | spvtools::utils::PrintTimerDescription(__VA_ARGS__) |
| 37 | |
| 38 | // Creates an object of ScopedTimer to measure the resource utilization for the |
| 39 | // scope surrounding it as the following example: |
| 40 | // |
| 41 | // { // <-- beginning of this scope |
| 42 | // |
| 43 | // /* ... code out of interest ... */ |
| 44 | // |
| 45 | // SPIRV_TIMER_SCOPED(std::cout, tag); |
| 46 | // |
| 47 | // /* ... lines of code that we want to know its resource usage ... */ |
| 48 | // |
| 49 | // } // <-- end of this scope. The destructor of ScopedTimer prints tag and |
| 50 | // the resource utilization to std::cout. |
| 51 | #define SPIRV_TIMER_SCOPED(...) \ |
| 52 | spvtools::utils::ScopedTimer<spvtools::utils::Timer> timer##__LINE__( \ |
| 53 | __VA_ARGS__) |
| 54 | |
| 55 | namespace spvtools { |
| 56 | namespace utils { |
| 57 | |
| 58 | // Prints the description of resource types measured by Timer class. If |out| is |
| 59 | // NULL, it does nothing. Otherwise, it prints resource types. The second is |
| 60 | // optional and if it is true, the function also prints resource type fields |
| 61 | // related to memory. Its default is false. In usual, this must be placed before |
| 62 | // calling Timer::Report() to inform what those fields printed by |
| 63 | // Timer::Report() indicate. |
| 64 | void PrintTimerDescription(std::ostream*, bool = false); |
| 65 | |
| 66 | // Status of Timer. kGetrusageFailed means it failed in calling getrusage(). |
| 67 | // kClockGettimeWalltimeFailed means it failed in getting wall time when calling |
| 68 | // clock_gettime(). kClockGettimeCPUtimeFailed means it failed in getting CPU |
| 69 | // time when calling clock_gettime(). |
| 70 | enum UsageStatus { |
| 71 | kSucceeded = 0, |
| 72 | kGetrusageFailed = 1 << 0, |
| 73 | kClockGettimeWalltimeFailed = 1 << 1, |
| 74 | kClockGettimeCPUtimeFailed = 1 << 2, |
| 75 | }; |
| 76 | |
| 77 | // Timer measures the resource utilization for a range of code. The resource |
| 78 | // utilization consists of CPU time (i.e., process time), WALL time (elapsed |
| 79 | // time), USR time, SYS time, RSS delta, and the delta of the number of page |
| 80 | // faults. RSS delta and the delta of the number of page faults are measured |
| 81 | // only when |measure_mem_usage| given to the constructor is true. This class |
| 82 | // should be used as the following example: |
| 83 | // |
| 84 | // spvtools::utils::Timer timer(std::cout); |
| 85 | // timer.Start(); // <-- set |usage_before_|, |wall_before_|, |
| 86 | // and |cpu_before_| |
| 87 | // |
| 88 | // /* ... lines of code that we want to know its resource usage ... */ |
| 89 | // |
| 90 | // timer.Stop(); // <-- set |cpu_after_|, |wall_after_|, and |
| 91 | // |usage_after_| |
| 92 | // timer.Report(tag); // <-- print tag and the resource utilization to |
| 93 | // std::cout. |
| 94 | class Timer { |
| 95 | public: |
| 96 | Timer(std::ostream* out, bool measure_mem_usage = false) |
| 97 | : report_stream_(out), |
| 98 | usage_status_(kSucceeded), |
| 99 | measure_mem_usage_(measure_mem_usage) {} |
| 100 | |
| 101 | // Sets |usage_before_|, |wall_before_|, and |cpu_before_| as results of |
| 102 | // getrusage(), clock_gettime() for the wall time, and clock_gettime() for the |
| 103 | // CPU time respectively. Note that this method erases all previous state of |
| 104 | // |usage_before_|, |wall_before_|, |cpu_before_|. |
| 105 | virtual void Start(); |
| 106 | |
| 107 | // Sets |cpu_after_|, |wall_after_|, and |usage_after_| as results of |
| 108 | // clock_gettime() for the wall time, and clock_gettime() for the CPU time, |
| 109 | // getrusage() respectively. Note that this method erases all previous state |
| 110 | // of |cpu_after_|, |wall_after_|, |usage_after_|. |
| 111 | virtual void Stop(); |
| 112 | |
| 113 | // If |report_stream_| is NULL, it does nothing. Otherwise, it prints the |
| 114 | // resource utilization (i.e., CPU/WALL/USR/SYS time, RSS delta) between the |
| 115 | // time of calling Timer::Start() and the time of calling Timer::Stop(). If we |
| 116 | // cannot get a resource usage because of failures, it prints "Failed" instead |
| 117 | // for the resource. |
| 118 | void Report(const char* tag); |
| 119 | |
| 120 | // Returns the measured CPU Time (i.e., process time) for a range of code |
| 121 | // execution. If kClockGettimeCPUtimeFailed is set by the failure of calling |
| 122 | // clock_gettime(), it returns -1. |
| 123 | virtual double CPUTime() { |
| 124 | if (usage_status_ & kClockGettimeCPUtimeFailed) return -1; |
| 125 | return TimeDifference(cpu_before_, cpu_after_); |
| 126 | } |
| 127 | |
| 128 | // Returns the measured Wall Time (i.e., elapsed time) for a range of code |
| 129 | // execution. If kClockGettimeWalltimeFailed is set by the failure of |
| 130 | // calling clock_gettime(), it returns -1. |
| 131 | virtual double WallTime() { |
| 132 | if (usage_status_ & kClockGettimeWalltimeFailed) return -1; |
| 133 | return TimeDifference(wall_before_, wall_after_); |
| 134 | } |
| 135 | |
| 136 | // Returns the measured USR Time for a range of code execution. If |
| 137 | // kGetrusageFailed is set because of the failure of calling getrusage(), it |
| 138 | // returns -1. |
| 139 | virtual double UserTime() { |
| 140 | if (usage_status_ & kGetrusageFailed) return -1; |
| 141 | return TimeDifference(usage_before_.ru_utime, usage_after_.ru_utime); |
| 142 | } |
| 143 | |
| 144 | // Returns the measured SYS Time for a range of code execution. If |
| 145 | // kGetrusageFailed is set because of the failure of calling getrusage(), it |
| 146 | // returns -1. |
| 147 | virtual double SystemTime() { |
| 148 | if (usage_status_ & kGetrusageFailed) return -1; |
| 149 | return TimeDifference(usage_before_.ru_stime, usage_after_.ru_stime); |
| 150 | } |
| 151 | |
| 152 | // Returns the measured RSS delta for a range of code execution. If |
| 153 | // kGetrusageFailed is set because of the failure of calling getrusage(), it |
| 154 | // returns -1. |
| 155 | virtual long RSS() const { |
| 156 | if (usage_status_ & kGetrusageFailed) return -1; |
| 157 | return usage_after_.ru_maxrss - usage_before_.ru_maxrss; |
| 158 | } |
| 159 | |
| 160 | // Returns the measured the delta of the number of page faults for a range of |
| 161 | // code execution. If kGetrusageFailed is set because of the failure of |
| 162 | // calling getrusage(), it returns -1. |
| 163 | virtual long PageFault() const { |
| 164 | if (usage_status_ & kGetrusageFailed) return -1; |
| 165 | return (usage_after_.ru_minflt - usage_before_.ru_minflt) + |
| 166 | (usage_after_.ru_majflt - usage_before_.ru_majflt); |
| 167 | } |
| 168 | |
| 169 | virtual ~Timer() {} |
| 170 | |
| 171 | private: |
| 172 | // Returns the time gap between |from| and |to| in seconds. |
| 173 | static double TimeDifference(const timeval& from, const timeval& to) { |
| 174 | assert((to.tv_sec > from.tv_sec) || |
| 175 | (to.tv_sec == from.tv_sec && to.tv_usec >= from.tv_usec)); |
| 176 | return static_cast<double>(to.tv_sec - from.tv_sec) + |
| 177 | static_cast<double>(to.tv_usec - from.tv_usec) * .000001; |
| 178 | } |
| 179 | |
| 180 | // Returns the time gap between |from| and |to| in seconds. |
| 181 | static double TimeDifference(const timespec& from, const timespec& to) { |
| 182 | assert((to.tv_sec > from.tv_sec) || |
| 183 | (to.tv_sec == from.tv_sec && to.tv_nsec >= from.tv_nsec)); |
| 184 | return static_cast<double>(to.tv_sec - from.tv_sec) + |
| 185 | static_cast<double>(to.tv_nsec - from.tv_nsec) * .000000001; |
| 186 | } |
| 187 | |
| 188 | // Output stream to print out the resource utilization. If it is NULL, |
| 189 | // Report() does nothing. |
| 190 | std::ostream* report_stream_; |
| 191 | |
| 192 | // Status to stop measurement if a system call returns an error. |
| 193 | unsigned usage_status_; |
| 194 | |
| 195 | // Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when |
| 196 | // Timer::Start() is called. It is used as the base status of CPU time. |
| 197 | timespec cpu_before_; |
| 198 | |
| 199 | // Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when |
| 200 | // Timer::Start() is called. It is used as the base status of WALL time. |
| 201 | timespec wall_before_; |
| 202 | |
| 203 | // Variable to save the result of getrusage() when Timer::Start() is called. |
| 204 | // It is used as the base status of USR time, SYS time, and RSS. |
| 205 | rusage usage_before_; |
| 206 | |
| 207 | // Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when |
| 208 | // Timer::Stop() is called. It is used as the last status of CPU time. The |
| 209 | // resouce usage is measured by subtracting |cpu_before_| from it. |
| 210 | timespec cpu_after_; |
| 211 | |
| 212 | // Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when |
| 213 | // Timer::Stop() is called. It is used as the last status of WALL time. The |
| 214 | // resouce usage is measured by subtracting |wall_before_| from it. |
| 215 | timespec wall_after_; |
| 216 | |
| 217 | // Variable to save the result of getrusage() when Timer::Stop() is called. It |
| 218 | // is used as the last status of USR time, SYS time, and RSS. Those resouce |
| 219 | // usages are measured by subtracting |usage_before_| from it. |
| 220 | rusage usage_after_; |
| 221 | |
| 222 | // If true, Timer reports the memory usage information too. Otherwise, Timer |
| 223 | // reports only USR time, WALL time, SYS time. |
| 224 | bool measure_mem_usage_; |
| 225 | }; |
| 226 | |
| 227 | // The purpose of ScopedTimer is to measure the resource utilization for a |
| 228 | // scope. Simply creating a local variable of ScopedTimer will call |
| 229 | // Timer::Start() and it calls Timer::Stop() and Timer::Report() at the end of |
| 230 | // the scope by its destructor. When we use this class, we must choose the |
| 231 | // proper Timer class (for class TimerType template) in advance. This class |
| 232 | // should be used as the following example: |
| 233 | // |
| 234 | // { // <-- beginning of this scope |
| 235 | // |
| 236 | // /* ... code out of interest ... */ |
| 237 | // |
| 238 | // spvtools::utils::ScopedTimer<spvtools::utils::Timer> |
| 239 | // scopedtimer(std::cout, tag); |
| 240 | // |
| 241 | // /* ... lines of code that we want to know its resource usage ... */ |
| 242 | // |
| 243 | // } // <-- end of this scope. The destructor of ScopedTimer prints tag and |
| 244 | // the resource utilization to std::cout. |
| 245 | // |
| 246 | // The template<class TimerType> is used to choose a Timer class. Currently, |
| 247 | // only options for the Timer class are Timer and MockTimer in the unit test. |
| 248 | template <class TimerType> |
| 249 | class ScopedTimer { |
| 250 | public: |
| 251 | ScopedTimer(std::ostream* out, const char* tag, |
| 252 | bool measure_mem_usage = false) |
| 253 | : timer(new TimerType(out, measure_mem_usage)), tag_(tag) { |
| 254 | timer->Start(); |
| 255 | } |
| 256 | |
| 257 | // At the end of the scope surrounding the instance of this class, this |
| 258 | // destructor saves the last status of resource usage and reports it. |
| 259 | virtual ~ScopedTimer() { |
| 260 | timer->Stop(); |
| 261 | timer->Report(tag_); |
| 262 | delete timer; |
| 263 | } |
| 264 | |
| 265 | private: |
| 266 | // Actual timer that measures the resource utilization. It must be an instance |
| 267 | // of Timer class if there is no special reason to use other class. |
| 268 | TimerType* timer; |
| 269 | |
| 270 | // A tag that will be printed in front of the trace reported by Timer class. |
| 271 | const char* tag_; |
| 272 | }; |
| 273 | |
| 274 | // CumulativeTimer is the same as Timer class, but it supports a cumulative |
| 275 | // measurement as the following example: |
| 276 | // |
| 277 | // CumulativeTimer *ctimer = new CumulativeTimer(std::cout); |
| 278 | // ctimer->Start(); |
| 279 | // |
| 280 | // /* ... lines of code that we want to know its resource usage ... */ |
| 281 | // |
| 282 | // ctimer->Stop(); |
| 283 | // |
| 284 | // /* ... code out of interest ... */ |
| 285 | // |
| 286 | // ctimer->Start(); |
| 287 | // |
| 288 | // /* ... lines of code that we want to know its resource usage ... */ |
| 289 | // |
| 290 | // ctimer->Stop(); |
| 291 | // ctimer->Report(tag); |
| 292 | // delete ctimer; |
| 293 | // |
| 294 | class CumulativeTimer : public Timer { |
| 295 | public: |
| 296 | CumulativeTimer(std::ostream* out, bool measure_mem_usage = false) |
| 297 | : Timer(out, measure_mem_usage), |
| 298 | cpu_time_(0), |
| 299 | wall_time_(0), |
| 300 | usr_time_(0), |
| 301 | sys_time_(0), |
| 302 | rss_(0), |
| 303 | pgfaults_(0) {} |
| 304 | |
| 305 | // If we cannot get a resource usage because of failures, it sets -1 for the |
| 306 | // resource usage. |
| 307 | void Stop() override { |
| 308 | Timer::Stop(); |
| 309 | |
| 310 | if (cpu_time_ >= 0 && Timer::CPUTime() >= 0) |
| 311 | cpu_time_ += Timer::CPUTime(); |
| 312 | else |
| 313 | cpu_time_ = -1; |
| 314 | |
| 315 | if (wall_time_ >= 0 && Timer::WallTime() >= 0) |
| 316 | wall_time_ += Timer::WallTime(); |
| 317 | else |
| 318 | wall_time_ = -1; |
| 319 | |
| 320 | if (usr_time_ >= 0 && Timer::UserTime() >= 0) |
| 321 | usr_time_ += Timer::UserTime(); |
| 322 | else |
| 323 | usr_time_ = -1; |
| 324 | |
| 325 | if (sys_time_ >= 0 && Timer::SystemTime() >= 0) |
| 326 | sys_time_ += Timer::SystemTime(); |
| 327 | else |
| 328 | sys_time_ = -1; |
| 329 | |
| 330 | if (rss_ >= 0 && Timer::RSS() >= 0) |
| 331 | rss_ += Timer::RSS(); |
| 332 | else |
| 333 | rss_ = -1; |
| 334 | |
| 335 | if (pgfaults_ >= 0 && Timer::PageFault() >= 0) |
| 336 | pgfaults_ += Timer::PageFault(); |
| 337 | else |
| 338 | pgfaults_ = -1; |
| 339 | } |
| 340 | |
| 341 | // Returns the cumulative CPU Time (i.e., process time) for a range of code |
| 342 | // execution. |
| 343 | double CPUTime() override { return cpu_time_; } |
| 344 | |
| 345 | // Returns the cumulative Wall Time (i.e., elapsed time) for a range of code |
| 346 | // execution. |
| 347 | double WallTime() override { return wall_time_; } |
| 348 | |
| 349 | // Returns the cumulative USR Time for a range of code execution. |
| 350 | double UserTime() override { return usr_time_; } |
| 351 | |
| 352 | // Returns the cumulative SYS Time for a range of code execution. |
| 353 | double SystemTime() override { return sys_time_; } |
| 354 | |
| 355 | // Returns the cumulative RSS delta for a range of code execution. |
| 356 | long RSS() const override { return rss_; } |
| 357 | |
| 358 | // Returns the cumulative delta of number of page faults for a range of code |
| 359 | // execution. |
| 360 | long PageFault() const override { return pgfaults_; } |
| 361 | |
| 362 | private: |
| 363 | // Variable to save the cumulative CPU time (i.e., process time). |
| 364 | double cpu_time_; |
| 365 | |
| 366 | // Variable to save the cumulative wall time (i.e., elapsed time). |
| 367 | double wall_time_; |
| 368 | |
| 369 | // Variable to save the cumulative user time. |
| 370 | double usr_time_; |
| 371 | |
| 372 | // Variable to save the cumulative system time. |
| 373 | double sys_time_; |
| 374 | |
| 375 | // Variable to save the cumulative RSS delta. |
| 376 | long rss_; |
| 377 | |
| 378 | // Variable to save the cumulative delta of the number of page faults. |
| 379 | long pgfaults_; |
| 380 | }; |
| 381 | |
| 382 | } // namespace utils |
| 383 | } // namespace spvtools |
| 384 | |
| 385 | #else // defined(SPIRV_TIMER_ENABLED) |
| 386 | |
| 387 | #define SPIRV_TIMER_DESCRIPTION(...) |
| 388 | #define SPIRV_TIMER_SCOPED(...) |
| 389 | |
| 390 | #endif // defined(SPIRV_TIMER_ENABLED) |
| 391 | |
| 392 | #endif // SOURCE_UTIL_TIMER_H_ |
| 393 | |