/* (c) Thales group, 2012 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; version 2 of the License. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: http://eodev.sourceforge.net Authors: Benjamin Bouvier */ # ifndef __EO_TIMER_H__ # define __EO_TIMER_H__ # include // gettimeofday() # include // rusage() # include // std::vector # include // std::map # include "utils/eoParallel.h" // eo::parallel # include "serial/eoSerial.h" // eo::Persistent /** * @brief Timer allowing to measure time between a start point and a stop point. * * This timer allows the user to measure user time, system time and wallclock time * between two points. Basically, user time is time spent in developer code ; system * time is time spent during the IO wait and system calls ; wallclock is the difference * of time we could observe by measuring time with a watch. * * @ingroup Utilities */ class eoTimer { public: /** * @brief Default ctor. Begins all the timers. */ eoTimer() { uuremainder = 0; usremainder = 0; restart(); } /** * @brief Restarts all the timers and launch the measure. */ void restart() { gettimeofday( &wc_start, NULL ); getrusage( RUSAGE_SELF, &_start ); } /** * @brief Measures the user time spent since the last restart(). * * If the number of elapsed seconds is zero, the spent milliseconds are * added to a remainder. If the remainder exceeds one second, it is * added to the number of elapsed seconds. * * @return Number of seconds elapsed in user space. */ long int usertime() { struct rusage _now; getrusage( RUSAGE_SELF, &_now ); long int result = _now.ru_utime.tv_sec - _start.ru_utime.tv_sec; long int remainder = _now.ru_utime.tv_usec - _start.ru_utime.tv_usec; if( remainder >= 0 ) { uuremainder += remainder; } else { uuremainder += ( 1000000 - remainder ); --result; } if( uuremainder >= 1000000 ) { uuremainder -= 1000000; ++result; } return result; } /** * @brief Measures the system time spent since the last restart(). * * If the number of elapsed seconds is zero, the spent milliseconds are * added to a remainder. If the remainder exceeds one second, it is * added to the number of elapsed seconds. * * @return Number of seconds elapsed in system (kernel) space. */ long int systime() { struct rusage _now; getrusage( RUSAGE_SELF, &_now ); long int result = _now.ru_stime.tv_sec - _start.ru_stime.tv_sec; long int remainder = _now.ru_stime.tv_usec - _start.ru_stime.tv_usec; if( remainder >= 0 ) { usremainder += remainder; } else { usremainder += ( 1000000 - remainder ); --result; } if( usremainder >= 1000000 ) { usremainder -= 1000000; ++result; } return result; } /** * @brief Measures the wallclock time spent since the last restart(). * * @return Number of seconds elapsed, as a double. */ double wallclock() { struct timeval wc_end; gettimeofday( &wc_end, NULL ); return ( wc_end.tv_sec - wc_start.tv_sec ) + ( wc_end.tv_usec - wc_start.tv_usec ) / 1000000.; } protected: // Structure used to measure user and system time. struct rusage _start; // Remainder (in milliseconds) for user time. long int uuremainder; // Remainder (in milliseconds) for system time. long int usremainder; // Structure used to measure wallclock time. struct timeval wc_start; }; /** * @brief Registers a group of statistics, each statistic corresponding to user, system and wallclock times distribution. * * This class helps the user to measure time in different parts of an application. A name is associated to a statistic, * on each call to start() and stop() for this name, a new number is added to the statistic, for each of the three * measured times. * * The statistics are only registered if the option "--parallelized-do-measure" is set to true, which can be checked * thanks to global object eo::parallel. * * This shows how the eoTimerStat can be used : * @code * eoTimerStat timerStat; * timerStat.start("first_point"); * for( int i = 0; i < 1000; ++i ) * { * timerStat.start("single_computation"); * single_computation( i ); * timerStat.stop("single_computation"); * } * // After this loop, timerStat contains a statistic of key "single_computation" which contains 1000 measures for * // each type of time. * timerStat.stop("first_point"); * // After this line, timerStat contains another statistic of key "first_point" which counted the duration of the * // whole loop. * * int singleComputationUsertimeMean = 0; * for( int i = 0; i < 1000; ++i ) * { * singleComputationUsertimeMean += timerStat.stats()["single_computation"].utime[i]; * } * std::cout << "Mean of user time spent in single computation: " << singleComputationUsertimeMean / 1000. << std::endl; * @endcode * * When using MPI, these statistics can be readily be serialized, so as to be sent over a network, for instance. * * Implementation details: this eoTimerStat is in fact a map of strings (key) / Stat (value). Stat is an internal * structure directly defined in the class, which contains three vectors modeling the distributions of the different * types of elapsed times. Another map of strings (key) / eoTimer (value) allows to effectively retrieve the different * times. The struct Stat will be exposed to client, which will use its members ; however, * the client doesn't have anything to do directly with the timer, that's why the two maps are splitted. * * @ingroup Utilities */ class eoTimerStat # ifdef WITH_MPI : public eoserial::Persistent # endif { public: /** * @brief Initializes a timer stat object. */ eoTimerStat() : _forceDoMeasure(false) { // empty } /** * @brief Statistic related to a key (name). * * This structure is the value in the map saved in the eoTimerStat. It contains the statistic bound to a key, * which are the user time distribution, the system time distribution and the wallclock time distribution, as * std::vector s. * * It can readily be serialized with boost when compiling with mpi. */ struct Stat # ifdef WITH_MPI : public eoserial::Persistent # endif { std::vector utime; std::vector stime; std::vector wtime; #ifdef WITH_MPI void unpack( const eoserial::Object* obj ) { utime.clear(); static_cast< eoserial::Array* >(obj->find("utime")->second) ->deserialize< std::vector, eoserial::Array::UnpackAlgorithm >( utime ); stime.clear(); static_cast< eoserial::Array* >(obj->find("stime")->second) ->deserialize< std::vector, eoserial::Array::UnpackAlgorithm >( stime ); wtime.clear(); static_cast< eoserial::Array* >(obj->find("wtime")->second) ->deserialize< std::vector, eoserial::Array::UnpackAlgorithm >( wtime ); } eoserial::Object* pack( void ) const { eoserial::Object* obj = new eoserial::Object; obj->add("utime", eoserial::makeArray< std::vector, eoserial::MakeAlgorithm >( utime ) ); obj->add("stime", eoserial::makeArray< std::vector, eoserial::MakeAlgorithm >( stime ) ); obj->add("wtime", eoserial::makeArray< std::vector, eoserial::MakeAlgorithm >( wtime ) ); return obj; } # endif }; #ifdef WITH_MPI void unpack( const eoserial::Object* obj ) { _stats.clear(); for( eoserial::Object::const_iterator it = obj->begin(), final = obj->end(); it != final; ++it) { eoserial::unpackObject( *obj, it->first, _stats[ it->first ] ); } } eoserial::Object* pack( void ) const { eoserial::Object* obj = new eoserial::Object; for( std::map::const_iterator it = _stats.begin(), final = _stats.end(); it != final; ++it) { obj->add( it->first, it->second.pack() ); } return obj; } # endif /** * @brief Forces the measures to be retrieved. */ void forceDoMeasure() { _forceDoMeasure = true; } /** * @brief Starts a new measure for the given key. * * This is only performed if parallel.doMeasure() is true, which is equivalent to the fact that * parser found "--parallel-do-measure=1" in command line args. * * @param key The key of the statistic. */ void start( const std::string & key ) { if( eo::parallel.doMeasure() or _forceDoMeasure ) { _timers[ key ].restart(); } } /** * @brief Stops the mesure for the given key and saves the elapsed times. * * Must follow a call of start with the same key. * * This is only performed if parallel.doMeasure() is true, which is equivalent to the fact that * parser found "--parallel-do-measure=1" in command line args. * * @param key The key of the statistic. */ void stop( const std::string& key ) { if( eo::parallel.doMeasure() or _forceDoMeasure ) { Stat & s = _stats[ key ]; eoTimer & t = _timers[ key ]; s.utime.push_back( t.usertime() ); s.stime.push_back( t.systime() ); s.wtime.push_back( t.wallclock() ); } } /** * @brief Getter for the statistics map. */ std::map< std::string, Stat >& stats() { return _stats; } /** * @brief Empties the statistics map. */ void clear() { _stats.clear(); } protected: // Statistics map: links a key (string) to a statistic. std::map< std::string, Stat > _stats; // Timers map: links a key to its timer. std::map< std::string, eoTimer > _timers; // boolean to force the retrieval of statistics bool _forceDoMeasure; }; # endif // __TIMER_H__