[Win32] QueryPerformance Anomalities

Started by
23 comments, last by GameDev.net 17 years, 6 months ago
For the past two days, I've seen pink-spotted elephingos flying around throwing pumpkins at kids because this weird behaviour of QueryPerformanceCounter and QueryPerformanceFrequency.
[source lang = "cpp"]

//////
// my objects constructor
//////
if( !QueryPerformanceFrequency( &m_frequency ) )
 throw "QueryPerformanceFrequency failed";

//////
// my objects update-function
//////
LARGE_INTEGER start;
if( !QueryPerformanceCounter( &start ) )
 throw "QueryPerformanceCounter@start failed";

// do some computations, time-space-continuum disruptions and coffe...

LARGE_INTEGER end;
if( !QueryPerformanceCounter( &end ) )
 throw "QueryPerformanceCounter@end failed";

// calculate the time between start and end
double time = static_cast < double >( ( end.QuadPart - start.QuadPart ) / static_cast < double >( m_frequency.QuadPart ) );

// keep track of time
static double counter = 0;
counter += time;

// average computations per second
double computations = 1.0 / time;

// run the app for approximately 1 second
if( counter >= 1.0 )
 return computations;




Unless my keen (yeah, right) eye got this wrong, the code seems valid, but the results are not. When my function returns after what's supposed to be 1 second, the application has in fact been active for 10 seconds. It seems that my "time" is 10 times less than what it should be. I've tried this on my two computers; one with and AMD64 and one AMD AthlonXP cpu, but the same behaviour exist on both. edit: i forgot to typecast the frequency to double, but the problem is still there [Edited by - Wixner on October 22, 2006 5:01:55 PM]
Advertisement
Consider yourself lucky... I occasionally get a negative number (like 1 out of 20 times). I don't think it's very reliable?

btw, you have a static_cast<double>(LONGLONG / double) above... I'm not sure what the result of a LONGLONG / double would be, but maybe that's the source of your problem?

Hey, this link seems to have some more info...
I looked through the link and that problem currently only occurs on dual-core Amd64 cpu's. The most annoying thing is that I know I've used this code earlier (a month or two ago).

I've just tested the code on a new fresh project, and the anomaly is still there:

[source lang = "cpp"]#include <windows.h>#include <iostream>int main( int argc, char** argv ){	LARGE_INTEGER frequency;	QueryPerformanceFrequency( &frequency );	double _frequency = static_cast < double >( frequency.QuadPart );		double counter = 0;	while( counter < 10 )	{		LARGE_INTEGER start = { 0 };		QueryPerformanceCounter( &start );		double _start = static_cast < double >( start.QuadPart );		// seems correct	//	Sleep( 10 );		// seems incorrect	//	Sleep( 1 );		// the more work we throw to our cpu, the more it seems to differ	//	for( int i = 0; i < 100000; ++i )	//		float a = i * 3.1415192f;				LARGE_INTEGER end = { 0 };		QueryPerformanceCounter( &end );		double _end = static_cast < double >( end.QuadPart ); 		double time = ( _end - _start ) / _frequency;		counter += time; 		std::cout << counter;		// my knowledge in console programming is.. limitied ;P		for( int i = 0; i < 1000; ++i )			std::cout << "\b";	}}


If you guys got the time, could you please check this out and see if the counter (supposed to count on a second-basis) seems correct?
Yeah duel core screws it up. You will get negative times in some cases, but don't worry, I got the solution right here for ya [smile] QueryPerformanceCounter is as accurate as you are gonna get on windows btw!

Anyways, I'm gonna just throw code at you, please excuse my language in my comments.. To make it work on a duel core system the easiest way is to set the thread affinity - basically lock it to run on one core:

/**************************************************************************** File:		cTime.cpp* Author: 	Neil Richardson* Ver/Date:	* Description:**		*		*		* **************************************************************************/#include "cDebug.h"#include "cTime.h"using namespace core;/////////////////////////////////////////////////// Ctor & Dtor/////////////////////////////////////////////////cTime::cTime():	MilliSeconds_		( 0.0f ),	Seconds_			( 0.0f ){#ifdef WIN32_DUAL_CORE_TIMING	// Set our threads affinity to first CPU to stop jittering	if ( SetThreadAffinityMask( GetCurrentThread(), 1 ) == 0 )	{		DBG_FAIL( "Invalid affinity! Do you have a CPU at all?\n" );	}#endif#ifdef WIN32_PERFORMANCE_TIMER	// Get the frequency	if ( QueryPerformanceFrequency(&Freq_) == 0 )	{		DBG_FAIL( "QueryPerformanceFrequency is shit\n" );	}	// Get first tick	QueryPerformanceCounter(&FirstTick_);#else // GetShitCount()	// Get first tick	FirstTick_ = GetTickCount();#endif}cTime::~cTime(){}/////////////////////////////////////////// update/////////////////////////////////////////void cTime::update(){#ifdef WIN32_PERFORMANCE_TIMER	// Get the tick	QueryPerformanceCounter(&Tick_);	// Compensate	Seconds_ = (cReal)(Tick_.QuadPart - FirstTick_.QuadPart) / (cReal)Freq_.QuadPart;	MilliSeconds_ = Seconds_ * 1000.0f;#else // GetShitCount()	MilliSeconds_ = (cReal)(GetTickCount() - FirstTick_);	Seconds_ = MilliSeconds_ * 0.001f;#endif}
Adventures of a Pro & Hobby Games Programmer - http://neilo-gd.blogspot.com/Twitter - http://twitter.com/neilogd
Yeah, but the funny thing is that I've got a single-core :D
Odd, I will say I've never experienced that problem before. I mean try locking the thread affinity - see if that works, I mean do any other games give you any trouble? Another time - Could use the multimedia time - timeGetTime.
Adventures of a Pro & Hobby Games Programmer - http://neilo-gd.blogspot.com/Twitter - http://twitter.com/neilogd
No other application depending on QueryPerformanceTimer gives me any problems (not that I know of ofcourse )

Setting the thread's affinity does not change anything - it would be strange if it did in the first place though :)

timeGetTime() seems to work with my initial tests, but we all know we should avoid timeGetTime() like the plague ;)

Ah well, I suppose i need to wait for my fever to go down before I wrestle this beast any more :(

Edit:
Added the information about thread affinity
ps: Why are you throwing strings?
daerid@gmail.com
Quote:Original post by daerid
ps: Why are you throwing strings?


It is just a (bad) habit :D
Quote:
// seems correct
// Sleep( 10 );

// seems incorrect
// Sleep( 1 );


This is a BAD way to test timer code. Sleep(1) tells the OS that you need a minimum of 1ms downtime.
So your app probably wont be reactivated for 10-15ms. That would explain the
difference in timing there

Quote:
// for( int i = 0; i < 100000; ++i )
// float a = i * 3.1415192f;


If you are going to use loops like this to test your code make sure that you have optimizations turned completely off
or that loop will probably get removed from the output code.


This topic is closed to new replies.

Advertisement