Jump to content
  • Advertisement
Sign in to follow this  
xyuri

QueryPerformanceCounter woes.

This topic is 4906 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Having lost all my source code in a recent hard disk format I dont have my good ole performance timer that I've used in previous little games, so I'm back on the web looking it all up and rebuilding the functions. Anyways, I have the following basic code to record time differences, but near the beginning it sometimes gives negative numbers, why?
LARGE_INTEGER lastTime;
LARGE_INTEGER tickFreq;
int ready = 0;

//QueryPerformanceCounter(&time);
//QueryPerformanceFrequency(&rate);
//double calc = (double) time.QuadPart / (double)rate.QuadPart;

void timerPrep(void)
{
	QueryPerformanceFrequency(&tickFreq);
	QueryPerformanceCounter(&lastTime);
	ready = 1;
}

double timerTick(void)
{
	if(ready == 0) {return 0;}

	LARGE_INTEGER thisTime;
	double theDifference;

	QueryPerformanceCounter(&thisTime);
	theDifference = ((double)thisTime.QuadPart - (double)lastTime.QuadPart) * tickFreq.QuadPart;

	lastTime = thisTime;

	return theDifference;	
}
I have written this basic console app to do some rough testing
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

LARGE_INTEGER lastTime;
LARGE_INTEGER tickFreq;
int ready = 0;

//QueryPerformanceCounter(&time);
//QueryPerformanceFrequency(&rate);
//double calc = (double) time.QuadPart / (double)rate.QuadPart;

void timerPrep(void)
{
	QueryPerformanceFrequency(&tickFreq);
	QueryPerformanceCounter(&lastTime);
	ready = 1;
}

double timerTick(void)
{
	if(ready == 0) {return 0;}

	LARGE_INTEGER thisTime;
	double theDifference;

	QueryPerformanceCounter(&thisTime);
	theDifference = ((double)thisTime.QuadPart - (double)lastTime.QuadPart) * tickFreq.QuadPart;

	lastTime = thisTime;

	return theDifference;	
}


int main(int argc, char *argv[])
{

  timerPrep();
  
  /*int i = 0;
  for (i = 0; i < 1000; ++i)
  {
    int lala = 4009;
    int boo = i / lala;
  }*/

  system("PAUSE");

  printf("Took %i\n", timerTick());

  printf("laaaa\n");
  system("PAUSE");
  return 0;
}
Resources on the web on how to use these functions seem rather vague ... does anyone know what I'm doing wrong at a glance?

Share this post


Link to post
Share on other sites
Advertisement
I had this problem a while ago...turnde out I was just using the low 32 bits which is obviously VERY wrong ;o) you're not though so lets move on.

It looks to me (without looking at my code as Im at work) like you should be dividing by the frequency...not multiplying by it...to get the time difference? you're mul'ing a 64bit number by another probably large 64 bit number...so Im guessing you're just overflowing and "theDifference" is wrapping to negative

? Could be wrong...I've not tested the theory.

Also perhaps you can save on a cast?

theDifference = (double)(thisTime.QuadPart - lastTime.QuadPart)\tickFreq.QuadPart;

again not tested and I can never decide if that sorta thing will work without plugging in the code... =op

good luck!

Share this post


Link to post
Share on other sites
Quote:
Original post by xyuri
Having lost all my source code in a recent hard disk format I dont have my good ole performance timer that I've used in previous little games, so I'm back on the web looking it all up and rebuilding the functions.

Anyways, I have the following basic code to record time differences, but near the beginning it sometimes gives negative numbers, why?

<code snip>

I have written this basic console app to do some rough testing

*** Source Snippet Removed ***

Resources on the web on how to use these functions seem rather vague ... does anyone know what I'm doing wrong at a glance?



double timerTick(void)
...
printf("Took %i\n", timerTick());


Are you sure? %i is an integer you know :) (use %f).

This was the first error. But the more important is that you must divide by the time freq, not multiply by it. It is a frequency, not a period.

Hence:


theDifference = ((double)thisTime.QuadPart - (double)lastTime.QuadPart) / double(tickFreq.QuadPart);


And then it seems to works...

HTH,

Share this post


Link to post
Share on other sites
As far as I can see, you are casting DWORDs into double's where you shouldnt.

theDifference = ((double)thisTime.QuadPart - (double)lastTime.QuadPart) * tickFreq.QuadPart;

should be:

theDifference = thisTime.QuadPart - lastTime.QuadPart;

where theDifference is a DWORD as well, divide by the total ticks per second and you should have your double there (typecast it when converting to seconds, not before), remember the quadparts are DWORD's

Good luck :)

p.s. it might work anyway, but it seems like a waste

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
don't use LARGE_INTEGER, you can use __int64s with those functions. then take difference between __int64s and convert to double, then divide by the frequency.

Share this post


Link to post
Share on other sites
// link with winmm.lib

