#ifndef AATIMERINC #define AATIMERINC #include #include #include #include #include #include using std::cout; using std::endl; #include "TStopwatch.h" #include "TSystem.h" #include "Table.hh" /* a stopwatch-like timer that uses cpu ticks. It is suitable for timing short pieces of code, contrary to std::chrono and root TStopwatch. */ namespace ticktimer { struct TickStopwatch { bool running; unsigned n; uint64_t total; uint64_t last_start; static uint64_t now() { return __rdtsc(); } void start() { running = true; n++; last_start = now(); } void stop() { if (running) total += now() - last_start; running = false; } void reset() { n = total = 0; running = false; } /*! stop and return accumalated time, in seconds or in clock-ticks */ double value( bool use_ticks = false ) { stop(); return total * ( use_ticks ? 1 : seconds_per_tick() ); } /*! stop and return only the last lap, in seconds or in clock-ticks */ double time_since_last_start( bool use_ticks = false ) { stop(); return ( now() - last_start ) * ( use_ticks ? 1 : seconds_per_tick() ); } /* Construct and optionally start the timer. */ TickStopwatch(bool startit = true ): running( startit), n( startit ), total(0), last_start( startit ? now() : 0 ) {} /*! A stopper object will stop the timer when it is destructed. This is useful for timing scopes. */ struct Stopper { TickStopwatch& t; Stopper(TickStopwatch& t ) : t(t) {} ~Stopper() { t.stop(); } }; Stopper stopper( bool startit = false ) { if (startit) start(); return Stopper(*this); } /*! Return the conversion factor between cpu ticks and secconds, determined experimentally. */ static double seconds_per_tick( bool recalibrate = false ) { static double r = 0; if (r == 0 || recalibrate ) { TickStopwatch ss("dum"); ss.start(); TStopwatch sw; double y = 0; for (int i = 0; i < 1000000; i++ ) { y = sqrt( y + i ); } ss.stop(); double secs = sw.RealTime(); cout << y << endl; r = secs / ss.total; } return r; } static double resolution( bool use_ticks = false ) { TickStopwatch w(false); w.start(); w.stop(); return w.value( use_ticks ); } }; /*! A Stopwatch-like timer for use in c++ an python There is a pool of stopwatches. Each stopwatch has a name (string). A Timer object also has a name. The con(decon)struction of a Timer object will start(stop) the stopwatch of the corresponding name. The stopwatches thus keep track of how long the Timer's are alive ( and how often a timer of that name is made). If a timer is created for which no stopwatch is not (yet) in the pool, the stopwatch is created. In C++, typical use is to define a variable of type Timer in some scope (e.g. function, loop) that we want to time; e.g. void f() { Timer _ ("my f timer"); // will measure how often and long f() runs // do stuf here { Timer _ ("just the part in the braces"); } } In python, you also time pieces of code using the folling idom with Timer("my timer") : time_this() and_this() but_not this() The static funciton Timer::report prints out timing information on all the stopwatches in the pool. */ struct Timer { TickStopwatch* s; Timer( string name , bool startit = true ) { auto& p = pool(); auto i = p.find(name); if ( i == p.end() ) { //cout << " new ticktimer " << name << " at " << &pool() << endl; p[name] = s = new TickStopwatch( false ); } else s = i->second; if ( startit ) s->start(); } ~Timer() { s->stop(); } // read and wheep. void reset() { s->reset(); } void start() { s->start(); } void stop() { s->stop(); } double value( bool use_ticks = false ) { return s->value( use_ticks ); } double time_since_last_start( bool use_ticks = false ) { return s->time_since_last_start(use_ticks); } TickStopwatch::Stopper stopper(bool startit) { auto r = s->stopper(); s->start(); return r; } /*! The pool is a collection of all TickStopwatchys. For reporting and for resuming existing timers */ static std::map& pool(); // has to be in cc, otherwise cling with have its own version. static void report( ostream& out = cout , bool use_ticks = false ) { string unit = use_ticks ? "(ticks)" : "(s)"; Table t(split("timer ncalls total" + unit + " time/call" + unit)); t.title = " TickTimer Report "; foreach_map( name, s , pool() ) { unsigned long c = s -> n; t << name << c << s -> value(use_ticks) << ( c ? s ->value(use_ticks) / c : 0 ); } out << t << endl; } /*! reset all timers in the pool */ static void reset_all() { foreach_map( k, v, pool() ) { (void) k; v->reset(); } } // the following are for python with statement Timer& __enter__() { return *this; } void __exit__(void*, void*, void* ) { s -> stop(); } }; } using namespace ticktimer; // this is probably the best way: map-lookup only once. #define TIMER(N) static Timer __xtim(#N, false ); auto _dum_ = __xtim.stopper(true); #define _TIMER(N) static Timer __xtim(N, false ); auto _dum_ = __xtim.stopper(true); // no stringification inline std::string methodName(const std::string& prettyFunction) { size_t colons = prettyFunction.find("::"); size_t begin = prettyFunction.substr(0, colons).rfind(" ") + 1; size_t end = prettyFunction.rfind("(") - begin; return prettyFunction.substr(begin, end) + "()"; } #define __METHOD_NAME__ methodName(__PRETTY_FUNCTION__) #define TIMEFUNC _TIMER( __METHOD_NAME__ ) #define __S1(x) #x #define __S2(x) __S1(x) #define LOCATION string(__func__)+ ":" __S2(__LINE__) #define TIMESCOPE _TIMER( LOCATION ) //============================================================================================= // Memory-checker //-============================================================================================ /*! A class to help checking memory alloction in a scope, use it via the MEMCHECK macro, which counts the memory allocated from it's place in the code until the of the current scope. Use the mem_report() function to see the recorded data. */ struct MemCounter { long int ncalls = 0; long int accumulated =0 ; long int minmem = 99999999999; long int maxmem = 0; long int currentmem; long int _startmem; struct Token { MemCounter* p; Token( MemCounter* p ) : p(p) {}; ~Token() { record(); } void record() { if(p) p->record(); p = nullptr; } }; void record() { ncalls++; currentmem = mem(); long int delta = currentmem - _startmem; minmem = std::min( minmem, delta ); maxmem = std::max( maxmem, delta ); accumulated += delta; } Token start() { _startmem = mem(); return Token(this); } long int mem() { static ProcInfo_t p; gSystem->GetProcInfo( &p ); return p.fMemResident; } }; inline std::map& mem_counter_map() { static std::map r ; return r; } inline void mem_report() { Table T("memcounter","ncalls","allocated","per call","min", "max", "current-proc" ); foreach_map( k,v, mem_counter_map() ) T << k << v.ncalls << v.accumulated << v.accumulated/(double)v.ncalls << v.minmem << v.maxmem << v.currentmem; print(T); } #define MEMCHECK MemCounter::Token __dummy = mem_counter_map()[ LOCATION ].start(); inline MemCounter::Token get_mem_counter(string name) { return mem_counter_map()[name].start(); } #endif