Simple stopwatch module (windows)

Share your advanced PureBasic knowledge/code with the community.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Simple stopwatch module (windows)

Post by netmaestro »

From time to time (pun not intended but wow there it is!) you need to put a precision stopwatch in your code. Here is what I use and it's about as simple and easy to use as I can make it:

Code: Select all

;/////////////////////////////////////////////////////////////////////////////////////////////
;
; Module:           Stopwatch.pbi
; Author:           Lloyd Gallant (netmaestro) www.lloydsplace.com
; Date:             August 4, 2016
; Target Compiler:  PureBasic 5.50 
; License:          Do as you like with it, no restrictions and NO warranty
;
; What is it:       Quick-and-dirty hires timer with automatic result display,
;                   designed for minimal user interaction. Returns precise milliseconds.
;                   Feel free to modify it for more complex or user-specific tasks.
;
; Commands:         StartStopwatch -Clears and starts the timer
;                   ReadStopwatch  -Reads the timer value and displays it
; 
;                   Usage example appears after the code. (May be left intact if 
;                   using as an include file)
;
;                   If you're precision-timing code execution, as always, remember
;                   to turn the debugger off. (DisableDebugger in code is not sufficient)
;
;////////////////////////////////////////////////////////////////////////////////////////////

DeclareModule Stopwatch
  
  Structure SWLARGEINTEGER
    StructureUnion 
      Longs.LARGE_INTEGER
      QuadPart.q
    EndStructureUnion
  EndStructure
  
  #SW_Begin = 0
  #SW_End   = 1
  
  Declare.l CallStopwatch(mode)
  
  Macro StartStopwatch
    Stopwatch::CallStopwatch(Stopwatch::#SW_Begin)
  EndMacro
  
  Macro ReadStopwatch
    MessageRequester("Stopwatch", Str(Stopwatch::CallStopwatch(Stopwatch::#SW_End)))
  EndMacro
  
EndDeclareModule

Module Stopwatch
  
  Procedure.l CallStopwatch(mode)
    
    Static.SWLARGEINTEGER StartingTime, Frequency
    Define.SWLARGEINTEGER EndingTime, ElapsedMicroseconds
    
    Select mode
      Case #SW_Begin
        QueryPerformanceFrequency_(@Frequency)
        QueryPerformanceCounter_(@StartingTime)
        
      Case #SW_End
        QueryPerformanceCounter_(@EndingTime)
        ElapsedMicroseconds\QuadPart = EndingTime\QuadPart - StartingTime\QuadPart
        ElapsedMicroseconds\QuadPart = ElapsedMicroseconds\QuadPart * 1000
        ElapsedMicroseconds\QuadPart = ElapsedMicroseconds\QuadPart / Frequency\QuadPart
        ProcedureReturn ElapsedMicroseconds\Longs\lowpart
    EndSelect
    
  EndProcedure
  
EndModule

CompilerIf #PB_Compiler_IsMainFile
  Stopwatch::StartStopwatch
  Delay(1000)
  Stopwatch::ReadStopwatch
CompilerEndIf

Enjoy it if you like it.
BERESHEIT
User avatar
ar-s
Enthusiast
Enthusiast
Posts: 344
Joined: Sat Oct 06, 2007 11:20 pm
Location: France

Re: Simple stopwatch module (windows)

Post by ar-s »

Clean as usual, thanks for sharing.
~Ar-S~
My Image Hoster for PB users
My webSite (french) with PB apps : LDVMULTIMEDIA
PB - 3.x / 5.7x / 6 - W11 x64 - Ryzen 7 3700x / #Rpi4

Code: Select all

r3p347 : 7ry : un71l d0n3 = 1
Joris
Addict
Addict
Posts: 890
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: Simple stopwatch module (windows)

Post by Joris »

Thanks, but...

Netmaestro I still wonder which kind of timer codes, is really stable and precies ?
Useable for a constant timing like needed in for example audio and video editings.
With your code I get as result is 999 msec. So Delay(1000) isn't accurate.
Change the delay to Delay(1001) I get 1001msec.

I've tested many others too and experinece lot of differences with most of them.
Some don't work independant (movings from gadgets or windows do interrupt them).

CPUTime against the API timeGetTime_ setup :
http://www.purebasic.fr/english/viewtop ... 12&t=38575

QueryPerformanceFrequency_ against the API timeGetTime_ setup :
http://www.purebasic.fr/english/viewtop ... 5&start=15

All of these show (big) differences.
Could you or anyone else clarify this (once and for all ;-)

Thanks.
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Simple stopwatch module (windows)

Post by netmaestro »

On Windows QueryPerformanceCounter is as good as it gets. PB's Delay() command has been using it for a couple of years now. Nothing is available anywhere that will give perfection and 99.9% is close enough for practical purposes.
BERESHEIT
Joris
Addict
Addict
Posts: 890
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: Simple stopwatch module (windows)

Post by Joris »

Netmaestro maybe the only problem of inaccurate timing might be my computer...

About the QueryPerformanceFrequency function :
https://msdn.microsoft.com/en-us/librar ... .85).aspx#
If the installed hardware supports a high-resolution performance counter, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, call GetLastError.
On systems that run Windows XP or later, the function will always succeed and will thus never return zero. (yeah dream on MS :) )
Here it returns zero (XP) and I'm still searching how this function can be enabled or detect it does work even return is zero.
Besides I see not any other source with QueryPerformanceFrequency here using this retrun as detection ?
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Re: Simple stopwatch module (windows)

Post by djes »

On Windows, there always some task that can stop your process. The only thing you can do against this problem is to stop the task switching the time of your measure (it's what I've showed in this old code http://www.purebasic.fr/english/viewtop ... 12&t=36852
Maybe it could be possible to check directly a hires timer from a driver process with an even higher task priority, but I've not tried this myself.
Joris
Addict
Addict
Posts: 890
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: Simple stopwatch module (windows)

Post by Joris »

djes wrote:On Windows, there always some task that can stop your process....
A timer Callback procedure shouldn't become interupted. And, that's is my experience too, but the highest accuraty is 1 msec. I still don't know how to use all the other timing function the same way : within a callback 'not interupted by other tasks'.
Here is a nice exmaple from TI-994A. Move the window and see what happens with both timers :

Code: Select all

Import "kernel32.lib"
  CreateTimerQueue()
  DeleteTimerQueueTimer(hTimerQueue, hTimer, completionEvent)
  DeleteTimerQueueEx(hTimerQueue, completionEvent)
  ChangeTimerQueueTimer (hTimerQueue, hTimer, dueTime, period)
  CreateTimerQueueTimer(hTimer, hTimerQueue, timerCallback, param, dueTime, period, flags)
EndImport

Global timer_Q1, timer_Q2, timer_Q3,tm.l,tx.l

Procedure timerProc_Q(param.i, timer.i)
  SetGadgetText(1, Str(tm)+"  "+"Timer high priority")
  tm+1                 
EndProcedure

wFlags = #PB_Window_ScreenCentered | #PB_Window_SystemMenu
OpenWindow(0, #PB_Any, #PB_Any, 280, 120, "Windows Queue Timer", wFlags)
TextGadget(1, 50, 20, 200, 30, "Timer 1: ")
TextGadget(2, 50, 50, 200, 30, "Timer 2: ")

timerQueue = CreateTimerQueue()
CreateTimerQueueTimer(@timer_Q1, timerQueue, @timerProc_Q(), 1, 0, 100, 0)
AddWindowTimer(0, 123, 100)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      appQuit = 1
    Case #PB_Event_Timer 
      SetGadgetText(2, Str(tx)+"  "+"Timer low priority") 
      tx+1    
  EndSelect
Until appQuit = 1

DeleteTimerQueueEx(timerQueue, 0)
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
Post Reply