important! ElapsedMilliseconds()

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
Kazmirzak
User
User
Posts: 92
Joined: Fri Jun 18, 2004 5:44 pm
Location: Germany

important! ElapsedMilliseconds()

Post by Kazmirzak »

I would like to use Elapsedmilliseconds() to time my game to 100 fps; this is standard for most of us, isn't it? To get good timing you need to measure time; but even with PB4.2B the smallest time difference that you can measure is 16ms!!

2. I would also like to use Elapsedmilliseconds to find out how long several calculations in my 100fps game need. But 16ms is ways too inaccourate!

SOLUTION: can't you ask the BlitzBasic team how they adopted their Millisecs() to work with 1ms!?

PureBasic

Code: Select all

For i=1 To 1000000
  sold=s : s=ElapsedMilliseconds() : If sold<>s : Debug s : EndIf
Next
End
Output in PureBasic V4.2 Beta
6244921
6244937
6244953
6244968
6244984
6245000
6245015
6245031
6245046

Code in BlitzBasicDemo160

Code: Select all

For i=1 To 1000000
  sold=s : s=MilliSecs() : If sold<>s : Print s : EndIf
Next
Delay(2000)
End
Output in BlitzBasicDemo160
6244921
6244922
6244923
6244924
6244925
6244926
6244927
6244928
6244929
6244930

...

******************************************************************************

(P.S. I tried the code with an array instead of the debugger - it doesnt make a difference)
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

Try this.

Code: Select all

Procedure TimerH()
  Static maxfreq.q
  Protected t.q
  If maxfreq=0
    QueryPerformanceFrequency_(@maxfreq)
    maxfreq=maxfreq/1000
  EndIf
  QueryPerformanceCounter_(@t.q)
  ProcedureReturn t/maxfreq
EndProcedure

For i=1 To 1000000
  sold=s : s=TimerH() : If sold<>s : Debug s : EndIf
Next
End
#NULL
Addict
Addict
Posts: 1497
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Post by #NULL »

here's an include of mine based on QueryPerformanceCounter_() too

Code: Select all

; time.pbi

; (uses win-api)

; + thanks to Ligatur
; + pb-forum: http://www.purebasic.fr/german/viewtopic.php?p=140652#140652



Define QPC_TICKS_PER_SECOND.l
Define QPC_Time_freq.q
Define QPC_Time_start.q



 ; if an high-resolution performance counter is available
 ; the procedure initTime() returns 1, otherwise 0.
 ; it also sets the resolution for getTime() (default is 1000 ticks per second -> milliseconds)
 ; it also resets the clock for getTime()
 ; [it also gets the counter-frequency needed for internal calculation in getTime()]
Procedure.l initTime(ticks_per_second.l=1000)
  Shared QPC_TICKS_PER_SECOND.l
  Shared QPC_Time_freq.q
  Shared QPC_Time_start.q
  If Not QueryPerformanceFrequency_(@QPC_Time_freq.q)
    ProcedureReturn 0
  Else
    QueryPerformanceCounter_(@QPC_Time_start.q)
    QPC_TICKS_PER_SECOND.l = ticks_per_second
    ProcedureReturn 1
  EndIf
EndProcedure


 ; getTime() returns the amount of ticks elapsed
 ; since the last call of initTime()
Procedure.l getTime()
  Shared QPC_TICKS_PER_SECOND.l
  Shared QPC_Time_freq.q
  Shared QPC_Time_start.q
  Static QPC_Time_now.q
  QueryPerformanceCounter_(@QPC_Time_now.q)
  ProcedureReturn (QPC_Time_now - QPC_Time_start) * QPC_TICKS_PER_SECOND / QPC_Time_freq
EndProcedure


; [merge_end]









; Debug "---- EXMAPLE - time.pbi"
; If Not initTime()
;   MessageRequester("error","no high-resolution performance counter available.")
;   End
; EndIf
; 
; initTime(10) ; now we get 10 ticks per second
; While  tick < QPC_TICKS_PER_SECOND * 2 ; for two seconds
;   tick=getTime()
;   If tick<>tick_previous
;     Debug Str(tick)
;     tick_previous=tick
;   EndIf
; Wend
; 
; Debug "":Debug "########################":Debug "########################":Debug ""
; 
; initTime(100000) ; now we get 100 ticks per millisecond !
; tick=getTime()
; While tick < QPC_TICKS_PER_SECOND * 0.001 ; for one milliseconds
;   tick=getTime()
;   out$=Str(tick) + " ticks since initTime()   [" + StrF(tick/QPC_TICKS_PER_SECOND, 8) + " seconds]"
;   Debug out$
; Wend
; Debug "----" 

PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: important! ElapsedMilliseconds()

Post by PB »

ElapsedMilliseconds() is a wrapper for the GetTickCount_() API, that's all.
Blitz would most likely be using their own custom-written timer, I'd say.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

