Home | History | Annotate | Download | only in util
      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