Like ElapsedMicroseconds(). I started to develop ElapsedMicroSeconds(). But I wanted a more comfortable function.
Here is the result of ElapsedMicroSeconds() and more comfortable HPT() HighPerformanceTimer
Updated: 2023/08/16 19:25
Updated: 2023/08/17 10:06, fixed Abs() Bug, PB's Abs is only for floats
Code: Select all
Procedure.q ElapsedMicroSeconds()
Protected v.Quad, f.Quad
QueryPerformanceFrequency_(f)
QueryPerformanceCounter_(v)
ProcedureReturn (v\q / (f\q /1e6) ) ; normalize value to MicroSeconds : 1e6 = 1.000.000 = 1Mhz
EndProcedure
#HPT_START = 0 ; START Timer and return RAW value of QueryPerformanceCounter [Ticks]
#HPT_STOP = 1 ; STOP Timer and return the time difference in MicroSeconds since start
#HPT_READ = 2 ; Read the time value [µs] (if Timer is stopped, it's the differntial time. If timer is started it is the START-Time)
#HPT_CALIBRATE = 3 ; Calibrate the timers (it captures the calling Time of HPT(), so timers will be automatically corrected by that ticks)
#HPT_GetAll = 4 ; #HPT_GetAll returns a Pointer to the TimerStructure THPT. Get a copy of all Timers with CopyStructure( HPT(#HPT_GetAll), *ReturnTmr.THPT, THPT)
Procedure.q HPT(cmd=#HPT_START, TimerNo=0)
Structure THPT ; helper Structure to overlay as Array on Datasection
T.QUAD[16] ; [16] =[0..15]
EndStructure
Static *HPT.THPT ; Pointer to Datasection HPT_DATA:
Static k.Quad ; Factor QueryPerformanceFrequency_ / 1 Mhz => Factor Ticks to MicroSeconds
Static cal.Quad ; calibration ticks : Time for calling HPT() in Ticks of QueryPerformanceFrequency
Protected v.Quad ; actual value for QueryPerformanceCounter
; INIT
If Not k\q ; k must be 1 or greater! k is the factor of what QueryPerofrmanceFrequency is higher than 1Mhz
QueryPerformanceFrequency_(k)
k\q / 1e6 ; Frequ/1.000.000 => Factor Mhz, MicroSeconds
If k\q < 1
k\q = 1 ; to prevent Errors if QueryPerofrmanceFrequency is less than 1MHz (should not happen!)
EndIf
*HPT = ?HPT_DATA ; set Pointer to HPT_DATA
EndIf
Select cmd
Case #HPT_START ; START Timer --> save actual value of QueryPerformanceCounter in DataSection
QueryPerformanceCounter_(*HPT\T[TimerNo])
ProcedureReturn *HPT\T[TimerNo]\q ; Return the saved value
Case #HPT_STOP ; STOP Timer --> Difference of QueryPerformanceCounter and saved value as MicroSeconds
QueryPerformanceCounter_(v)
*HPT\T[TimerNo]\q = ( (v\q - *HPT\T[TimerNo]\q)) -cal\q
If *HPT\T[TimerNo]\q < 0 ; Abs() because PB's Abs() is for floats only
*HPT\T[TimerNo]\q = - *HPT\T[TimerNo]\q
EndIf
ProcedureReturn *HPT\T[TimerNo]\q / k\q ; Return MicroSeconds
Case #HPT_READ
ProcedureReturn *HPT\T[TimerNo]\q / k\q
Case #HPT_CALIBRATE ; gets the Ticks for HPT() calls and save it!
cal\q = HPT(#HPT_START) ; with Parameter #HP_START we get back the RAW Ticks
cal\q = (HPT(#HPT_START) - cal\q)
If cal\q <0 ; Abs(cal\q) because PB's ABS() is for floats only
cal\q = - cal\q
EndIf
ProcedureReturn cal\q ; No of Ticks at QueryPerformanceFrequency
Case #HPT_GetAll
ProcedureReturn *HPT ; Return Pointer to TimerStructure to get access to all timers
EndSelect
DataSection
HPT_DATA: ; Reserve Space in DataSection for each Timer. Access a Timer with *HPT\T[No]
; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15]; for mot Timers, add more values here
Data.q 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
EndDataSection
EndProcedure
; TEST - CODE
Define uSec.q
uSec = ElapsedMicroSeconds()
Delay(10)
Debug "measure Delay(10)"
Debug "Delay of 10ms : measured µs = " + Str(ElapsedMicroSeconds() - uSec) ; should be little more than 10.000
Debug #Null$
#cstLoops = 100000
Define I, sq.f
Define cal.q
; calibrate the Timer if necessary. On my Ryzen 5800 this are 6..8 Ticks with Debugger and 0..2 without at 10Mhz QueryPerofrmanceFrequency
; if you do not need it that exact, calibration is not necessary!
cal = HPT(#HPT_CALIBRATE)
; -------------------------------------------
; Demo for fast use of Timer 0
; -------------------------------------------
HPT() ; Start Timer (0)
For I = 0 To #cstLoops
sq = I*I
Next
Debug "Demo for fast use of Timer 0"
Debug HPT(#HPT_STOP) ; Stop Timer(0) and Print Time
Debug #Null$
; -------------------------------------------
; Demo for MultiTimer
; -------------------------------------------
HPT(#HPT_START, 7) ; use Timer 7 as total time
; -------------------------------------------
HPT() ; Start Timer (0)
For I = 0 To #cstLoops
sq = Sqr(I)
Next
HPT(#HPT_STOP) ; Stop Timer(0)
; -------------------------------------------
HPT(#HPT_START,1) ; Start Timer (1)
I=0
While I<= #cstLoops
sq = Sqr(I)
I + 1
Wend
HPT(#HPT_STOP,1) ; Stop Timer (1)
; -------------------------------------------
HPT(#HPT_START,2) ; Start Timer (2)
I=0
Repeat
sq = Sqr(I)
I + 1
Until I> #cstLoops
HPT(#HPT_STOP,2) ; Start Timer (2)
; -------------------------------------------
HPT(#HPT_STOP,7) ; Stop Timer (7), total time
; -------------------------------------------
Define txt.s
Define f.Quad
QueryPerformanceFrequency_(f) ; get the QueryPerofrmanceFrequency
f\q / 1e6 ; convert Frequency to MHz
; -------------------------------------------
; DEMO how to get a BackupCopy of all Timers
; now we use HPT(#HPT_GetAll) to get the Pointer to the Timer Structure in DataSection
; with this Pointer we can get a copy of all TimerValues
Define AllTimerValues.THPT ; Define a variable for the timer copy
CopyStructure( HPT(#HPT_GetAll), AllTimerValues, THPT) ; copy the complete TimerStructre form HPT() DataSection
Debug "Get All Timer values with HPT(#HPT_GetAll)"
For I = 0 To 15
Debug Str(I) + " : " + Str(AllTimerValues\T[I]\q)
Next
; -------------------------------------------
txt = "For-Next µs = " + Str(HPT(#HPT_READ)) + #CRLF$
txt + "While-Wend µs = " + Str(HPT(#HPT_READ,1)) + #CRLF$
txt + "Repeat-Until µs = " + Str(HPT(#HPT_READ,2)) + #CRLF$
txt + "total time µs = " + Str(HPT(#HPT_READ,7)) + #CRLF$
txt + "Calibration Ticks = " + Str(cal) + " of QueryPerformanceFrequency " + Str(f\q) + "MHz"
MessageRequester( "Timing Test for PB Loops with " + FormatNumber(#cstLoops, 0, "," , ".") + " Loops of Squre() Calculations", txt, #PB_MessageRequester_Info )