libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
p5 ticker.cpp
Go to the documentation of this file.
1 // Copyright � 1997-2002 by J.M.McGuiness, all rights reserved.
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 //
17 /////////////////////////////////////////////////////////////////////////////
18 
19 #include "stdafx.h"
20 #include <time.h>
21 #include <math.h>
22 
23 #include "p5 ticker.h"
24 
25 #ifdef _DEBUG
26 #define new DEBUG_NEW
27 #undef THIS_FILE
28 static char THIS_FILE[] = __FILE__;
29 #endif
30 
31 /////////////////////////////////////////////////////////////////////////////
32 
33 using namespace libjmmcg;
34 
35 IMPLEMENT_DYNAMIC(P5_Ticker, CObject)
36 
37 #pragma optimize("",off)
38 inline bool P5_Ticker::Check_Has_RDTSC(void) const
39 {
40  SYSTEM_INFO sys_info;
41  GetSystemInfo(&sys_info);
42  if (sys_info.dwProcessorType==PROCESSOR_INTEL_PENTIUM)
43  {
44  try
45  {
46  _asm
47  {
48  _emit 0x0f ; rdtsc
49  _emit 0x31
50  fxch
51  }
52  }
53  catch (...) // Check to see if the opcode is defined.
54  {
55  TRACE0("RDTSC instruction NOT present.\n");
56  return false;
57  }
58  // Check to see if the instruction ticks accesses something that changes.
59  volatile ULARGE_INTEGER ts1,ts2;
60  _asm
61  {
62  xor eax,eax
63  _emit 0x0f ; cpuid
64  _emit 0xa2
65  _emit 0x0f ; rdtsc
66  _emit 0x31
67  mov ts1.HighPart,edx
68  mov ts1.LowPart,eax
69  xor eax,eax
70  _emit 0x0f ; cpuid
71  _emit 0xa2
72  _emit 0x0f ; rdtsc
73  _emit 0x31
74  mov ts2.HighPart,edx
75  mov ts2.LowPart,eax
76  }
77  // If we return true then there's a very good chance it's a real RDTSC instruction!
78  if (ts2.HighPart==ts1.HighPart)
79  {
80  if (ts2.LowPart>ts1.LowPart)
81  {
82  TRACE0("RDTSC instruction probably present.\n");
83  return true;
84  }
85  else
86  {
87  TRACE0("RDTSC instruction NOT present.\n");
88  return false;
89  }
90  }
91  else if (ts2.HighPart>ts1.HighPart)
92  {
93  TRACE0("RDTSC instruction probably present.\n");
94  return true;
95  }
96  else
97  {
98  TRACE0("RDTSC instruction NOT present.\n");
99  return false;
100  }
101  }
102  else
103  {
104  TRACE0("RDTSC instruction NOT present.\n");
105  return false;
106  }
107 }
108 #pragma optimize("",on)
109 
110 P5_Ticker::os_type P5_Ticker::Check_Os(void) const
111 {
112  os_type ret=other;
113  OSVERSIONINFO osvi;
114  osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
115  GetVersionEx(&osvi);
116  if (osvi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS)
117  {
118  ret=win_95;
119  }
120  else if (osvi.dwPlatformId==VER_PLATFORM_WIN32_NT)
121  {
122  ret=win_nt;
123  }
124  return ret;
125 }
126 
127 P5_Ticker::P5_Ticker(void)
128 {
129  on_os=Check_Os();
130  Has_RDTSC=Check_Has_RDTSC();
131  count.QuadPart=0;
132 }
133 
134 inline P5_Ticker::P5_Ticker(const P5_Ticker &ts)
135 {
136  Has_RDTSC=ts.Has_RDTSC;
137  count.QuadPart=ts.count.QuadPart;
138 }
139 
140 P5_Ticker &P5_Ticker::operator=(const P5_Ticker &ts)
141 {
142  Has_RDTSC=ts.Has_RDTSC;
143  count.QuadPart=ts.count.QuadPart;
144  return (*this);
145 }
146 
147 P5_Ticker &P5_Ticker::operator=(const ULARGE_INTEGER &ts)
148 {
149  Has_RDTSC=Check_Has_RDTSC();
150  count.QuadPart=ts.QuadPart;
151  return (*this);
152 }
153 
154 inline P5_Ticker P5_Ticker::operator+(const P5_Ticker &ts1) const
155 {
156  P5_Ticker ts;
157  ts.count.QuadPart=count.QuadPart+ts1.count.QuadPart;
158  return (ts);
159 }
160 
161 inline P5_Ticker P5_Ticker::operator-(const P5_Ticker &ts1) const
162 {
163  P5_Ticker ts;
164  ts.count.QuadPart=count.QuadPart-ts1.count.QuadPart;
165  return (ts);
166 }
167 
168 P5_Ticker &P5_Ticker::Get_Time(void)
169 {
170  if (on_os==win_nt)
171  {
172  Get_Time_NT();
173  }
174  else if (on_os==win_95)
175  {
176  Get_Time_95();
177  }
178  return (*this);
179 }
180 
181 #pragma optimize("",off)
182 inline P5_Ticker &P5_Ticker::Get_Time_NT(void)
183 {
184  if (Has_RDTSC)
185  {
186  volatile ULARGE_INTEGER ts;
187  _asm
188  {
189  xor eax,eax
190  push ecx
191  push edx
192  _emit 0x0f ; cpuid - serialise the processor
193  _emit 0xa2
194  pop edx
195  pop ecx
196  _emit 0x0f ; rdtsc
197  _emit 0x31
198  mov ts.HighPart,edx
199  mov ts.LowPart,eax
200  }
201  TRACE1("Time stamp counter value: quad=%lu.\n",ts.QuadPart);
202  count.QuadPart=ts.QuadPart;
203  }
204  else
205  {
206  count.QuadPart=0;
207  }
208  return (*this);
209 }
210 #pragma optimize("",on)
211 
212 #pragma optimize("",off)
213 inline P5_Ticker &P5_Ticker::Get_Time_95(void)
214 {
215  if (Has_RDTSC)
216  {
217  volatile ULARGE_INTEGER ts;
218  _asm
219  {
220  cli
221  xor eax,eax
222  push ecx
223  push edx
224  _emit 0x0f ; cpuid - serialise the processor
225  _emit 0xa2
226  pop edx
227  pop ecx
228  _emit 0x0f ; rdtsc
229  _emit 0x31
230  mov ts.HighPart,edx
231  mov ts.LowPart,eax
232  sti
233  }
234  TRACE1("Time stamp counter value: quad=%lu.\n",ts.QuadPart);
235  count.QuadPart=ts.QuadPart;
236  }
237  else
238  {
239  count.QuadPart=0;
240  }
241  return (*this);
242 }
243 #pragma optimize("",on)
244 
245 // The following function will work out the processor clock frequency to a
246 // specified accuracy determined by the target average deviation required.
247 // Note that the worst average deviation of the result is less than 5MHz for
248 // a mean frequency of 90MHz. So basically the target average deviation is
249 // supplied only if you want a more accurate result, it won't let you get a
250 // worse one. (Units are Hz.)
251 //
252 // (The average deviation is a better and more robust measure than it's cousin
253 // the standard deviation of a quantity. The item determined by each is
254 // essentially similar. See "Numerical Recipies", W.Press et al for more
255 // details.)
256 //
257 // This function will run for a maximum of 20 seconds before giving up on
258 // trying to improve the average deviation, with the average deviation
259 // actually achieved replacing the supplied target value. Use "max_loops" to
260 // change this. To improve the value the function converges to increase
261 // "interval" (which is in units of ms, default value=1000ms).
262 
263 #pragma optimize("",off)
264 double P5_Ticker::Get_Frequency(double &target_ave_dev,const unsigned long interval,const unsigned int max_loops) const
265 {
266  CWaitCursor wait;
267  register LARGE_INTEGER goal,period,current;
268  register unsigned int ctr=0;
269  double curr_freq,ave_freq; // In Hz.
270  double ave_dev,tmp=0;
271  P5_Ticker s,f;
272  if (!QueryPerformanceFrequency(&period))
273  {
274  throw no_performance_counter;
275  }
276  period.QuadPart*=interval;
277  period.QuadPart/=1000;
278 
279  // Start of tight timed loop.
280  QueryPerformanceCounter(&goal);
281  goal.QuadPart+=period.QuadPart;
282  s.Get_Time();
283  do
284  {
285  QueryPerformanceCounter(&current);
286  } while(current.QuadPart<goal.QuadPart);
287  f.Get_Time();
288  // End of tight timed loop.
289 
290  ave_freq=1000*(f-s).To_Ticks()/interval;
291  do
292  {
293  // Start of tight timed loop.
294  QueryPerformanceCounter(&goal);
295  goal.QuadPart+=period.QuadPart;
296  s.Get_Time();
297  do
298  {
299  QueryPerformanceCounter(&current);
300  } while(current.QuadPart<goal.QuadPart);
301  f.Get_Time();
302  // End of tight timed loop.
303 
304  // Average of the old frequency plus the new.
305  curr_freq=1000*(f-s).To_Ticks()/interval;
306  ave_freq=(curr_freq+ave_freq)/2;
307 
308  // Work out the current average deviation of the frequency.
309  tmp+=fabs(curr_freq-ave_freq);
310  ave_dev=tmp/++ctr;
311  } while (ave_dev>target_ave_dev && ctr<max_loops);
312  target_ave_dev=ave_dev;
313  TRACE2("Estimated the processor clock frequency =%gHz, dev.=�%gHz.\n",ave_freq,ave_dev);
314  return ave_freq;
315 }
316 #pragma optimize("",on)
317 
318 /////////////////////////////////////////////////////////////////////////////
319 // P5_Ticker diagnostics
320 
321 #ifdef _DEBUG
322 void P5_Ticker::AssertValid(void) const
323 {
324  CObject::AssertValid();
325 }
326 
327 void P5_Ticker::Dump(CDumpContext& dc) const
328 {
329  CObject::Dump(dc);
330  dc<<
331  _T("RDTSC instruction detected on target processor:%i.\n")<<Has_RDTSC
332  <<_T("Current value of Time Stamp counter (truncated to unsigned long):%lu.\n")<<(unsigned long)count.QuadPart
333  <<'\n';
334 }
335 #endif //_DEBUG
336 
337 /////////////////////////////////////////////////////////////////////////////