Advertisement

processor frequency

Started by March 07, 2003 09:35 AM
9 comments, last by 666_1337 21 years, 8 months ago
hi everybody. i''m writing a game with thousands of things moving arround. since every object is isolated from from most of the other objects, i have to calculate the elapsed time for every unit. ... there''s now about one million gettimeofday calls per sec, and i thought i could speed things up using the 686-assembly rdtsc-command. ... asm("rdtsc\n" "mov %%edx, %0\n" "mov %%eax, 1+%0\n" :"=m"(_time) ; well... now i have the actual processor clocks stored in _time. and substracting the old amount of processor clocks gets me "delta clocks". now i would need to divide this value by the processor''s frequency. but where do i get it from (I of course know it, but i need to run this code on other machines)?
our new version has many new and good features. sadly, the good ones are not new and the new ones are not good
You could simplyfy this by using QueryPerformanceFrequency and it''s associated timer/counter.

Why would you call gettimeofday one million times per second. Surely just call it once per second. It''s not likely to change much in the next second.
Advertisement
You''d have to get it the same way any other app gets it (like DXDiag): By using the actual clock and measuring how many clock cycles happen in a certain amount of time (so 1 million cycles in 1 second would mean 1MHz). The longer the amount of time the more accurate, but keep in mind that clock speed is *NOT* a reliable way to measure time. The rdtsc opcodes are meant for performance profiling on a known machine. Many (and pretty soon all) laptops can vary their clockspeed in realtime in order to conserve battery power, and you''ll likely see this coming to the desktop too (the Pentium 4 already does this in response to temperature). Instead of doing many doing many timeofday calls, why not do one timeofday call for every X units, and cache the result.
you mean i should share an object (or a pointer to a object) to every unit, that contains the elapsed time?

well...

void display(void)
{
struct timeval t;
gettimeofday(&t, NULL);
float delta_T = _t.tv_sec - time.tv_sec + ((_t.tv_usec - time.tv_usec) / 1.0E6);
time = t;

glClear(GL_COLOR_BUFFER_BITS | GL_ACCUM_BUFFER_BITS | GL_DEPTH_BUFFER_BITS | GL_STENCIL_BUFFER_BITS);
glMatrixMode(GL_PROJECTION);
glLoadMatrix(g_projection_matrix);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(-cam -> xRot, 1, 0, 0);
glRotatef(-cam -> yRot, 0, 1, 0);
glRotatef(-cam -> zRot, 0, 0, 1);
glTranslatef( -cam->position.x, -cam->position.y, cam->position.z);

glGetFloatv(GL_MODELVIEW_MATRIX, modl);
g_viewfrustum -> refresh(g_projection_matrix, modl);
g_unit_root -> refreshPositions(delta_T);
g_terrain -> draw();
g_unit_root -> drawAll();
cam -> drawInfos(delta_T);

glutSwapBuffers();
}

and this is the unit's func:

void unitController :: refreshPositions(float elapsedTime)
{
int i;
for(i = 0; i < MAX_UNITS_ACTIVE; i++)
{
if(_units.isUsed)
{
_units.position.x += _units.orientation.x*elapsedTime;<br> _units.position.y += _units.orientation.y*elapsedTime;<br> _units.position.z += _units.orientation.z*elapsedTime;<br><br> _units.orientation.x += _units.isTurningX*elapsedTime*_units.omegaX;<br> _units.orientation.y += _units.isTurningY*elapsedTime*_units.omegaY;<br> _units.orientation.z += _units.isTurningZ*elapsedTime*_units.omegaZ;<br> }<br> }<br> }<br></code><br><br>now this should work…?<br>p.s. how do i make a code box in in a posting?<br><br><SPAN CLASS=editedby>[edited by - 666_1337 &#111;n March 7, 2003 11:12:41 AM]</SPAN> </i>
our new version has many new and good features. sadly, the good ones are not new and the new ones are not good

    [source]< /source > //but same format as above  


[edited by - kordova on March 7, 2003 11:23:23 AM]
I would suggest QueryPerformanceCounter to...There is one issue with using it though...Not on processors support it, though must new ones should..

here is what I use for timing...
PerfTimer.h

  /*  This class is simple to use. Just declare a variable(s) as type CPerfTimer,  call Start() to start timimg and call Stop() to stop timimg. You can pause a  timer by calling Stop() and then you can call Start() to resume. Retrieve the  elapsed time by calling an Elapsed..() function. Assignment, addition,   subtraction and comparison are supported. There are a few information calls  available also. All calls except Start and Stop can be performed on a timer  without stopping it.  */#ifndef __PERFTIMER_H__#define __PERFTIMER_H__class CPerfTimer{public:  CPerfTimer(BOOL bStart = FALSE) {Init(bStart);}  CPerfTimer(const CPerfTimer& Src);   virtual ~CPerfTimer() {;}  void Start(BOOL bReset = FALSE);   // Start from current value or optionally from 0  void Stop();                       // Stop timing. Use Start afterwards to continue.   BOOL IsRunning();                  // Returns FALSE if stopped.    BOOL IsSupported();                // Returns FALSE if performance counter not supported.                                     // Call after constructing at least one CPerfTimer  const double Resolution();         // Returns timer resolution in seconds  const double Resolutionms();       // Returns timer resolution in milliseconds  const double Resolutionus();       // Returns timer resolution in microseconds    const double Elapsed();            // Returns elapsed time in seconds  const double Elapsedms();          // Returns elapsed time in milliseconds   const double Elapsedus();          // Returns elapsed time in microseconds  const CPerfTimer& operator=(const CPerfTimer& Src); // Assignment operator   // Math operators  CPerfTimer operator+(const CPerfTimer& Src) const;	CPerfTimer operator-(const CPerfTimer& Src) const;	const CPerfTimer& operator+=(const CPerfTimer& Src);	const CPerfTimer& operator-=(const CPerfTimer& Src);  // For time in seconds  CPerfTimer operator+(const double Secs) const;	CPerfTimer operator-(const double Secs) const;	const CPerfTimer& operator+=(const double Secs);	const CPerfTimer& operator-=(const double Secs);  // Boolean comparison operators	BOOL operator<(const CPerfTimer& Src);	BOOL operator>(const CPerfTimer& Src);	BOOL operator<=(const CPerfTimer& Src);	BOOL operator>=(const CPerfTimer& Src);  // For time in seconds  BOOL operator<(const double Secs);	BOOL operator>(const double Secs);	BOOL operator<=(const double Secs);	BOOL operator>=(const double Secs);  virtual void Lock() const {;}     // Override for thread safe operation  virtual void Unlock() const {;}     // Override for thread safe operationprotected:  void Init(BOOL bStart);  void Copy(const CPerfTimer& Src);private:  __int64 m_Start;  static __int64 m_Freq;   // does not change while system is running  static __int64 m_Adjust; // Adjustment time it takes to Start and Stop};class CPerfTimerT : public CPerfTimer{ // You only need to use types of this class if a timer is going to be shared between threadspublic:  CPerfTimerT(BOOL bStart = FALSE)  {    m_hMutex = CreateMutex(NULL,FALSE,"");    Init(bStart);  }  CPerfTimerT(const CPerfTimerT& Src)   {     m_hMutex = CreateMutex(NULL,FALSE,"");    Copy(Src);   }  CPerfTimerT(const CPerfTimer& Src)   {     m_hMutex = CreateMutex(NULL,FALSE,"");    Copy(Src);   }  virtual ~CPerfTimerT()   { CloseHandle(m_hMutex); }  const CPerfTimerT& operator=(const CPerfTimerT& Src) // Assignment operator   {    Copy(Src);    return *this;   }   virtual void Lock() const { WaitForSingleObject(m_hMutex,10000); }     virtual void Unlock() const { ReleaseMutex(m_hMutex); }   private:  HANDLE m_hMutex;};inline void CPerfTimer::Init(BOOL bStart){  if (!m_Freq)   { // Initialization should only run once    QueryPerformanceFrequency((LARGE_INTEGER *)&m_Freq);     if (!m_Freq)      m_Freq = 1; // Timer will be useless but will not cause divide by zero    m_Start = 0;     m_Adjust = 0;     Start();            // Time a Stop    Stop();     m_Adjust = m_Start;  }  // This is the only part that normally runs  m_Start = 0;   if (bStart)    Start(); }inline CPerfTimer::CPerfTimer(const CPerfTimer& Src)  {  Copy(Src);}inline void CPerfTimer::Copy(const CPerfTimer& Src){  if (&Src == this)     return; // avoid deadlock if someone tries to copy it to itself  Src.Lock();  Lock();  m_Start = Src.m_Start;   Unlock();  Src.Unlock();}inline void CPerfTimer::Start(BOOL bReset) { // Start from current value or optionally from 0  __int64 i;  QueryPerformanceCounter((LARGE_INTEGER *)&i);  Lock();  if ((!bReset) && (m_Start < 0))    m_Start += i;   // We are starting with an accumulated time  else     m_Start = i;    // Starting from 0  Unlock();} inline void CPerfTimer::Stop() { // Stop timing. Use Start afterwards to continue  Lock();  if (m_Start <= 0)  {    Unlock();    return;          // Was not running  }  __int64 i;  QueryPerformanceCounter((LARGE_INTEGER *)&i);   m_Start += -i;          // Stopped timer keeps elapsed timer ticks as a negative   if (m_Start < m_Adjust) // Do not overflow    m_Start -= m_Adjust;  // Adjust for time timer code takes to run  else     m_Start = 0;          // Stop must have been called directly after Start  Unlock();} inline BOOL CPerfTimer::IsRunning() { // Returns FALSE if stopped.  Lock();  BOOL bRet = (m_Start > 0); // When < 0, holds elpased clicks  Unlock();  return bRet;   } inline const double CPerfTimer::Elapsed(){ // Returns elapsed time in seconds  CPerfTimer Result(*this);  Result.Stop();  return (double)(-Result.m_Start)/(double)m_Freq; }inline const double CPerfTimer::Elapsedms() { // Returns elapsed time in milliseconds  CPerfTimer Result(*this);  Result.Stop();  return (-Result.m_Start*1000.0)/(double)m_Freq; }inline const double CPerfTimer::Elapsedus() { // Returns elapsed time in microseconds  CPerfTimer Result(*this);  Result.Stop();  return (-Result.m_Start * 1000000.0)/(double)m_Freq; }// Assignment operatorinline const CPerfTimer& CPerfTimer::operator=(const CPerfTimer& Src) {  Copy(Src);  return *this; }// Math operatorsinline CPerfTimer CPerfTimer::operator+(const CPerfTimer& Src) const{  CPerfTimer Result(*this);  Result += Src;   return Result; }inline CPerfTimer CPerfTimer::operator-(const CPerfTimer& Src) const{  CPerfTimer Result(*this);  Result -= Src;   return Result; }inline const CPerfTimer& CPerfTimer::operator+=(const CPerfTimer& Src){  CPerfTimer SrcStop(Src);  // Temp is necessary in case Src is not stopped  SrcStop.Stop();  Lock();  m_Start += SrcStop.m_Start;  Unlock();  return *this; }inline const CPerfTimer& CPerfTimer::operator-=(const CPerfTimer& Src){  CPerfTimer SrcStop(Src);  // Temp is necessary in case Src is not stopped  SrcStop.Stop();  Lock();  m_Start -= SrcStop.m_Start;   Unlock();  return *this; }// For time in secondsinline CPerfTimer CPerfTimer::operator+(const double Secs) const{  CPerfTimer Result(*this);  Result += Secs;   return Result; }inline CPerfTimer CPerfTimer::operator-(const double Secs) const{  CPerfTimer Result(*this);  Result += Secs;   return Result; }inline const CPerfTimer& CPerfTimer::operator+=(const double Secs){  Lock();  m_Start -= (__int64)(Secs*(double)m_Freq);  Unlock();  return *this; }inline const CPerfTimer& CPerfTimer::operator-=(const double Secs){  Lock();  m_Start += (__int64)(Secs*(double)m_Freq);  Unlock();  return *this; }// Boolean comparison operatorsinline BOOL CPerfTimer::operator<(const CPerfTimer& Src){   BOOL bRet;   CPerfTimer Temp(Src);  Lock();  if (m_Start <= 0)  {    Temp.Stop();    bRet = (m_Start > Temp.m_Start);     Unlock();    return bRet;  }  else  if (Temp.m_Start > 0)  {    bRet = (m_Start < Temp.m_Start);     Unlock();    return bRet;  }  else  {    Unlock();    CPerfTimer ThisStop(*this);    ThisStop.Stop();    return (ThisStop.m_Start > Temp.m_Start);   }}inline BOOL CPerfTimer::operator>(const CPerfTimer& Src){   BOOL bRet;   CPerfTimer Temp(Src);  Lock();  if (m_Start <= 0)  {    Temp.Stop();    bRet = (m_Start < Temp.m_Start);     Unlock();    return bRet;  }  else  if (Temp.m_Start > 0)  {    bRet = (m_Start > Temp.m_Start);     Unlock();    return bRet;  }  else  {    Unlock();    CPerfTimer ThisStop(*this);    ThisStop.Stop();    return (ThisStop.m_Start < Temp.m_Start);   }}inline BOOL CPerfTimer::operator<=(const CPerfTimer& Src){   return !(*this > Src);}inline BOOL CPerfTimer::operator>=(const CPerfTimer& Src){   return !(*this < Src);}// For time in secondsinline BOOL CPerfTimer::operator<(const double Secs){   BOOL bRet;   Lock();  if (m_Start <= 0)  {    bRet = (m_Start > (__int64)(-Secs*(double)m_Freq));     Unlock();    return bRet;  }  else  {    Unlock();    CPerfTimer ThisStop(*this);    ThisStop.Stop();    return (ThisStop.m_Start > (__int64)(-Secs*(double)m_Freq));   }}inline BOOL CPerfTimer::operator>(const double Secs){   BOOL bRet;   Lock();  if (m_Start <= 0)  {    bRet = (m_Start < (__int64)(-Secs*(double)m_Freq));     Unlock();    return bRet;  }  else  {    Unlock();    CPerfTimer ThisStop(*this);    ThisStop.Stop();    return (ThisStop.m_Start < (__int64)(-Secs*(double)m_Freq));   }}inline BOOL CPerfTimer::operator<=(const double Secs){   return !(*this > Secs);}inline BOOL CPerfTimer::operator>=(const double Secs){   return !(*this < Secs);}#endif //__PERFTIMER_H__  


PerfTimer.c

  #include "stdafx.h"#include "PerfTimer.h"// Declare and initialize static member vars that get set only once and never change__int64 CPerfTimer::m_Freq = 0; __int64 CPerfTimer::m_Adjust = 0; // All functions defined inline for speed. After all, the performance counter is // supposed to be able to time very short events fairly accurately.BOOL CPerfTimer::IsSupported(){ // Returns FALSE if performance counter not supported.  // Call after constructing at least one CPerfTimer  return (m_Freq > 1);}const double CPerfTimer::Resolution()   { // Returns timer resolution in seconds  return 1.0/(double)m_Freq; }const double CPerfTimer::Resolutionms() { // Returns timer resolution in milliseconds  return 1000.0/(double)m_Freq; }const double CPerfTimer::Resolutionus() { // Returns timer resolution in microseconds  return 1000000.0/(double)m_Freq; }  
Advertisement
I think what your saying is that you are making a query to the time elapsed for each object and are looking for a way to reduce the overhead of doing this thousands of times each loop cycle? If this is the case, then QueryPerformaceCounter is probably the worst choice to do this with since it comes with the most overhead (from what I have read here in the forums anyway compared to GetTickCount/timeGetTime). How about having a global or static current_time that gets updated right before you start your loop through thousands of items and using that value to get your time delta for each object instead of making thousands of calls to your get time function? Or at least only update it every x amount of iterations through the loop...

pseudo code
// globalDWORD current_time = timeGetTime();void game_loop(){        // one call to the time function at the beginning    current_time = timeGetTime();    // ... loop through all the objects    int timeDelta = current_time - obj->last_update;    // ... do stuff with timeDelta    obj->last_update = current_time;    //... etc}  


I think I just repeated what Michalson was saying... I hope it helped...

edit: some of the spelling errors... man I suck at typeing


[edited by - evillive2 on March 7, 2003 1:53:17 PM]
Evillive2
good call..This gaming thing is still new to me...QueryPerformanceCounter is about the highest accuracy you can acheive but it does come at a cost...
good call..This gaming thing is still new to me...QueryPerformanceCounter is about the highest accuracy you can acheive but it does come at a cost...
May I suggest another alternative. On Win2K, and XP you can grab the registry key:
HKLM/HARDWARE/DESCRIPTION/System/CentralProcessor/0/~MHz
And if they have two processors you will also see a 1 key. It''s not guaranteed to be there I don''t believe, and it isn''t always completely accurate. But it sure does work well. Also in that key is the processor type, so you can tell if it''s an AMD, Intel, etc. Works pretty well. I''m not sure if it''s on 98, I haven''t run that for a while...



Always remember, you''''re unique. Just like everyone else.

Greven
Always remember, you''re unique. Just like everyone else.Greven

This topic is closed to new replies.

Advertisement