If you're on Windows and you are comfortable with timing logic that uses ElapsedMilliseconds(), there is a simple solution you can use. Windows deliberately pegs timer resolution back to a rate that is useful for most purposes and doesn't use more resources than necessary. However, if you would like to set the timer res up to a level more suitable to games you can use the API call timeGetTime_(). It is very similar to ElapsedMilliseconds() in what it does, but its performance will go up according to a call you can make to timeBeginPeriod_(). Here's some test code, run it leaving the lines commented first. You should get a total for a of around 64 or so. Then uncomment the lines and rerun, if you have a reasonably modern computer the variable a should have been incremented close to 1000 times in the one second it runs. This effectively raises the timer resolution to 1ms or pretty close to it.

Code: Select all

; timeGetDevCaps_( timer_res.TIMECAPS, SizeOf(TIMECAPS) )
; smallestperiod = timer_res\wPeriodMin
; timeBeginPeriod_(smallestperiod)

a=0
s = timeGetTime_()
t = timeGetTime_()
Repeat
  If timeGetTime_()-t <> 0
    a+1
    t=timeGetTime_()
  EndIf
Until timeGetTime_()-s >=1000

; timeEndPeriod_(smallestperiod)

MessageRequester("",Str(a))
BERESHEIT
Kazmirzak
User
User
Posts: 92
Joined: Fri Jun 18, 2004 5:44 pm
Location: Germany

Post by Kazmirzak »

Thank you very much for all these quick and detailed proposals. I'm going to try one of these out for sure!!

But I think, for all of us who are no system experts it would be great to take one of these proposals into the command ElapsedMilliseconds()? Any opinions by the PureBasic team?

Think of all those people who want to test the speed of Pure Basic!!
akj
Enthusiast
Enthusiast
Posts: 668
Joined: Mon Jun 09, 2003 10:08 pm
Location: Nottingham

Post by akj »

@ netmaestro

This is very strange, but on running your timer program (without the debugger) on my PC with Windows ME it makes no difference whether or not the lines are commented. In all cases it gives a result between 800 and 1000.
Anthony Jordan
dell_jockey
Enthusiast
Enthusiast
Posts: 767
Joined: Sat Jan 24, 2004 6:56 pm

Post by dell_jockey »

akj wrote:@ netmaestro

This is very strange, but on running your timer program (without the debugger) on my PC with Windows ME it makes no difference whether or not the lines are commented. In all cases it gives a result between 800 and 1000.
same here, rock solid 999 or 1000, both with commented and uncommented lines.
cheers,
dell_jockey
________
http://blog.forex-trading-ideas.com
User avatar
Blue
Addict
Addict
Posts: 964
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Post by Blue »

