libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
CPUTicker.cpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright © 2002 by J.M.McGuiness, coder@hussar.me.uk
3 **
4 ** This library is free software; you can redistribute it and/or
5 ** modify it under the terms of the GNU Lesser General Public
6 ** License as published by the Free Software Foundation; either
7 ** version 2.1 of the License, or (at your option) any later version.
8 **
9 ** This library is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ** Lesser General Public License for more details.
13 **
14 ** You should have received a copy of the GNU Lesser General Public
15 ** License along with this library; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 // TITLE:
19 // High-Resolution Counter Class.
20 //
21 // DESCRIPTION:
22 // This file declares a class the wraps the Pentium-specific time stamp counter.
23 // This counter has a resolution in terms of PCLKS (processor clocks) so it can
24 // be used for direct instruction timings.
25 // This class has been tested to work on Pentium processors (at least P60's to
26 // P166's), Pentium Pro's and Pentium II's. It also works on AMD K6-233's.
27 //
28 // Extra usage details are in the header file.
29 //
30 // VERSION HISTORY:
31 // 26/3/96 1.0 Created.
32 // 16/7/97 2.0 PJ Naughter, A number of additions including:
33 // - Support for running on Windows NT.
34 // - Now uses the build-in 64 bit data type "__int64".
35 // - Improved diagnostic info thanks to the above.
36 // - Usage of static variables to improve efficiency.
37 // - Addition of a function which will convert from CPU ticks to seconds.
38 // - Improved adhereance to the MFC coding style and standards.
39 // J.M.McGuiness, further modifications.
40 // 22/10/1997 3.0 Modified by J.M.McGuiness, made calling conventions explicitly to.
41 // - Removed MFC coding style and standards, as I don't like them.
42 // - Sped up method for calling appropriate function to read the CPU counter.
43 // - Set the calling convention explicitly to for further optimisation.
44 // 1/12/1997 3.1 Modified to compile with MSVC++ v4.x and to ensure certain functions and structures are defined.
45 // 2/2/1998 3.2 Updated the contact e-mail addresses.
46 // 15/6/1999 3.3 Modified to C++ import mechanisim.
47 // 25/2/2004 See header file for details.
48 /////////////////////////////////////////////////////////////////////////////
49 
50 #include "stdafx.h"
51 
52 #include "CPUTicker.hpp"
53 
54 #include "../../../../core/exception.hpp"
55 
56 using namespace libjmmcg;
57 
58 /////////////////////////////////////////////////////////////////////////////
59 
61 
62 /////////////////////////////////////////////////////////////////////////////
63 // Make it more flexible - newer versions of MSVC++ support the opcodes, so use them to help the optimiser.
64 #if _MSC_VER < 1200
65 # define JMMCG_RDTSC
66  _emit 0x0f ; rdtsc
67  _emit 0x31
68 #else
69 # define JMMCG_RDTSC
70  rdtsc
71 #endif
72 
73 #if _MSC_VER < 1200
74 # define JMMCG_CPUID
75  _emit 0x0f ; cpuid - serialise the processor
76  _emit 0xa2
77 #else
78 # define JMMCG_CPUID
79  cpuid
80 #endif
81 
82 /////////////////////////////////////////////////////////////////////////////
83 
85 
86 /////////////////////////////////////////////////////////////////////////////
87 
88 inline unsigned __int64
89 CPUTicker::CPUCountFn::GetCPUCountFnNT::operator()(void) const {
90  volatile register ULARGE_INTEGER ts; // Used to allow access to the low and high word parts.
91  __asm {
92  xor eax,eax
93  push ecx
94  push edx
96  pop ecx
98  mov ts.HighPart,edx
99  pop edx
100  mov ts.LowPart,eax
101  }
102  return ts.QuadPart;
103 }
104 
105 inline unsigned __int64
106 CPUTicker::CPUCountFn::GetCPUCountFn9X::operator()(void) const {
107  __asm {
108  cli
109  }
110  const unsigned __int64 ret=GetCPUCountFnNT::operator()();
111  __asm {
112  sti
113  }
114  return ret;
115 }
116 
117 #pragma optimize("",off)
118 bool
119 CPUTicker::HasRDTSC(void) {
120  SYSTEM_INFO sys_info;
121  GetSystemInfo(&sys_info);
122  if (sys_info.dwProcessorType==PROCESSOR_INTEL_PENTIUM) {
123  try {
124  __asm {
126  }
127  } catch (...) { // Check to see if the opcode is defined.
128  JMMCG_TRACE(_T("RDTSC instruction NOT present."));
129  return false;
130  }
131  // Check to see if the instruction ticks accesses something that changes.
132  volatile ULARGE_INTEGER ts1,ts2;
133  __asm {
134  xor eax,eax
137  mov ts1.HighPart,edx
138  mov ts1.LowPart,eax
139  xor eax,eax
142  mov ts2.HighPart,edx
143  mov ts2.LowPart,eax
144  }
145  // If we return true then there's a very good chance it's a real RDTSC instruction!
146  if (ts2.HighPart==ts1.HighPart) {
147  if (ts2.LowPart>ts1.LowPart) {
148  JMMCG_TRACE(_T("RDTSC instruction probably present."));
149  return true;
150  } else {
151  JMMCG_TRACE(_T("RDTSC instruction NOT present."));
152  return false;
153  }
154  } else if (ts2.HighPart>ts1.HighPart) {
155  JMMCG_TRACE(_T("RDTSC instruction probably present."));
156  return true;
157  } else {
158  JMMCG_TRACE(_T("RDTSC instruction NOT present."));
159  return false;
160  }
161  } else {
162  JMMCG_TRACE(_T("RDTSC instruction NOT present."));
163  return false;
164  }
165 }
166 #pragma optimize("",on)
167 
168 inline
169 CPUTicker::CPUTicker(void)
170 : counter(),
171  TickCount() {
172  if (!HasRDTSC()) {
173  throw exception_type(_T("RDTSC not supported."),JMMCG_FUNCTION(*this),__REV_INFO__); // No point in instanciating the class if there's no RDTSC instruction!
174  }
175 }
176 
177 inline
178 CPUTicker::CPUTicker(const CPUTicker &ticker)
179 : counter(),
180  TickCount(ticker.TickCount),
181  deviation(ticker.deviation),
182  freq(ticker.freq) {
183 }
184 
185 inline
186 CPUTicker::~CPUTicker(void) {
187 }
188 
189 CPUTicker &
190 CPUTicker::operator=(const CPUTicker &ticker) {
191  TickCount=ticker.TickCount;
192  deviation=ticker.deviation;
193  freq=ticker.freq;
194  return *this;
195 }
196 
197 inline void
198 CPUTicker::GetCPUCount(void) noexcept(true) {
199  TickCount=counter.GetCount();
200 }
201 
202 signed __int64
203 CPUTicker::GetTickCountS(void) const {
204  register unsigned __int64 ticks;
205  if ((ticks=TickCount)>=static_cast<unsigned __int64>(std::numeric_limits<__int64>::max())) {
206  throw exception_type(_T("__int64 range error"),JMMCG_FUNCTION(&CPUTicker::GetTickCountS),__REV_INFO__);
207  }
208  return static_cast<signed __int64>(ticks);
209 }
210 
211 CPUTicker
212 CPUTicker::operator-(const CPUTicker &ticker) const {
213  register CPUTicker tmp(*this);
214  tmp.TickCount=TickCount-ticker.TickCount;
215  if (TickCount<ticker.TickCount) {
216  throw exception_type(_T("__int64 range error"),JMMCG_FUNCTION(&CPUTicker::operator-),__REV_INFO__);
217  }
218  return tmp;
219 }
220 
221 #pragma optimize("",off)
222 const bool
223 CPUTicker::GetAndCalcCPUFrequency(double &frequency,double &target_ave_dev,const unsigned long interval,const unsigned int max_loops) {
224  register LARGE_INTEGER goal,period,current;
225  register unsigned int ctr=0;
226  double curr_freq,ave_freq; // In Hz.
227  double ave_dev,tmp=0;
228  CPUTicker s,f;
229  if (!QueryPerformanceFrequency(&period)) {
230  return true;
231  }
232  period.QuadPart*=interval;
233  period.QuadPart/=1000;
234 
235  // Start of tight timed loop.
236  QueryPerformanceCounter(&goal);
237  goal.QuadPart+=period.QuadPart;
238  s.GetCPUCount();
239  do {
240  QueryPerformanceCounter(&current);
241  } while(current.QuadPart<goal.QuadPart);
242  f.GetCPUCount();
243  // End of tight timed loop.
244 
245  register unsigned __int64 ticks;
246  if ((ticks=f.TickCount-s.TickCount)>=static_cast<unsigned __int64>(std::numeric_limits<__int64>::max())) {
247  throw exception_type(_T("__int64 range error"),JMMCG_FUNCTION(&CPUTicker::GetAndCalcCPUFrequency),__REV_INFO__);
248  }
249  ave_freq=1000*static_cast<double>(static_cast<signed __int64>(ticks))/interval;
250  do {
251  // Start of tight timed loop.
252  QueryPerformanceCounter(&goal);
253  goal.QuadPart+=period.QuadPart;
254  s.GetCPUCount();
255  do {
256  QueryPerformanceCounter(&current);
257  } while(current.QuadPart<goal.QuadPart);
258  f.GetCPUCount();
259  // End of tight timed loop.
260 
261  // Average of the old frequency plus the new.
262  if ((ticks=f.TickCount-s.TickCount)>=static_cast<unsigned __int64>(std::numeric_limits<__int64>::max())) {
263  throw exception_type(_T("__int64 range error"),JMMCG_FUNCTION(&CPUTicker::GetAndCalcCPUFrequency),__REV_INFO__);
264  }
265  curr_freq=1000*static_cast<double>(static_cast<signed __int64>(ticks))/interval;
266  ave_freq=(curr_freq+ave_freq)/2;
267 
268  // Work out the current average deviation of the frequency.
269  tmp+=fabs(curr_freq-ave_freq);
270  ave_dev=tmp/++ctr;
271  } while (ave_dev>target_ave_dev && ctr<max_loops);
272  deviation=target_ave_dev=ave_dev;
273  freq=frequency=ave_freq;
274  JMMCG_TRACE(_T("Estimated the processor clock frequency =")<<ave_freq<<_T("Hz, dev.=�")<<ave_dev<<_T("Hz."));
275  return false;
276 }
277 #pragma optimize("",on)