// bsls_stopwatch.h -*-C++-*- #ifndef INCLUDED_BSLS_STOPWATCH #define INCLUDED_BSLS_STOPWATCH #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide access to user, system, and wall times of current process. // //@CLASSES: // bsls::Stopwatch: accumulates user, system, wall times of current process // //@DESCRIPTION: This component provides a class, 'bsls::Stopwatch', that // implements real-time (system clock) interval timers for the system, user, // and wall times of the current process. A 'bsls::Stopwatch' object can // accumulate the above values from multiple runs and always presents only the // final total (zero if never started or reset to the initial state). // ///Accuracy and Precision ///---------------------- // A 'bsls::Stopwatch' object returns its elapsed time intervals in seconds as // 'double' values. The precision is given by that of the 'bsls::TimeUtil' // component, and, as such, strives to be as high as possible. Monotonic // behavior is platform-dependent, however, as are accuracy and useful // precision. The user is advised to determine the actual performance on each // platform of interest. In general, it is better to avoid stopping and // restarting the stopwatch too often (e.g., inside a loop). It is better to // measure the overhead of the loop separately and subtract that time from the // over-all time interval. // ///Accuracy on Windows ///- - - - - - - - - - // 'bsls::Stopwatch' may be slow or inconsistent on some Windows machines. See // the 'Accuracy and Precision' section of 'bsls_timeutil.h'. // ///Usage Examples ///-------------- // The following snippets of code illustrate basic use of a 'bsls::Stopwatch' // object. First we create a stopwatch and note that the accumulated times are // all initially 0.0: //.. // bsls::Stopwatch s; // const double t0s = s.accumulatedSystemTime(); assert(0.0 == t0s); // const double t0u = s.accumulatedUserTime(); assert(0.0 == t0u); // const double t0w = s.accumulatedWallTime(); assert(0.0 == t0w); //.. // Next we start the stopwatch such that it does not accumulate system or user // times. Note that a stopwatch always accumulates wall time (i.e., as long as // it is in the RUNNING state): //.. // s.start(); // const double t1s = s.accumulatedSystemTime(); assert(0.0 == t1s); // const double t1u = s.accumulatedUserTime(); assert(0.0 == t1u); // const double t1w = s.accumulatedWallTime(); assert(0.0 <= t1w); //.. // Now stop the stopwatch and restart it so as to accumulate system and user // times (i.e., by passing 'true' to the 'start' method): //.. // s.stop(); // const double t2s = s.accumulatedSystemTime(); assert(t1s == t2s); // const double t2u = s.accumulatedUserTime(); assert(t1u == t2u); // const double t2w = s.accumulatedWallTime(); assert(t1w <= t2w); // // s.start(bsls::Stopwatch::k_COLLECT_ALSO_CPU_TIMES); // const double t3s = s.accumulatedSystemTime(); assert(t2s <= t3s); // const double t3u = s.accumulatedUserTime(); assert(t2u <= t3u); // const double t3w = s.accumulatedWallTime(); assert(t2w <= t3w); //.. // Finally, we reset the stopwatch, which both puts it into the STOPPED state // and resets all accumulated times back to their initial state (i.e., 0.0): //.. // s.reset(); // const double t4s = s.accumulatedSystemTime(); assert(0.0 == t4s); // const double t4u = s.accumulatedUserTime(); assert(0.0 == t4u); // const double t4w = s.accumulatedWallTime(); assert(0.0 == t4w); // const double t5s = s.accumulatedSystemTime(); assert(0.0 == t5s); // const double t5u = s.accumulatedUserTime(); assert(0.0 == t5u); // const double t5w = s.accumulatedWallTime(); assert(0.0 == t5w); //.. #include <bsls_keyword.h> #include <bsls_timeutil.h> #include <bsls_types.h> #include <string.h> namespace BloombergLP { namespace bsls { // =============== // class Stopwatch // =============== class Stopwatch { // The 'class' provides an accumulator for the system, user, and wall times // of the current process. A stopwatch can be in either the STOPPED // (initial) state or the RUNNING state. It potentially tracks three // values: the accumulated system time, the accumulated user time, and the // accumulated wall time (all in seconds and all initially set to zero). // Whether or not system and user times are accumulated is conditional on // how the stopwatch is started (see the 'start' method). While in the // RUNNING state, a stopwatch accumulates the above values and it retains // the values if put into the STOPPED state (unless 'reset' is called). // The accumulated times can be accessed at any time and in either state // (RUNNING or STOPPED). // DATA Types::Int64 d_startSystemTime; // system time when started // (nanoseconds) Types::Int64 d_startUserTime; // user time when started // (nanoseconds) TimeUtil::OpaqueNativeTime d_startWallTime; // wall time when started // (nanoseconds) Types::Int64 d_accumulatedSystemTime; // accumulated system time // (nanoseconds) Types::Int64 d_accumulatedUserTime; // accumulated user time // (nanoseconds) Types::Int64 d_accumulatedWallTime; // accumulated wall time // (nanoseconds) bool d_isRunning; // state flag ('true' if RUNNING, // 'false' if STOPPED) bool d_collectCpuTimesFlag; // 'true' if cpu times are being // collected // CLASS DATA static const double s_nanosecondsPerSecond; // conversion factor // (for nanoseconds to // seconds) private: // NOT IMPLEMENTED Stopwatch& operator=(const Stopwatch&) BSLS_KEYWORD_DELETED; private: // PRIVATE MANIPULATORS void updateTimes(); // Update the CPU times accumulated but this stopwatch. // PRIVATE ACCESSORS void accumulatedTimesRaw(Types::Int64 *systemTime, Types::Int64 *userTime, TimeUtil::OpaqueNativeTime *wallTime) const; // Load into the specified 'systemTime', 'userTime', and 'wallTime' the // values of the system time, user time, and wall time (in // nanoseconds), respectively, as provided by 'TimeUtil'. Types::Int64 elapsedWallTime(TimeUtil::OpaqueNativeTime rawWallTime) const; // Return the elapsed time, in nanoseconds, between the current // 'd_startWallTime' and the specified 'rawWallTime'. public: // PUBLIC CONSTANTS static const bool k_COLLECT_WALL_TIME_ONLY = false; static const bool k_COLLECT_WALL_AND_CPU_TIMES = true; // For readability/ease of understanding of code that calls the // 'start(bool)' method use these constants as arguments. // CREATORS Stopwatch(); // Create a stopwatch in the STOPPED state having total accumulated // system, user, and wall times all equal to 0.0. #ifdef BSLS_COMPILERFEATURES_SUPPORT_DEFAULTED_FUNCTIONS // To avoid warnings about future incompatibility due to the deleted copy // assignment operator we declare the copy constructor as implicitly // generated. For consistency the destructor was also placed here and // declared to be explicitly generated. Stopwatch(const Stopwatch& other) = default; // Create a stopwatch having the state and total accumulated system, // user, and wall time of the specified 'other' object. Note that this // method's definition is compiler generated. ~Stopwatch() = default; // Destroy this stopwatch. Note that this method's definition is // compiler generated. #endif // MANIPULATORS void reset(); // Place this stopwatch in the STOPPED state, unconditionally stopping // the accumulation of elapsed times, and set the quiescent elapsed // times to 0.0. void start(bool collectCpuTimes = false); // Place this stopwatch in the RUNNING state and begin accumulating // elapsed times if this object was in the STOPPED state. Optionally // specify a 'collectCpuTimes' flag indicating whether CPU times should // be collected. If 'collectCpuTimes' is not specified, then CPU times // are *not* collected. Note that the instantaneous total elapsed // times are available from the RUNNING state. Also note that // disabling collection of CPU times will result in fewer systems calls // and therefore faster measurements. Also note that 'collectCpuTimes' // may be expressed using the one of the class level constants // 'k_COLLECT_WALL_TIME_ONLY', and 'k_COLLECT_WALL_AND_CPU_TIMES' for // easier immediate understanding of the meaning of the client code. void stop(); // Place this stopwatch in the STOPPED state, unconditionally stopping // the accumulation of elapsed times. Note that the quiescent // accumulated elapsed times are available while in the STOPPED state. // ACCESSORS double accumulatedSystemTime() const; // Return the total (instantaneous and quiescent) elapsed system time // (in seconds) accumulated by this stopwatch, or 0 if the collection // of CPU times is disabled. void accumulatedTimes(double *systemTime, double *userTime, double *wallTime) const; // Load into the specified 'systemTime', 'userTime' and 'wallTime' the // total (instantaneous and quiescent) elapsed system, user, and wall // times (all in seconds) accumulated by this stopwatch. Note that // this method attempts to retrieve all of the values at the same time // (atomically), if the underlying platform supports it. double accumulatedUserTime() const; // Return the total (instantaneous and quiescent) elapsed user time (in // seconds) accumulated by this stopwatch, or 0 if the collection of // CPU times is disabled. double accumulatedWallTime() const; // Return the total (instantaneous and quiescent) elapsed wall time (in // seconds) accumulated by this stopwatch. double elapsedTime() const; // Return the total (instantaneous and quiescent) elapsed wall time (in // seconds) accumulated by this stopwatch. Note that this method is // equivalent to 'accumulatedWallTime'. bool isRunning() const; // Return 'true' if this stopwatch is in the RUNNING state, and 'false' // otherwise. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // --------------- // class Stopwatch // --------------- // PRIVATE ACCESSORS inline void Stopwatch::accumulatedTimesRaw(Types::Int64 *systemTime, Types::Int64 *userTime, TimeUtil::OpaqueNativeTime *wallTime) const { TimeUtil::getProcessTimers(systemTime, userTime); TimeUtil::getTimerRaw(wallTime); } inline Types::Int64 Stopwatch::elapsedWallTime( TimeUtil::OpaqueNativeTime rawWallTime) const { return TimeUtil::convertRawTime(rawWallTime) - TimeUtil::convertRawTime(d_startWallTime); } // CREATORS inline Stopwatch::Stopwatch() : d_startSystemTime(0) , d_startUserTime(0) // , d_startWallTime(0) // opaque type, no default ctor from 0. , d_accumulatedSystemTime(0) , d_accumulatedUserTime(0) , d_accumulatedWallTime(0) , d_isRunning(false) , d_collectCpuTimesFlag(false) { TimeUtil::initialize(); memset(&d_startWallTime, 0, sizeof(d_startWallTime)); } // MANIPULATORS inline void Stopwatch::reset() { d_isRunning = false; d_accumulatedSystemTime = 0; d_accumulatedUserTime = 0; d_accumulatedWallTime = 0; } inline void Stopwatch::start(bool collectCpuTimes) { if (!d_isRunning) { d_collectCpuTimesFlag = collectCpuTimes; if (d_collectCpuTimesFlag) { accumulatedTimesRaw(&d_startSystemTime, &d_startUserTime, &d_startWallTime); } else { TimeUtil::getTimerRaw(&d_startWallTime); } d_isRunning = true; } } inline void Stopwatch::stop() { if (d_isRunning) { if (d_collectCpuTimesFlag) { updateTimes(); } else { TimeUtil::OpaqueNativeTime now; TimeUtil::getTimerRaw(&now); d_accumulatedWallTime += elapsedWallTime(now); } d_isRunning = false; } } // ACCESSORS inline double Stopwatch::accumulatedSystemTime() const { if (!d_collectCpuTimesFlag) { return 0.0; // RETURN } if (d_isRunning) { return (double)(d_accumulatedSystemTime + TimeUtil::getProcessSystemTimer() - d_startSystemTime) / s_nanosecondsPerSecond; // RETURN } return (double)d_accumulatedSystemTime / s_nanosecondsPerSecond; } inline double Stopwatch::accumulatedUserTime() const { if (!d_collectCpuTimesFlag) { return 0.0; // RETURN } if (d_isRunning) { return (double)(d_accumulatedUserTime + TimeUtil::getProcessUserTimer() - d_startUserTime) / s_nanosecondsPerSecond; // RETURN } return (double)d_accumulatedUserTime / s_nanosecondsPerSecond; } inline double Stopwatch::accumulatedWallTime() const { if (d_isRunning) { TimeUtil::OpaqueNativeTime now; TimeUtil::getTimerRaw(&now); return (double)(d_accumulatedWallTime + elapsedWallTime(now)) / s_nanosecondsPerSecond; // RETURN } return (double)d_accumulatedWallTime / s_nanosecondsPerSecond; } inline double Stopwatch::elapsedTime() const { return accumulatedWallTime(); } inline bool Stopwatch::isRunning() const { return d_isRunning; } } // close package namespace #ifndef BDE_OPENSOURCE_PUBLICATION // BACKWARD_COMPATIBILITY // ============================================================================ // BACKWARD COMPATIBILITY // ============================================================================ typedef bsls::Stopwatch bsls_Stopwatch; // This alias is defined for backward compatibility. #endif // BDE_OPENSOURCE_PUBLICATION -- BACKWARD_COMPATIBILITY } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2016 Bloomberg Finance L.P. // // 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. // ----------------------------- END-OF-FILE ----------------------------------