/lib/ode/ode_source/ode/src/timer.cpp
C++ | 423 lines | 288 code | 77 blank | 58 comment | 24 complexity | ea84ff575c0d3f13ab4f017208b7c215 MD5 | raw file
1/************************************************************************* 2 * * 3 * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * 4 * All rights reserved. Email: russ@q12.org Web: www.q12.org * 5 * * 6 * This library is free software; you can redistribute it and/or * 7 * modify it under the terms of EITHER: * 8 * (1) The GNU Lesser General Public License as published by the Free * 9 * Software Foundation; either version 2.1 of the License, or (at * 10 * your option) any later version. The text of the GNU Lesser * 11 * General Public License is included with this library in the * 12 * file LICENSE.TXT. * 13 * (2) The BSD-style license that is included with this library in * 14 * the file LICENSE-BSD.TXT. * 15 * * 16 * This library is distributed in the hope that it will be useful, * 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files * 19 * LICENSE.TXT and LICENSE-BSD.TXT for more details. * 20 * * 21 *************************************************************************/ 22 23/* 24 25TODO 26---- 27 28* gettimeofday() and the pentium time stamp counter return the real time, 29 not the process time. fix this somehow! 30 31*/ 32 33#include <ode/common.h> 34#include <ode/timer.h> 35#include "config.h" 36#include "util.h" 37 38// misc defines 39#define ALLOCA dALLOCA16 40 41//**************************************************************************** 42// implementation for windows based on the multimedia performance counter. 43 44#ifdef WIN32 45 46#include "windows.h" 47 48static inline void getClockCount (unsigned long cc[2]) 49{ 50 LARGE_INTEGER a; 51 QueryPerformanceCounter (&a); 52 cc[0] = a.LowPart; 53 cc[1] = a.HighPart; 54} 55 56 57static inline void serialize() 58{ 59} 60 61 62static inline double loadClockCount (unsigned long cc[2]) 63{ 64 LARGE_INTEGER a; 65 a.LowPart = cc[0]; 66 a.HighPart = cc[1]; 67 return double(a.QuadPart); 68} 69 70 71double dTimerResolution() 72{ 73 return 1.0/dTimerTicksPerSecond(); 74} 75 76 77double dTimerTicksPerSecond() 78{ 79 static int query=0; 80 static double hz=0.0; 81 if (!query) { 82 LARGE_INTEGER a; 83 QueryPerformanceFrequency (&a); 84 hz = double(a.QuadPart); 85 query = 1; 86 } 87 return hz; 88} 89 90#endif 91 92//**************************************************************************** 93// implementation based on the pentium time stamp counter. the timer functions 94// can be serializing or non-serializing. serializing will ensure that all 95// instructions have executed and data has been written back before the cpu 96// time stamp counter is read. the CPUID instruction is used to serialize. 97 98#if defined(PENTIUM) && !defined(WIN32) 99 100// we need to know the clock rate so that the timing function can report 101// accurate times. this number only needs to be set accurately if we're 102// doing performance tests and care about real-world time numbers - otherwise, 103// just ignore this. i have not worked out how to determine this number 104// automatically yet. 105 106#define PENTIUM_HZ (500e6) 107 108static inline void getClockCount (unsigned long cc[2]) 109{ 110#ifndef X86_64_SYSTEM 111 asm volatile ( 112 "rdtsc\n" 113 "movl %%eax,(%%esi)\n" 114 "movl %%edx,4(%%esi)\n" 115 : : "S" (cc) : "%eax","%edx","cc","memory"); 116#else 117 asm volatile ( 118 "rdtsc\n" 119 "movl %%eax,(%%rsi)\n" 120 "movl %%edx,4(%%rsi)\n" 121 : : "S" (cc) : "%eax","%edx","cc","memory"); 122#endif 123} 124 125 126static inline void serialize() 127{ 128#ifndef X86_64_SYSTEM 129 asm volatile ( 130 "mov $0,%%eax\n" 131 "push %%ebx\n" 132 "cpuid\n" 133 "pop %%ebx\n" 134 : : : "%eax","%ecx","%edx","cc","memory"); 135#else 136 asm volatile ( 137 "mov $0,%%rax\n" 138 "push %%rbx\n" 139 "cpuid\n" 140 "pop %%rbx\n" 141 : : : "%rax","%rcx","%rdx","cc","memory"); 142#endif 143} 144 145 146static inline double loadClockCount (unsigned long a[2]) 147{ 148 double ret; 149#ifndef X86_64_SYSTEM 150 asm volatile ("fildll %1; fstpl %0" : "=m" (ret) : "m" (a[0]) : 151 "cc","memory"); 152#else 153 asm volatile ("fildll %1; fstpl %0" : "=m" (ret) : "m" (a[0]) : 154 "cc","memory"); 155#endif 156 return ret; 157} 158 159 160double dTimerResolution() 161{ 162 return 1.0/PENTIUM_HZ; 163} 164 165 166double dTimerTicksPerSecond() 167{ 168 return PENTIUM_HZ; 169} 170 171#endif 172 173//**************************************************************************** 174// otherwise, do the implementation based on gettimeofday(). 175 176#if !defined(PENTIUM) && !defined(WIN32) 177 178#ifndef macintosh 179 180#include <sys/time.h> 181#include <unistd.h> 182 183 184static inline void getClockCount (unsigned long cc[2]) 185{ 186 struct timeval tv; 187 gettimeofday (&tv,0); 188 cc[0] = tv.tv_usec; 189 cc[1] = tv.tv_sec; 190} 191 192#else // macintosh 193 194#include <CoreServices/CoreServices.h> 195#include <ode/Timer.h> 196 197static inline void getClockCount (unsigned long cc[2]) 198{ 199 UnsignedWide ms; 200 Microseconds (&ms); 201 cc[1] = ms.lo / 1000000; 202 cc[0] = ms.lo - ( cc[1] * 1000000 ); 203} 204 205#endif 206 207 208static inline void serialize() 209{ 210} 211 212 213static inline double loadClockCount (unsigned long a[2]) 214{ 215 return a[1]*1.0e6 + a[0]; 216} 217 218 219double dTimerResolution() 220{ 221 unsigned long cc1[2],cc2[2]; 222 getClockCount (cc1); 223 do { 224 getClockCount (cc2); 225 } 226 while (cc1[0]==cc2[0] && cc1[1]==cc2[1]); 227 do { 228 getClockCount (cc1); 229 } 230 while (cc1[0]==cc2[0] && cc1[1]==cc2[1]); 231 double t1 = loadClockCount (cc1); 232 double t2 = loadClockCount (cc2); 233 return (t1-t2) / dTimerTicksPerSecond(); 234} 235 236 237double dTimerTicksPerSecond() 238{ 239 return 1000000; 240} 241 242#endif 243 244//**************************************************************************** 245// stop watches 246 247void dStopwatchReset (dStopwatch *s) 248{ 249 s->time = 0; 250 s->cc[0] = 0; 251 s->cc[1] = 0; 252} 253 254 255void dStopwatchStart (dStopwatch *s) 256{ 257 serialize(); 258 getClockCount (s->cc); 259} 260 261 262void dStopwatchStop (dStopwatch *s) 263{ 264 unsigned long cc[2]; 265 serialize(); 266 getClockCount (cc); 267 double t1 = loadClockCount (s->cc); 268 double t2 = loadClockCount (cc); 269 s->time += t2-t1; 270} 271 272 273double dStopwatchTime (dStopwatch *s) 274{ 275 return s->time / dTimerTicksPerSecond(); 276} 277 278//**************************************************************************** 279// code timers 280 281// maximum number of events to record 282#define MAXNUM 100 283 284static int num = 0; // number of entries used in event array 285static struct { 286 unsigned long cc[2]; // clock counts 287 double total_t; // total clocks used in this slot. 288 double total_p; // total percentage points used in this slot. 289 int count; // number of times this slot has been updated. 290 const char *description; // pointer to static string 291} event[MAXNUM]; 292 293 294// make sure all slot totals and counts reset to 0 at start 295 296static void initSlots() 297{ 298 static int initialized=0; 299 if (!initialized) { 300 for (int i=0; i<MAXNUM; i++) { 301 event[i].count = 0; 302 event[i].total_t = 0; 303 event[i].total_p = 0; 304 } 305 initialized = 1; 306 } 307} 308 309 310void dTimerStart (const char *description) 311{ 312 initSlots(); 313 event[0].description = const_cast<char*> (description); 314 num = 1; 315 serialize(); 316 getClockCount (event[0].cc); 317} 318 319 320void dTimerNow (const char *description) 321{ 322 if (num < MAXNUM) { 323 // do not serialize 324 getClockCount (event[num].cc); 325 event[num].description = const_cast<char*> (description); 326 num++; 327 } 328} 329 330 331void dTimerEnd() 332{ 333 if (num < MAXNUM) { 334 serialize(); 335 getClockCount (event[num].cc); 336 event[num].description = "TOTAL"; 337 num++; 338 } 339} 340 341//**************************************************************************** 342// print report 343 344static void fprintDoubleWithPrefix (FILE *f, double a, const char *fmt) 345{ 346 if (a >= 0.999999) { 347 fprintf (f,fmt,a); 348 return; 349 } 350 a *= 1000.0; 351 if (a >= 0.999999) { 352 fprintf (f,fmt,a); 353 fprintf (f,"m"); 354 return; 355 } 356 a *= 1000.0; 357 if (a >= 0.999999) { 358 fprintf (f,fmt,a); 359 fprintf (f,"u"); 360 return; 361 } 362 a *= 1000.0; 363 fprintf (f,fmt,a); 364 fprintf (f,"n"); 365} 366 367 368void dTimerReport (FILE *fout, int average) 369{ 370 int i; 371 size_t maxl; 372 double ccunit = 1.0/dTimerTicksPerSecond(); 373 fprintf (fout,"\nTimer Report ("); 374 fprintDoubleWithPrefix (fout,ccunit,"%.2f "); 375 fprintf (fout,"s resolution)\n------------\n"); 376 if (num < 1) return; 377 378 // get maximum description length 379 maxl = 0; 380 for (i=0; i<num; i++) { 381 size_t l = strlen (event[i].description); 382 if (l > maxl) maxl = l; 383 } 384 385 // calculate total time 386 double t1 = loadClockCount (event[0].cc); 387 double t2 = loadClockCount (event[num-1].cc); 388 double total = t2 - t1; 389 if (total <= 0) total = 1; 390 391 // compute time difference for all slots except the last one. update totals 392 double *times = (double*) ALLOCA (num * sizeof(double)); 393 for (i=0; i < (num-1); i++) { 394 double t1 = loadClockCount (event[i].cc); 395 double t2 = loadClockCount (event[i+1].cc); 396 times[i] = t2 - t1; 397 event[i].count++; 398 event[i].total_t += times[i]; 399 event[i].total_p += times[i]/total * 100.0; 400 } 401 402 // print report (with optional averages) 403 for (i=0; i<num; i++) { 404 double t,p; 405 if (i < (num-1)) { 406 t = times[i]; 407 p = t/total * 100.0; 408 } 409 else { 410 t = total; 411 p = 100.0; 412 } 413 fprintf (fout,"%-*s %7.2fms %6.2f%%",(int)maxl,event[i].description, 414 t*ccunit * 1000.0, p); 415 if (average && i < (num-1)) { 416 fprintf (fout," (avg %7.2fms %6.2f%%)", 417 (event[i].total_t / event[i].count)*ccunit * 1000.0, 418 event[i].total_p / event[i].count); 419 } 420 fprintf (fout,"\n"); 421 } 422 fprintf (fout,"\n"); 423}