unsigned long GetTime(void) // returns the time in milliseconds
{
unsigned __int64 frequency;
unsigned __int64 count;

if (QueryPerformanceFrequency((ULARGE_INTEGER *)&frequency))
{
QueryPerformanceCounter((ULARGE_INTEGER *)&count);

return (unsigned long)(count * 1000 / frequency);

}

return timeGetTime();

}

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
don't use LARGE_INTEGER, you can use __int64s with those functions. then take difference between __int64s and convert to double, then divide by the frequency.


Well, LARGE_INTEGER is the type which is used by QPC and QFF. Since this is a type abstraction (we don't have to worry about the underlying data type), I believe we should still use them.


QueryPerformanceCounter((LARGE_INTEGER *)&my_i64_var);


Is probably more dangerous, and I'm not sure it is really needed.

Regards,

Share this post


Link to post
Share on other sites
Quote:
Original post by Emmanuel Deloget
Quote:
Original post by Anonymous Poster
don't use LARGE_INTEGER, you can use __int64s with those functions. then take difference between __int64s and convert to double, then divide by the frequency.


Well, LARGE_INTEGER is the type which is used by QPC and QFF. Since this is a type abstraction (we don't have to worry about the underlying data type), I believe we should still use them.


QueryPerformanceCounter((LARGE_INTEGER *)&my_i64_var);


Is probably more dangerous, and I'm not sure it is really needed.

Regards,


Taken from the windows header files



typedef __int64 LONGLONG;

#if defined(MIDL_PASS)
typedef struct _LARGE_INTEGER {
#else // MIDL_PASS
typedef union _LARGE_INTEGER {
struct {
DWORD LowPart;
LONG HighPart;
};
struct {
DWORD LowPart;
LONG HighPart;
} u;
#endif //MIDL_PASS
LONGLONG QuadPart;
} LARGE_INTEGER;





as you can see __int64 is the same as LARGE_INTEGER.

so

why use __int64 ?

-> because MSVC++ 7 has built in support for __int64

what does this mean ?

-> it means we can multiply and divide it safely in the function I detailed above

what if I dont have MSVC++ 7

-> then it is not necessary to use __int64, but you will have to implement the division and multiplication yourself

Share this post


Link to post
Share on other sites
Quote:
Original post by ZedFx
Taken from the windows header files

*** Source Snippet Removed ***

as you can see __int64 is the same as LARGE_INTEGER


Yes. I didn't say it was wrong. Hum. Well. As for today, it is true. But if I have a look to a particular implementation of the STL I'll have überl33t hints to find new way to use my vector. Is it a good idea ?

When an API gives you an abstracted data type to use, it is actually better to use it. Moreover, I think it is actually a very bad idea to tell a beginner to use what I might call "a hack". I don't call it a hack because it is bad, I call it like this because it assumes the knowledge of the internals of the API you are using (in this case, you had to search though the windows header to find it. It is not documented anywhere. A LARGE_INTEGER resolves to a LONGLONG or to a struct, not to a __int64).

Plus, you had to add those casts everwhere to enable everything to compile. Will they work if they go to __int128 when they'll release longhorn?

Quote:
Original post by ZedFx
so why use __int64 ?
-> because MSVC++ 7 has built in support for __int64
what does this mean ?
-> it means we can multiply and divide it safely in the function I detailed above
what if I dont have MSVC++ 7
-> then it is not necessary to use __int64, but you will have to implement the division and multiplication yourself


I hope their is support for LONGLONG as well :) More seriously, No offense but this cannot be an argument. Because VC7 supports __int64, I have to use it instead of an abstracted type? If I use gcc then LONGLONG is a typedef to long long. Should I change all my code? Should I make two version of my code to compile on both compiler? Just because they have support for a particular, non standard buitin type?

Hope you get my point, regards,

Share this post


Link to post
Share on other sites
w00000t ! :-) Thanks very much fellas. Yes, it appears that dividing by the frequency can be wuite useful! I really love the friendliness of everyone on these boards :-) Thanks again.

Just so all can see, here is my updated timer class:

LARGE_INTEGER timerLast;
LARGE_INTEGER timerThis;
LARGE_INTEGER timerFreq;
int timerReady = 0;

void timerPrep(void)
{
QueryPerformanceFrequency(&timerFreq);
QueryPerformanceCounter(&timerThis);
timerReady = 1;
}

double timerTick(void)
{
// If timerPrep function hasnt been run then return 0
if(timerReady == 0) {return 0;}
// LAST = THIS so that THIS can be updated and the difference returned
timerLast = timerThis;
// Update THIS
QueryPerformanceCounter(&timerThis);
// Return the Difference
return (double)(timerThis.QuadPart - timerLast.QuadPart) / (double)timerFreq.QuadPart;
}


[Edited by - xyuri on February 9, 2005 5:22:23 PM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!