| // Copyright (c) 2018 Google LLC. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Contains utils for getting resource utilization |
| |
| #ifndef SOURCE_UTIL_TIMER_H_ |
| #define SOURCE_UTIL_TIMER_H_ |
| |
| #if defined(SPIRV_TIMER_ENABLED) |
| |
| #include <sys/resource.h> |
| #include <cassert> |
| #include <iostream> |
| |
| // A macro to call spvtools::utils::PrintTimerDescription(std::ostream*, bool). |
| // The first argument must be given as std::ostream*. If it is NULL, the |
| // function does nothing. Otherwise, it prints resource types measured by Timer |
| // class. The second is optional and if it is true, the function also prints |
| // resource type fields related to memory. Otherwise, it does not print memory |
| // related fields. Its default is false. In usual, this must be placed before |
| // calling Timer::Report() to inform what those fields printed by |
| // Timer::Report() indicate (or spvtools::utils::PrintTimerDescription() must be |
| // used instead). |
| #define SPIRV_TIMER_DESCRIPTION(...) \ |
| spvtools::utils::PrintTimerDescription(__VA_ARGS__) |
| |
| // Creates an object of ScopedTimer to measure the resource utilization for the |
| // scope surrounding it as the following example: |
| // |
| // { // <-- beginning of this scope |
| // |
| // /* ... code out of interest ... */ |
| // |
| // SPIRV_TIMER_SCOPED(std::cout, tag); |
| // |
| // /* ... lines of code that we want to know its resource usage ... */ |
| // |
| // } // <-- end of this scope. The destructor of ScopedTimer prints tag and |
| // the resource utilization to std::cout. |
| #define SPIRV_TIMER_SCOPED(...) \ |
| spvtools::utils::ScopedTimer<spvtools::utils::Timer> timer##__LINE__( \ |
| __VA_ARGS__) |
| |
| namespace spvtools { |
| namespace utils { |
| |
| // Prints the description of resource types measured by Timer class. If |out| is |
| // NULL, it does nothing. Otherwise, it prints resource types. The second is |
| // optional and if it is true, the function also prints resource type fields |
| // related to memory. Its default is false. In usual, this must be placed before |
| // calling Timer::Report() to inform what those fields printed by |
| // Timer::Report() indicate. |
| void PrintTimerDescription(std::ostream*, bool = false); |
| |
| // Status of Timer. kGetrusageFailed means it failed in calling getrusage(). |
| // kClockGettimeWalltimeFailed means it failed in getting wall time when calling |
| // clock_gettime(). kClockGettimeCPUtimeFailed means it failed in getting CPU |
| // time when calling clock_gettime(). |
| enum UsageStatus { |
| kSucceeded = 0, |
| kGetrusageFailed = 1 << 0, |
| kClockGettimeWalltimeFailed = 1 << 1, |
| kClockGettimeCPUtimeFailed = 1 << 2, |
| }; |
| |
| // Timer measures the resource utilization for a range of code. The resource |
| // utilization consists of CPU time (i.e., process time), WALL time (elapsed |
| // time), USR time, SYS time, RSS delta, and the delta of the number of page |
| // faults. RSS delta and the delta of the number of page faults are measured |
| // only when |measure_mem_usage| given to the constructor is true. This class |
| // should be used as the following example: |
| // |
| // spvtools::utils::Timer timer(std::cout); |
| // timer.Start(); // <-- set |usage_before_|, |wall_before_|, |
| // and |cpu_before_| |
| // |
| // /* ... lines of code that we want to know its resource usage ... */ |
| // |
| // timer.Stop(); // <-- set |cpu_after_|, |wall_after_|, and |
| // |usage_after_| |
| // timer.Report(tag); // <-- print tag and the resource utilization to |
| // std::cout. |
| class Timer { |
| public: |
| Timer(std::ostream* out, bool measure_mem_usage = false) |
| : report_stream_(out), |
| usage_status_(kSucceeded), |
| measure_mem_usage_(measure_mem_usage) {} |
| |
| // Sets |usage_before_|, |wall_before_|, and |cpu_before_| as results of |
| // getrusage(), clock_gettime() for the wall time, and clock_gettime() for the |
| // CPU time respectively. Note that this method erases all previous state of |
| // |usage_before_|, |wall_before_|, |cpu_before_|. |
| virtual void Start(); |
| |
| // Sets |cpu_after_|, |wall_after_|, and |usage_after_| as results of |
| // clock_gettime() for the wall time, and clock_gettime() for the CPU time, |
| // getrusage() respectively. Note that this method erases all previous state |
| // of |cpu_after_|, |wall_after_|, |usage_after_|. |
| virtual void Stop(); |
| |
| // If |report_stream_| is NULL, it does nothing. Otherwise, it prints the |
| // resource utilization (i.e., CPU/WALL/USR/SYS time, RSS delta) between the |
| // time of calling Timer::Start() and the time of calling Timer::Stop(). If we |
| // cannot get a resource usage because of failures, it prints "Failed" instead |
| // for the resource. |
| void Report(const char* tag); |
| |
| // Returns the measured CPU Time (i.e., process time) for a range of code |
| // execution. If kClockGettimeCPUtimeFailed is set by the failure of calling |
| // clock_gettime(), it returns -1. |
| virtual double CPUTime() { |
| if (usage_status_ & kClockGettimeCPUtimeFailed) return -1; |
| return TimeDifference(cpu_before_, cpu_after_); |
| } |
| |
| // Returns the measured Wall Time (i.e., elapsed time) for a range of code |
| // execution. If kClockGettimeWalltimeFailed is set by the failure of |
| // calling clock_gettime(), it returns -1. |
| virtual double WallTime() { |
| if (usage_status_ & kClockGettimeWalltimeFailed) return -1; |
| return TimeDifference(wall_before_, wall_after_); |
| } |
| |
| // Returns the measured USR Time for a range of code execution. If |
| // kGetrusageFailed is set because of the failure of calling getrusage(), it |
| // returns -1. |
| virtual double UserTime() { |
| if (usage_status_ & kGetrusageFailed) return -1; |
| return TimeDifference(usage_before_.ru_utime, usage_after_.ru_utime); |
| } |
| |
| // Returns the measured SYS Time for a range of code execution. If |
| // kGetrusageFailed is set because of the failure of calling getrusage(), it |
| // returns -1. |
| virtual double SystemTime() { |
| if (usage_status_ & kGetrusageFailed) return -1; |
| return TimeDifference(usage_before_.ru_stime, usage_after_.ru_stime); |
| } |
| |
| // Returns the measured RSS delta for a range of code execution. If |
| // kGetrusageFailed is set because of the failure of calling getrusage(), it |
| // returns -1. |
| virtual long RSS() const { |
| if (usage_status_ & kGetrusageFailed) return -1; |
| return usage_after_.ru_maxrss - usage_before_.ru_maxrss; |
| } |
| |
| // Returns the measured the delta of the number of page faults for a range of |
| // code execution. If kGetrusageFailed is set because of the failure of |
| // calling getrusage(), it returns -1. |
| virtual long PageFault() const { |
| if (usage_status_ & kGetrusageFailed) return -1; |
| return (usage_after_.ru_minflt - usage_before_.ru_minflt) + |
| (usage_after_.ru_majflt - usage_before_.ru_majflt); |
| } |
| |
| virtual ~Timer() {} |
| |
| private: |
| // Returns the time gap between |from| and |to| in seconds. |
| static double TimeDifference(const timeval& from, const timeval& to) { |
| assert((to.tv_sec > from.tv_sec) || |
| (to.tv_sec == from.tv_sec && to.tv_usec >= from.tv_usec)); |
| return static_cast<double>(to.tv_sec - from.tv_sec) + |
| static_cast<double>(to.tv_usec - from.tv_usec) * .000001; |
| } |
| |
| // Returns the time gap between |from| and |to| in seconds. |
| static double TimeDifference(const timespec& from, const timespec& to) { |
| assert((to.tv_sec > from.tv_sec) || |
| (to.tv_sec == from.tv_sec && to.tv_nsec >= from.tv_nsec)); |
| return static_cast<double>(to.tv_sec - from.tv_sec) + |
| static_cast<double>(to.tv_nsec - from.tv_nsec) * .000000001; |
| } |
| |
| // Output stream to print out the resource utilization. If it is NULL, |
| // Report() does nothing. |
| std::ostream* report_stream_; |
| |
| // Status to stop measurement if a system call returns an error. |
| unsigned usage_status_; |
| |
| // Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when |
| // Timer::Start() is called. It is used as the base status of CPU time. |
| timespec cpu_before_; |
| |
| // Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when |
| // Timer::Start() is called. It is used as the base status of WALL time. |
| timespec wall_before_; |
| |
| // Variable to save the result of getrusage() when Timer::Start() is called. |
| // It is used as the base status of USR time, SYS time, and RSS. |
| rusage usage_before_; |
| |
| // Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when |
| // Timer::Stop() is called. It is used as the last status of CPU time. The |
| // resouce usage is measured by subtracting |cpu_before_| from it. |
| timespec cpu_after_; |
| |
| // Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when |
| // Timer::Stop() is called. It is used as the last status of WALL time. The |
| // resouce usage is measured by subtracting |wall_before_| from it. |
| timespec wall_after_; |
| |
| // Variable to save the result of getrusage() when Timer::Stop() is called. It |
| // is used as the last status of USR time, SYS time, and RSS. Those resouce |
| // usages are measured by subtracting |usage_before_| from it. |
| rusage usage_after_; |
| |
| // If true, Timer reports the memory usage information too. Otherwise, Timer |
| // reports only USR time, WALL time, SYS time. |
| bool measure_mem_usage_; |
| }; |
| |
| // The purpose of ScopedTimer is to measure the resource utilization for a |
| // scope. Simply creating a local variable of ScopedTimer will call |
| // Timer::Start() and it calls Timer::Stop() and Timer::Report() at the end of |
| // the scope by its destructor. When we use this class, we must choose the |
| // proper Timer class (for class TimerType template) in advance. This class |
| // should be used as the following example: |
| // |
| // { // <-- beginning of this scope |
| // |
| // /* ... code out of interest ... */ |
| // |
| // spvtools::utils::ScopedTimer<spvtools::utils::Timer> |
| // scopedtimer(std::cout, tag); |
| // |
| // /* ... lines of code that we want to know its resource usage ... */ |
| // |
| // } // <-- end of this scope. The destructor of ScopedTimer prints tag and |
| // the resource utilization to std::cout. |
| // |
| // The template<class TimerType> is used to choose a Timer class. Currently, |
| // only options for the Timer class are Timer and MockTimer in the unit test. |
| template <class TimerType> |
| class ScopedTimer { |
| public: |
| ScopedTimer(std::ostream* out, const char* tag, |
| bool measure_mem_usage = false) |
| : timer(new TimerType(out, measure_mem_usage)), tag_(tag) { |
| timer->Start(); |
| } |
| |
| // At the end of the scope surrounding the instance of this class, this |
| // destructor saves the last status of resource usage and reports it. |
| virtual ~ScopedTimer() { |
| timer->Stop(); |
| timer->Report(tag_); |
| delete timer; |
| } |
| |
| private: |
| // Actual timer that measures the resource utilization. It must be an instance |
| // of Timer class if there is no special reason to use other class. |
| TimerType* timer; |
| |
| // A tag that will be printed in front of the trace reported by Timer class. |
| const char* tag_; |
| }; |
| |
| // CumulativeTimer is the same as Timer class, but it supports a cumulative |
| // measurement as the following example: |
| // |
| // CumulativeTimer *ctimer = new CumulativeTimer(std::cout); |
| // ctimer->Start(); |
| // |
| // /* ... lines of code that we want to know its resource usage ... */ |
| // |
| // ctimer->Stop(); |
| // |
| // /* ... code out of interest ... */ |
| // |
| // ctimer->Start(); |
| // |
| // /* ... lines of code that we want to know its resource usage ... */ |
| // |
| // ctimer->Stop(); |
| // ctimer->Report(tag); |
| // delete ctimer; |
| // |
| class CumulativeTimer : public Timer { |
| public: |
| CumulativeTimer(std::ostream* out, bool measure_mem_usage = false) |
| : Timer(out, measure_mem_usage), |
| cpu_time_(0), |
| wall_time_(0), |
| usr_time_(0), |
| sys_time_(0), |
| rss_(0), |
| pgfaults_(0) {} |
| |
| // If we cannot get a resource usage because of failures, it sets -1 for the |
| // resource usage. |
| void Stop() override { |
| Timer::Stop(); |
| |
| if (cpu_time_ >= 0 && Timer::CPUTime() >= 0) |
| cpu_time_ += Timer::CPUTime(); |
| else |
| cpu_time_ = -1; |
| |
| if (wall_time_ >= 0 && Timer::WallTime() >= 0) |
| wall_time_ += Timer::WallTime(); |
| else |
| wall_time_ = -1; |
| |
| if (usr_time_ >= 0 && Timer::UserTime() >= 0) |
| usr_time_ += Timer::UserTime(); |
| else |
| usr_time_ = -1; |
| |
| if (sys_time_ >= 0 && Timer::SystemTime() >= 0) |
| sys_time_ += Timer::SystemTime(); |
| else |
| sys_time_ = -1; |
| |
| if (rss_ >= 0 && Timer::RSS() >= 0) |
| rss_ += Timer::RSS(); |
| else |
| rss_ = -1; |
| |
| if (pgfaults_ >= 0 && Timer::PageFault() >= 0) |
| pgfaults_ += Timer::PageFault(); |
| else |
| pgfaults_ = -1; |
| } |
| |
| // Returns the cumulative CPU Time (i.e., process time) for a range of code |
| // execution. |
| double CPUTime() override { return cpu_time_; } |
| |
| // Returns the cumulative Wall Time (i.e., elapsed time) for a range of code |
| // execution. |
| double WallTime() override { return wall_time_; } |
| |
| // Returns the cumulative USR Time for a range of code execution. |
| double UserTime() override { return usr_time_; } |
| |
| // Returns the cumulative SYS Time for a range of code execution. |
| double SystemTime() override { return sys_time_; } |
| |
| // Returns the cumulative RSS delta for a range of code execution. |
| long RSS() const override { return rss_; } |
| |
| // Returns the cumulative delta of number of page faults for a range of code |
| // execution. |
| long PageFault() const override { return pgfaults_; } |
| |
| private: |
| // Variable to save the cumulative CPU time (i.e., process time). |
| double cpu_time_; |
| |
| // Variable to save the cumulative wall time (i.e., elapsed time). |
| double wall_time_; |
| |
| // Variable to save the cumulative user time. |
| double usr_time_; |
| |
| // Variable to save the cumulative system time. |
| double sys_time_; |
| |
| // Variable to save the cumulative RSS delta. |
| long rss_; |
| |
| // Variable to save the cumulative delta of the number of page faults. |
| long pgfaults_; |
| }; |
| |
| } // namespace utils |
| } // namespace spvtools |
| |
| #else // defined(SPIRV_TIMER_ENABLED) |
| |
| #define SPIRV_TIMER_DESCRIPTION(...) |
| #define SPIRV_TIMER_SCOPED(...) |
| |
| #endif // defined(SPIRV_TIMER_ENABLED) |
| |
| #endif // SOURCE_UTIL_TIMER_H_ |