perf.cpp

Go to the documentation of this file.
00001 /*
00002  * perf.cpp
00003  *
00004  * Copyright (C) 2006,2009,2010  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Implementation of the timing library.  See perf.h
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "perf.h"               // always include our own header first
00036 
00037 #include <iostream>
00038 
00039 #ifdef WIN32
00040 #include <winsock2.h>
00041 #include <time.h>
00042 #else   // WIN32
00043 #include <sys/time.h>
00044 #endif  // WIN32
00045 
00046 #include "threadsafe/threadsafe_map.h"
00047 
00048 
00049 #ifdef WIN32
00050 
00051 #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
00052   #define DELTA_EPOCH_IN_MICROSECS  11644473600000000Ui64
00053 #else
00054   #define DELTA_EPOCH_IN_MICROSECS  11644473600000000ULL
00055 #endif
00056   
00057 // Definition of a gettimeofday function
00058 // from  http://www.suacommunity.com/dictionary/gettimeofday-entry.php
00059 int
00060 gettimeofday
00061 (
00062 struct timeval *tv,
00063 struct timezone *tz
00064 )
00065 {
00066   ASSERT(tv, "null");
00067   ASSERT(!tz, "do not support timezone");
00068 
00069 //  DPRINTF("gettimeofday()");
00070 
00071 // Define a structure to receive the current Windows filetime
00072   FILETIME ft;
00073  
00074 // Initialize the present time to 0 and the timezone to UTC
00075   unsigned __int64 tmpres = 0;
00076  
00077     GetSystemTimeAsFileTime(&ft);
00078  
00079 // The GetSystemTimeAsFileTime returns the number of 100 nanosecond 
00080 // intervals since Jan 1, 1601 in a structure. Copy the high bits to 
00081 // the 64 bit tmpres, shift it left by 32 then or in the low 32 bits.
00082     tmpres |= ft.dwHighDateTime;
00083     tmpres <<= 32;
00084     tmpres |= ft.dwLowDateTime;
00085  
00086 // Convert to microseconds by dividing by 10
00087     tmpres /= 10;
00088  
00089 // The Unix epoch starts on Jan 1 1970.  Need to subtract the difference 
00090 // in seconds from Jan 1 1601.
00091     tmpres -= DELTA_EPOCH_IN_MICROSECS;
00092  
00093 // Finally change microseconds to seconds and place in the seconds value. 
00094 // The modulus picks up the microseconds.
00095     tv->tv_sec = (long)(tmpres / 1000000UL);
00096     tv->tv_usec = (long)(tmpres % 1000000UL);
00097   
00098   //  DPRINTF("tv_sec = %d", tv->tv_sec);
00099 
00100   return 0;
00101 }
00102 
00103 #endif // WIN32
00104 
00105 
00106 
00107 namespace perf {
00108 
00109 
00110 struct timer_rec_t {
00111         // constructor, manipulator
00112         timer_rec_t(void) throw() {
00113                         calls = 0;
00114                         total.clear();
00115                 }
00116 
00117         void update(IN const time_t& delta) throw() {
00118                         mlock m(mutex);
00119 
00120                         calls++;
00121                         total.increment(delta);
00122                 }
00123 
00124         // data fields
00125         smart_mutex     mutex;
00126         long            calls;          // number of calls
00127         time_t          total;          // total time
00128 };
00129 
00130 typedef threadsafe_map<std::string, timer_rec_t *> map_timer_t;
00131 
00132 typedef std::multimap<double, std::string> map_time_name_t;
00133 
00134 
00135 static bool s_timersEnabled                     = true;
00136 
00137 static map_timer_t s_timers;            // master list of timers
00138 
00139 
00140 ////////////////////////////////////////////////////////////////////////////////
00141 //
00142 //      static helper methods
00143 //
00144 ////////////////////////////////////////////////////////////////////////////////
00145 
00146 static void *
00147 initializeTimer
00148 (
00149 IN const char * name
00150 )
00151 {
00152         ASSERT(name, "null");
00153 
00154         timer_rec_t * tr = NULL;
00155         if (s_timers.lookup(name, tr)) {
00156                 ASSERT(tr, "null timer record in map?");
00157                 return tr;
00158         }
00159 
00160         // need to create new entry
00161         tr = new timer_rec_t;
00162         ASSERT(tr, "out of memory");
00163 
00164         // create
00165         s_timers.insert(name, tr);
00166         return tr;
00167 }
00168 
00169 
00170 
00171 static void
00172 updateTimer
00173 (
00174 IN void * context,
00175 IN const time_t& delta
00176 )
00177 throw()
00178 {
00179         ASSERT(context, "null");
00180 
00181         timer_rec_t * tr = (timer_rec_t *) context;
00182 
00183         // increment count of calls, and accumulate the total time
00184         tr->update(delta);
00185 }
00186 
00187 
00188 
00189 ////////////////////////////////////////////////////////////////////////////////
00190 //
00191 //      perf::Timer implementation
00192 //
00193 ////////////////////////////////////////////////////////////////////////////////
00194 
00195 Timer::Timer
00196 (
00197 IN const char * name
00198 )
00199 {
00200         ASSERT(name, "null timer name?");
00201 
00202         if (!s_timersEnabled) {
00203                 m_context = 0;
00204                 return;
00205         }
00206 
00207         // create an empty record for this timer, so destructor doesn't have
00208         // to do any allocations etc.
00209         m_start_time = getNow();
00210         m_context = initializeTimer(name);
00211 }
00212 
00213 
00214 
00215 Timer::~Timer(void)
00216 {
00217         if (!m_context)
00218                 return;
00219 
00220         time_t delta = getNow();
00221         delta.decrement(m_start_time);
00222 
00223         updateTimer(m_context, delta);
00224 }
00225 
00226 
00227 
00228 ////////////////////////////////////////////////////////////////////////////////
00229 //
00230 //      DebugTimer
00231 //
00232 ////////////////////////////////////////////////////////////////////////////////
00233 
00234 DebugTimer::DebugTimer(IN const char * msg)
00235 {
00236         m_start_time = getNow();
00237         m_msg = msg;
00238 }
00239 
00240 
00241 DebugTimer::~DebugTimer(void) throw()
00242 {
00243         time_t now = getNow();
00244         now.decrement(m_start_time);
00245 
00246         long seconds = (long) now.getSeconds();
00247         long hours = seconds / 3600;
00248         long minutes = (seconds % 3600) / 60;
00249         DPRINTF("%s: %ld hours %ld minutes %ld seconds",
00250             m_msg.c_str(), hours, minutes, seconds % 60);
00251 }
00252 
00253 
00254 
00255 ////////////////////////////////////////////////////////////////////////////////
00256 //
00257 //      public API
00258 //
00259 ////////////////////////////////////////////////////////////////////////////////
00260 
00261 time_t
00262 getNow
00263 (
00264 void
00265 )
00266 throw()
00267 {
00268         /* 
00269         Bummer: clock_gettime() is not supported by 2.4* kernels
00270         struct timespec ts;
00271         ASSERT(!clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts),
00272             "Failed clock_gettime() call?");
00273         t.set(ts.tv_sec, (ts.tv_nsec + 500) / 1000);
00274          */
00275 
00276 /*
00277         this uses wall time--blah
00278         HOWEVER it is the only timer that doesn't have rollover problems
00279  */
00280         time_t t;
00281         struct timeval tv;
00282         ASSERT(!gettimeofday(&tv, NULL), "Failed calling gettimeofday()");
00283         t.set(tv.tv_sec, tv.tv_usec);
00284         return t;
00285 /*
00286         // Better: use process clock.  Rolls over every 72 minutes though!
00287         dword_t c = clock();
00288         // ASSERT(-1 != c, "clock() not supported?");
00289         dword_t dwSec = c / CLOCKS_PER_SEC;
00290         long seconds = (long) dwSec;
00291         if (seconds < 0) {
00292                 DPRINTF("Seconds < 0??? seconds = %ld", seconds);
00293                 seconds = 0;
00294         }
00295         long useconds = c % CLOCKS_PER_SEC;
00296         if (useconds < 0) {
00297                 DPRINTF("useconds < 0??? useconds = %ld", useconds);
00298                 useconds = 0;
00299         }
00300         t.set(seconds, useconds);
00301 */
00302 }
00303 
00304 
00305 
00306 void
00307 enableTimers
00308 (
00309 IN bool enable
00310 )
00311 throw()
00312 {
00313         s_timersEnabled = enable;
00314 
00315         if (enable) {
00316                 DPRINTF("Timers enabled");
00317         } else {
00318                 DPRINTF("Timers disabled");
00319         }
00320 }
00321 
00322 
00323 
00324 void
00325 getTimingSummary
00326 (
00327 OUT std::string& out
00328 )
00329 {
00330         out = "";
00331 
00332         char buffer[1024];
00333 
00334         // sort in order of time
00335         map_time_name_t map;
00336         map_timer_t::iterator_t M;
00337         s_timers.getIterator(M);
00338         std::string name;
00339         timer_rec_t * tr = NULL;
00340         while (s_timers.getNextElement(M, name, tr)) {
00341                 ASSERT(tr, "null timer record in timer map?");
00342                 map.insert(map_time_name_t::value_type(tr->total.getSeconds(), name));
00343         }
00344 
00345         // now walk in reverse order (from largest to smallest)
00346         for (map_time_name_t::reverse_iterator i = map.rbegin();
00347              i != map.rend(); ++i) {
00348 
00349                 const std::string& name = i->second;
00350                 timer_rec_t * tr;
00351                 ASSERT(s_timers.lookup(name, tr) && tr,
00352                     "Missing or invalid record for '%s'", name.c_str());
00353 
00354                 out += "  ";
00355 
00356                 sprintf(buffer, "%31s", name.c_str());
00357                 out += buffer;
00358                 out += ": ";
00359 
00360                 double s = tr->total.getSeconds();      // seconds total
00361                 double mpc = 1000.0 * s / tr->calls;    // milliseconds per call
00362 
00363                 int min = 0;            // minutes
00364                 if (s > 90.0) {
00365                         min = int(s / 60.0);
00366                         s = s - (60.0 * min);
00367                 }
00368 
00369                 if (min > 0) {
00370                         sprintf(buffer,
00371                             "%7ld calls, %2d minutes %4.1lf seconds",
00372                             tr->calls, min, s);
00373                 } else {
00374                         sprintf(buffer,
00375                             "%7ld calls, %5.2lf seconds", tr->calls, s);
00376                 }
00377                 out += buffer;
00378 
00379                 if (mpc < 1000.0) {
00380                         sprintf(buffer, ": %6.2lf ms/call", mpc);
00381                         out += buffer;
00382                 }
00383 
00384                 out += "\n";
00385         }
00386 }
00387 
00388 
00389 
00390 void
00391 dumpTimingSummary
00392 (
00393 IO std::ostream& stream
00394 )
00395 {
00396         ASSERT(stream.good(), "bad?");
00397 
00398         std::string summary;
00399         getTimingSummary(summary);
00400         stream << "Timing Summary:\n";
00401         stream << summary;
00402         stream << "\n";
00403 }
00404 
00405 
00406 
00407 };      // perf namespace
00408