The topic may be a bit old, but remains most likely of current interest.
So, I take the liberty of presenting a Microsoft page (url as of June 2009: http://support.microsoft.com/kb/172338) with an interesting article on the subject.
I simply translated the writer's test code into PB code, turned his explanations into comments and ... voilà:

Code: Select all

;; source: http://support.microsoft.com/kb/172338 
EnableExplicit
Procedure Test_Timers()
 Define.q ctr1, Ctr2, Freq
 Define Count1, Count2, Loops
 
; ;
; ;  Time QueryPerformanceCounter
; ;

  If QueryPerformanceCounter_(@Ctr1)
    QueryPerformanceCounter_(@Ctr2)
    QueryPerformanceFrequency_(@Freq)
    Debug "Start Value: " + Str(Ctr1)
    Debug "End Value: "+ Str(Ctr2)
    
    Debug "QueryPerformanceCounter minimum resolution: 1/" + Str(Freq) + " sec"
    Debug "API Overhead: " + StrD((Ctr2 - Ctr1) / Freq) + " seconds"

  Else
    Debug "High-resolution counter not supported."
  EndIf
  
; ; 
; ; Time GetTickCount
; ;

  Loops = 0
  Count1 = GetTickCount_()
  Repeat
    Count2 = GetTickCount_()
    Loops + 1
  Until Count1 <> Count2

  Debugg
  Debug "GetTickCount minimum resolution: " + Str(Count2 - Count1) + " ms"
  Debug "Took " + Str( Loops) + " loops"

; ;
; ;  Time timeGetTime
; ;

  Loops = 0
  Count1 = timeGetTime_()
  Repeat
    Count2 = timeGetTime_()
    Loops + 1
  Until Count1 <> Count2

  Debugg
  Debug "timeGetTime minimum resolution: " + Str(Count2 - Count1) + " ms"
  Debug "Took " + Str(Loops) + " loops"
EndProcedure

; ;
; ; Run the function from the Debug/Immediate window. 
; ; Your output should appear similar to the following:
; ;   Start Value: 3516284.3498
; ;   End Value: 3516284.3521
; ;   QueryPerformanceCounter minimum resolution: 1/1193182 sec
; ;   API Overhead: 1.92761875388667E-05 seconds 
; ; 
; ;   GetTickCount minimum resolution: 10 ms
; ;     Took 650 loops  
; ; 
; ;   timeGetTime minimum resolution: 10 ms
; ;     Took 1565 loops
; ;
; ; Multiple statements execute before either GetTickCount Or timeGetTime record a change.
; ; The actual number of loops will vary depending on the background tasks the operating
; ; system is executing. 
; ; 
; ; On the other hand, QueryPerformanceCounter changes value between successive
; ; API calls, indicating its usefulness in high-resolution timing.
; ; The resolution in this case is on the order of a microsecond. 
; ; Because the resolution is system-dependent, there are no standard units that it 
; ; measures. You have to divide the difference by the QueryPerformanceFrequency to 
; ; determine the number of seconds elapsed. In the case above, the overhead for just 
; ; calling the API is about 19 microseconds. This would have to be subtracted when 
; ; timing other code as follows:
; ;
   

  #n_additions  = 100 *10 ;* 100 * 10 * 7

   Procedure Time_Addition()
    Debugg
    Debugg
   Define.q Ctr1, Ctr2, Freq, Overhead
   Define.i A, i
     QueryPerformanceFrequency_(@Freq)
     QueryPerformanceCounter_(@Ctr1)
     QueryPerformanceCounter_(@Ctr2)
     
     Overhead = Ctr2 - Ctr1        ; determine API overhead
     QueryPerformanceCounter_(@Ctr1)  ; time loop
     For i = 1 To #n_additions
       A = A + i
     Next i
     QueryPerformanceCounter_(@Ctr2)
     Debug  "( " + Str(Ctr1) + " - " + Str(Ctr2) + " - " + Str(Overhead)  + ") / " + Str(Freq)
     Debug  Str(#n_additions) + " additions took " + StrD((Ctr2 - Ctr1 - Overhead) / Freq) + " seconds"
   EndProcedure

; ;
; ; Sample output:
; ;   ( 3630876.6256 - 3630876.6388 - 0.0013 ) / 119.3182
; ;   100 additions took 9.97333181358753E-05 seconds
; ; NOTE: Because currency variables are used, the values returned are 10000 times 
; ; smaller than the actual counters. Because the calculation of seconds involves a 
; ; division operation, this factor is cancelled out.
; ;    

Test_Timers()
Time_Addition()

End
@netMaestro: thanks for an excellent tip. It just makes things so very simple.
PB Forums : Proof positive that 2 heads (or more...) are better than one :idea:
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Re: important! ElapsedMilliseconds()

Post by Kaeru Gaman »

yes, the topic is old...
Kazmirzak wrote:I would like to use Elapsedmilliseconds() to time my game to 100 fps; this is standard for most of us, isn't it?
what some of us seem to forget most of the time:
"standard" FpS are often bound to VSync!
honestly, 100 FpS is the most unusual number I ever heard.

so, if you don't explicitely deactivate VSync, you maybe not able to run FpS different than 50, 60 or 85Hz.
and deactivating VSync may lead to flickering or artefacts...



better request the VRate, and sync your FpS to it.

best, async your game TICs and your FpS.
oh... and have a nice day.
Kazmirzak
User
User
Posts: 92
Joined: Fri Jun 18, 2004 5:44 pm
Location: Germany

Post by Kazmirzak »

I am quite surprised to hear from that now! But don't misunderstand me - the purpose of the post is not to ask how to get to 100fps but to switch to some other API in order to be able to measure Milliseconds properly!

Better example is that: it is nearly impossible to write a sounds application that syncs several sound commands to music without a good Milliseconds()

How about that solution, in order not to break existing code or touch the performance in any way:

Code: Select all

 MilliSeconds(precise=0)    - measures the time. If precise is specified, time will be measured more precisely. Default is non-precise.
User avatar
pdwyer
Addict
Addict
Posts: 2813
Joined: Tue May 08, 2007 1:27 pm
Location: Chiba, Japan

Post by pdwyer »

I wonder if linux has a high perf counter of some type so that PB could create a HPEllapsedMilliseconds()

The winAPI works but it's not cross platform
Paul Dwyer

“In nature, it’s not the strongest nor the most intelligent who survives. It’s the most adaptable to change” - Charles Darwin
“If you can't explain it to a six-year old you really don't understand it yourself.” - Albert Einstein
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Post by Kaeru Gaman »

@Kazmirzak

> but to switch to some other API in order to be able to measure Milliseconds properly!

I know, I understood that...

I just wanted to remind everybody who reads this, that a HPTimer alone is not sufficent to get a certain FpS on every Client's machine.

for professional Music Software, sure, it's impossible to write something acceptable with native commands.

but since the movie lib plays everything with the right codec present,
it's not necessary for standard Apps and Games.


but I agree to you: it would be nice to have a native way to use HiPerf Timers.
Also for various Scientific Apps it would be useful/necessary,
so adding this could be a serious step forward in professionality for PureBasic.


@Paul
good point. I have not the merest idea about Linux-API...but I would bet there is some.

...if not the native ElapsedMilliseconds is already 1ms precise.
the 16ms gap is a Windows timeslice problem.
I wouldn't be surprised if Linux doesn't have this problem at all...
oh... and have a nice day.
Post Reply