1 /*
2  *  Portable Agile C++ Classes (PACC)
3  *  Copyright (C) 2004 by Marc Parizeau
4  *  http://manitou.gel.ulaval.ca/~parizeau/PACC
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2.1 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  Contact:
21  *  Laboratoire de Vision et Systemes Numeriques
22  *  Departement de genie electrique et de genie informatique
23  *  Universite Laval, Quebec, Canada, G1K 7P4
24  *  http://vision.gel.ulaval.ca
25  *
26  */
27 
28 /*!
29  * \file PACC/Util/Timer.cpp
30  * \brief Class methods for the portable timer.
31  * \author Marc Parizeau, Laboratoire de vision et systèmes numériques, Université Laval
32  * $Revision: 1.5.2.1 $
33  * $Date: 2007/09/10 18:24:10 $
34  */
35 
36 #include "Util/Timer.hpp"
37 #include "Util/Assert.hpp"
38 
39 #ifdef WIN32
40 #include <windows.h>
41 #include <stdexcept>
42 #else
43 #include <sys/time.h>
44 #include <unistd.h>
45 #endif
46 
47 using namespace std;
48 using namespace PACC;
49 
50 double Timer::mPeriod = 0;
51 
52 /*!
53 Under Windows, this method always calls the QueryPerformanceFrequency function to determine the count period of the hardware time-stamp.
54 
55 Under Unix, with hardware mode activated, when using the gcc compiler on an i386 family processor (Pentium or AMD), or on a PowerPC family processor, this method calibrates the hardware time-stamp counter using the standard gettimeofday function. This calibration is conducted by measuring a delay of approximatly \c inDelay micro-seconds, and the count period is averaged over \c inTimes runs. In all other cases, or when the hardware mode is deactivated, the count period is fixed at 1 micro-second (the maximum resolution of gettimeofday).
56 
57 This method is called automatically by the class constructor. Under normal circumstances, the user should not concern himself with calibration.
58  */
calibrateCountPeriod(unsigned int inDelay,unsigned int inTimes)59 void Timer::calibrateCountPeriod(unsigned int inDelay, unsigned int inTimes)
60 {
61 #ifdef WIN32
62 	// use the windows counter
63 	LARGE_INTEGER lFrequency;
64 	PACC_AssertM(QueryPerformanceFrequency(&lFrequency), "Timer::Timer() no performance counter on this processor!");
65 	mPeriod = 1. / lFrequency.QuadPart;
66 #else
67 	if(mHardware) {
68 #if defined (__GNUG__) && (defined (__i386__) || defined (__ppc__))
69 		double lPeriod = 0;
70 		// calibrate by matching the time-stamps with the micro-seconds of gettimeofday
71 		for(unsigned int i = 0; i < inTimes; ++ i) {
72 			timeval lStartTime, lTime;
73 			::gettimeofday(&lStartTime, 0);
74 			unsigned long long lStartCount = getCount();
75 			::usleep(inDelay);
76 			::gettimeofday(&lTime, 0);
77 			unsigned long long lCount = getCount() - lStartCount;
78 			lTime.tv_sec -= lStartTime.tv_sec;
79 			lTime.tv_usec -= lStartTime.tv_usec;
80 			// dismiss the first run of the loop
81 			if(i != 0) lPeriod += (lTime.tv_sec + lTime.tv_usec*0.000001)/lCount;
82 		}
83 		mPeriod = lPeriod/(inTimes-1);
84 #else
85 		// use the microseconds of gettimeofday
86 		mPeriod = 0.000001;
87 #endif
88 	} else {
89 		// use the microseconds of gettimeofday
90 		mPeriod = 0.000001;
91 	}
92 #endif
93 }
94 
95 /*!
96 This method returns the highest resolution count available for this timer. Under Windows, it returns the hardware performance counter value as provided by the QueryPerformanceCounter method.
97 
98 Under Unix, when using the gcc compiler on an i386 family processor (Pentium or AMD), or on a PowerPC family processor, this method can also return the hardware performance counter value using in-lined assembly code. On all other platforms, or when the hardware mode is deactivated, it returns a count based on the standard gettimeofday function (in micro-seconds).
99  */
getCount(void) const100 unsigned long long Timer::getCount(void) const
101 {
102 	unsigned long long lCount = 0;
103 #ifdef WIN32
104 	LARGE_INTEGER lCurrent;
105 	QueryPerformanceCounter(&lCurrent);
106 	lCount = lCurrent.QuadPart;
107 #else
108 	if(mHardware) {
109 #if defined (__GNUG__) && defined (__i386__)
110 		__asm__ volatile("rdtsc" : "=A" (lCount));
111 #else
112 #if defined (__GNUG__) && defined (__ppc__)
113 		register unsigned int lLow;
114 		register unsigned int lHigh1;
115 		register unsigned int lHigh2;
116 		do {
117 			// make sure that high bits have not changed
118 			__asm__ volatile ( "mftbu %0" : "=r" (lHigh1) );
119 			__asm__ volatile ( "mftb  %0" : "=r" (lLow) );
120 			__asm__ volatile ( "mftbu %0" : "=r" (lHigh2) );
121 		} while(lHigh1 != lHigh2);
122 		// transfer to lCount
123 		unsigned int *lPtr = (unsigned int*) &lCount;
124 		*lPtr++ = lHigh1; *lPtr = lLow;
125 #else
126 		timeval lCurrent;
127 		::gettimeofday(&lCurrent, 0);
128 		lCount = (unsigned long long)lCurrent.tv_sec*1000000 + lCurrent.tv_usec;
129 #endif
130 #endif
131 	} else {
132 		timeval lCurrent;
133 		::gettimeofday(&lCurrent, 0);
134 		lCount = (unsigned long long)lCurrent.tv_sec*1000000 + lCurrent.tv_usec;
135 	}
136 #endif
137 	return lCount;
138 }
139