// 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 #include #include // 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 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(to.tv_sec - from.tv_sec) + static_cast(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(to.tv_sec - from.tv_sec) + static_cast(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 // 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 is used to choose a Timer class. Currently, // only options for the Timer class are Timer and MockTimer in the unit test. template 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_