More accurate alternative to millisecs()

BlitzMax Forums/BlitzMax Programming/More accurate alternative to millisecs()

I was going to request it, but in the meantime came up with this (only works on OS X):

I found that millisecs wasn't accurate enough for me, so I present the following.

Add this bit to your Blitz source:

Import "GetTime.c"

Extern
	Function getTime:Double()
End Extern



And place this in a text file named "GetTime.c" in the same folder as your Blitz project:
#include <Carbon/Carbon.h>

double getTime()
{
double t;

t=(double)GetCurrentEventTime();
return t;
}


This returns the time in seconds since System startup. I have no idea how to do this on Windows, but I'm sure someone else does.

I dont own BlitzMax

Umm, doesnt millisecs (at least in the other Blitz's) return the milliseconds since system startup?

Maybe so, but this is accurate to more than 1/1000th of a second, which is what I needed.

So how accurate is it?

I did a quick check with google and didn't find anything specific. There were some suggestions that the resolution is better than one millisecond. But how much better?

Since you can measure 1.3 and 1.4 microseconds while running a testprogram, its 1/10000000 second.
I'm on windows - so I cannot test it myself and must rely on what I read at iDevGames:

I have exactly what you want...
#include <CoreServices/CoreServices.h>

double GetUpTime(void)
{
AbsoluteTime atime = UpTime();
Nanoseconds nsecs = AbsoluteToNanoseconds(atime);
double time = UnsignedWideToUInt64(nsecs);
return time*1.0E-9;
}; 
This function returns the uptime of the machine in seconds, and on my machine (G4/400) two successive calls measure a delay of 1.3-1.4 microseconds.


On win32 I recommend QueryPerformanceCounter to get the time and QueryPerformanceFrequency to get the resolution of the timer.

QueryPerfomanceCounter is the highest resolution on Windows.

So yes, tickcount() aint as accurate.

Doesn't QueryPerformanceCounter mess up with Intel's SpeedStep and, in the future, AMD's Cool & Quiet?

You are right Michael, I never thought of that.
However, Intel mention something called an Enhanced Timer which make use the QueryPerformance functions but deals with SpeedStep and such.
But I don't know if it could be used in Bmax or not.

This thread on another forum might be of use to you lot.

It is more precise than millisecs to be sure. I was finding it difficult to get accurate fps readings when I could only get the time to the 1000th of a second, since my project runs between 300 and 750 fps.

It is more precise than millisecs to be sure. I was finding it difficult to get accurate fps readings when I could only get the time to the 1000th of a second, since my project runs between 300 and 750 fps.

Don't count millisecs between frames, count frames per second (or half second, etc). I'll dig up my FPS code:

Const fpsUpdate# = 0.5
Global fpsCount, fpsLastUpdated, fpsLastCount

Function FPS( )
	
	If ( Millisecs( ) - fpsLastUpdated ) > Int( fpsUpdate# * 1000.0 )
		
		fpsLastCount = Float( fpsCount / fpsUpdate# )
		fpsCount = 1
		fpsLastUpdated = MilliSecs( )
		
	Else
	
		fpsCount = fpsCount + 1
		
	EndIf
	
	Return fpsLastCount
	
End Function


With fpsUpdate as default, it counts the number of frames every half second and multiplies by two. This is good because it gives you a precise, to the nearest frame number - it also means the fps counter is MUCH more stable and easier to read than updating every frame.

Here is some examples of using QueryPerformanceCounter in Windows - wrapped in a little class (on GHz machines, this is nanosecond accuracy):

Framework brl.retro

' initialize the timer internals
TCodeTimer.Initialise()

Print "Timing 1..."

Local myarray:Int[1000]
Local dummy:Int

timer:TCodeTimer = TCodeTimer.Create()

timer.StartWatch()

While timer.WatchTime() < 1000

	timer.StartTimer()
	
	Local ma_len:Int = myarray.length - 1
	For counter = 0 To ma_len
		dummy = myarray[counter]
	Next 
	
	timer.StopTimer()

Wend
	
Print "Using length in local variable"
Print "Iterations: " + timer._iterations
Print "Total Time: " + timer._totaltime
Print "Average time: " + timer._totaltime / Double(timer._iterations)
Print "***************************"
Print 

timer.ResetTimer()

Print "Timing 2..."

timer.StartWatch()

While timer.WatchTime() < 1000

	timer.StartTimer()
	
	For counter = 0 Until myarray.length
		dummy = myarray[counter]
	Next 
	
	timer.StopTimer()

Wend
	
Print "Using length in For/Until"
Print "Iterations: " + timer._iterations
Print "Total Time: " + timer._totaltime
Print "Average time: " + timer._totaltime / Double(timer._iterations)
Print "***************************"
Print 

timer.ResetTimer()

Print "Timing 3..."

timer.StartWatch()

While timer.WatchTime() < 1000

	timer.StartTimer()
	
	For value=EachIn myarray
	Next 
	
	timer.StopTimer()

Wend
	
Print "Using length in For/Until"
Print "Iterations: " + timer._iterations
Print "Total Time: " + timer._totaltime
Print "Average time: " + timer._totaltime / Double(timer._iterations)
Print "***************************"
Print 



' classes
Extern "win32"
	Function QueryPerformanceCounter(count:Long Var)
	Function QueryPerformanceFrequency(freq:Long Var)
End Extern

Type TCodeTimer
	Field _start:Long
	Field _stop:Long
	Field _iterations:Int
	Field _totaltime:Double
	
	Method ResetTimer()
		_start 		= 0
		_stop 		= 0
		_iterations 	= 0
		_totaltime 	= 0
	End Method	
	
	Method StartTimer()
		QueryPerformanceCounter(_start)
	End Method
	
	
	Method StopTimer()
		QueryPerformanceCounter(_stop) 
		_totaltime :+ (Double(_stop - _start) / Double(_freq)) 
		_iterations :+ 1
	End Method
	
	
	''' Stopwatch functions
	Field _watchstart:Double
	
	Method StartWatch()
		self._watchstart = QueryCounter()
	End Method
	
	''' returns the number of milliseconds since calling StartWatch()
	Method WatchTime:Double()
		Return QueryCounter() - _watchstart
	End Method 
	
	''' Globals
	
	''' Initialize should be called before first use, to determine timer class overhead
	Global _overhead:Double	
	Global _freq:Long
	
	Function Create:TCodeTimer()
		Return New TCodeTimer
	End Function
	
	Function Initialise()
		QueryPerformanceFrequency(_freq)
		_freq :/ 1000
	End Function
	
	Function QueryCounter:Double()
		Local counter:Long
		QueryPerformanceCounter(counter)
		Return Double(counter) / _freq
	End Function 

End Type